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 /// path: any | 13 /// path: any |
14 /// | 14 /// |
15 /// Then run `pub install`. | 15 /// Then run `pub install`. |
16 /// | 16 /// |
17 /// For more information, see the [path package on pub.dartlang.org][pkg]. | 17 /// For more information, see the [path package on pub.dartlang.org][pkg]. |
18 /// | 18 /// |
19 /// [pub]: http://pub.dartlang.org | 19 /// [pub]: http://pub.dartlang.org |
20 /// [pkg]: http://pub.dartlang.org/packages/path | 20 /// [pkg]: http://pub.dartlang.org/packages/path |
21 /// | |
22 /// ## Usage ## | |
23 /// | |
24 /// The path library was designed to be imported with a prefix, though you don't | |
25 /// have to if you don't want to: | |
26 /// | |
27 /// import 'package:path/path.dart' as path; | |
28 /// | |
29 /// The most common way to use the library is through the top-level functions. | |
30 /// These manipulate path strings based on your current working directory and | |
31 /// the path style (POSIX, Windows, or URLs) of the host platform. For example: | |
32 /// | |
33 /// path.join("directory", "file.txt"); | |
34 /// | |
35 /// This calls the top-level [join()] function to join "directory" and | |
nweiz
2013/07/25 20:47:02
Nit: "[join()]" -> "[join]".
Bob Nystrom
2013/07/25 23:20:10
Done.
| |
36 /// "file.txt" using the current platform's directory separator. | |
37 /// | |
38 /// If you want to work with paths for a specific platform regardless of the | |
39 /// underlying platform that the program is running on, you can create a | |
40 /// [Builder] and give it an explicit [Style]: | |
41 /// | |
42 /// var builder = new path.Builder(style: Style.windows); | |
43 /// builder.join("directory", "file.txt"); | |
44 /// | |
45 /// This will join "directory" and "file.txt" using the Windows path separator, | |
46 /// even when the program is run on a POSIX machine. | |
21 library path; | 47 library path; |
22 | 48 |
23 @MirrorsUsed(targets: 'dart.dom.html.window, ' | 49 @MirrorsUsed(targets: 'dart.dom.html.window, ' |
24 'dart.io.Directory.current, ' | 50 'dart.io.Directory.current, ' |
25 'dart.io.Platform.operatingSystem') | 51 'dart.io.Platform.operatingSystem') |
26 import 'dart:mirrors'; | 52 import 'dart:mirrors'; |
27 | 53 |
28 /// An internal builder for the current OS so we can provide a straight | 54 /// An internal builder for the current OS so we can provide a straight |
29 /// functional interface and not require users to create one. | 55 /// functional interface and not require users to create one. |
30 final _builder = new Builder(); | 56 final _builder = new Builder(); |
(...skipping 29 matching lines...) Expand all Loading... | |
60 return _io.classes[const Symbol('Directory')] | 86 return _io.classes[const Symbol('Directory')] |
61 .getField(const Symbol('current')).reflectee.path; | 87 .getField(const Symbol('current')).reflectee.path; |
62 } else if (_html != null) { | 88 } else if (_html != null) { |
63 return _html.getField(const Symbol('window')) | 89 return _html.getField(const Symbol('window')) |
64 .reflectee.location.href; | 90 .reflectee.location.href; |
65 } else { | 91 } else { |
66 return '.'; | 92 return '.'; |
67 } | 93 } |
68 } | 94 } |
69 | 95 |
70 /// Gets the path separator for the current platform. On Mac and Linux, this | 96 /// Gets the path separator for the current platform. This is `\` on Windows |
71 /// is `/`. On Windows, it's `\`. | 97 /// and `/` on other platforms (including the browser). |
72 String get separator => _builder.separator; | 98 String get separator => _builder.separator; |
73 | 99 |
74 /// Converts [path] to an absolute path by resolving it relative to the current | 100 /// Converts [path] to an absolute path by resolving it relative to the current |
75 /// working directory. If [path] is already an absolute path, just returns it. | 101 /// working directory. If [path] is already an absolute path, just returns it. |
76 /// | 102 /// |
77 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt | 103 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt |
78 String absolute(String path) => join(current, path); | 104 String absolute(String path) => join(current, path); |
79 | 105 |
80 /// Gets the part of [path] after the last separator. | 106 /// Gets the part of [path] after the last separator. |
81 /// | 107 /// |
(...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
758 /// | 784 /// |
759 /// // Windows | 785 /// // Windows |
760 /// builder.toUri(r'C:\path\to\foo') | 786 /// builder.toUri(r'C:\path\to\foo') |
761 /// // -> Uri.parse('file:///C:/path/to/foo') | 787 /// // -> Uri.parse('file:///C:/path/to/foo') |
762 /// | 788 /// |
763 /// // URL | 789 /// // URL |
764 /// builder.toUri('http://dartlang.org/path/to/foo') | 790 /// builder.toUri('http://dartlang.org/path/to/foo') |
765 /// // -> Uri.parse('http://dartlang.org/path/to/foo') | 791 /// // -> Uri.parse('http://dartlang.org/path/to/foo') |
766 Uri toUri(String path) { | 792 Uri toUri(String path) { |
767 if (isRelative(path)) { | 793 if (isRelative(path)) { |
768 return Uri.parse(path.replaceAll(style.separatorPattern, '/')); | 794 var parsed = _parse(path); |
nweiz
2013/07/25 20:47:02
Why use [_parse] here instead of [split]?
Bob Nystrom
2013/07/25 23:20:10
This path is known to be relative, so I don't thin
nweiz
2013/07/25 23:44:27
It's not more straightforward to a reader of the c
Bob Nystrom
2013/07/25 23:58:37
This code was removed anyway, so it's a moot point
| |
795 return style.relativePathToUri(parsed.parts); | |
769 } else { | 796 } else { |
770 return style.pathToUri(join(root, path)); | 797 return style.absolutePathToUri(join(root, path)); |
771 } | 798 } |
772 } | 799 } |
773 | 800 |
774 _ParsedPath _parse(String path) { | 801 _ParsedPath _parse(String path) { |
775 var before = path; | 802 var before = path; |
776 | 803 |
777 // Remove the root prefix, if any. | 804 // Remove the root prefix, if any. |
778 var root = style.getRoot(path); | 805 var root = style.getRoot(path); |
779 var isRootRelative = style.getRelativeRoot(path) != null; | 806 var isRootRelative = style.getRelativeRoot(path) != null; |
780 if (root != null) path = path.substring(root.length); | 807 if (root != null) path = path.substring(root.length); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
814 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. | 841 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. |
815 static final posix = new _PosixStyle(); | 842 static final posix = new _PosixStyle(); |
816 | 843 |
817 /// Windows paths use "\" (backslash) as separators. Absolute paths start with | 844 /// Windows paths use "\" (backslash) as separators. Absolute paths start with |
818 /// a drive letter followed by a colon (example, "C:") or two backslashes | 845 /// a drive letter followed by a colon (example, "C:") or two backslashes |
819 /// ("\\") for UNC paths. | 846 /// ("\\") for UNC paths. |
820 // TODO(rnystrom): The UNC root prefix should include the drive name too, not | 847 // TODO(rnystrom): The UNC root prefix should include the drive name too, not |
821 // just the "\\". | 848 // just the "\\". |
822 static final windows = new _WindowsStyle(); | 849 static final windows = new _WindowsStyle(); |
823 | 850 |
824 /// URLs aren't filesystem paths, but they're supported by Pathos to make it | 851 /// URLs aren't filesystem paths, but they're supported to make it easier to |
825 /// easier to manipulate URL paths in the browser. | 852 /// manipulate URL paths in the browser. |
826 /// | 853 /// |
827 /// URLs use "/" (forward slash) as separators. Absolute paths either start | 854 /// URLs use "/" (forward slash) as separators. Absolute paths either start |
828 /// with a protocol and optional hostname (e.g. `http://dartlang.org`, | 855 /// with a protocol and optional hostname (e.g. `http://dartlang.org`, |
829 /// `file://`) or with "/". | 856 /// `file://`) or with "/". |
830 static final url = new _UrlStyle(); | 857 static final url = new _UrlStyle(); |
831 | 858 |
832 /// The name of this path style. Will be "posix" or "windows". | 859 /// The name of this path style. Will be "posix" or "windows". |
833 String get name; | 860 String get name; |
834 | 861 |
835 /// The path separator for this style. On POSIX, this is `/`. On Windows, | 862 /// The path separator for this style. On POSIX, this is `/`. On Windows, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
878 if (relativeRootPattern == null) return null; | 905 if (relativeRootPattern == null) return null; |
879 // TODO(rnystrom): Use firstMatch() when #7080 is fixed. | 906 // TODO(rnystrom): Use firstMatch() when #7080 is fixed. |
880 var matches = relativeRootPattern.allMatches(path); | 907 var matches = relativeRootPattern.allMatches(path); |
881 if (matches.isEmpty) return null; | 908 if (matches.isEmpty) return null; |
882 return matches.first[0]; | 909 return matches.first[0]; |
883 } | 910 } |
884 | 911 |
885 /// Returns the path represented by [uri] in this style. | 912 /// Returns the path represented by [uri] in this style. |
886 String pathFromUri(Uri uri); | 913 String pathFromUri(Uri uri); |
887 | 914 |
888 /// Returns the URI that represents [path]. | 915 /// Returns the URI that represents the relative path made of [parts]. |
889 /// | 916 Uri relativePathToUri(Iterable<String> parts) => new Uri(pathSegments: parts); |
nweiz
2013/07/25 20:47:02
It's weird that this takes a list of parts but [ab
Bob Nystrom
2013/07/25 23:20:10
Done. Good call.
| |
890 /// Pathos will always path an absolute path for [path]. Relative paths are | 917 |
891 /// handled automatically by [Builder]. | 918 /// Returns the URI that represents [path], which is assumed to be absolute. |
892 Uri pathToUri(String path); | 919 Uri absolutePathToUri(String path); |
893 | 920 |
894 String toString() => name; | 921 String toString() => name; |
895 } | 922 } |
896 | 923 |
897 /// The style for POSIX paths. | 924 /// The style for POSIX paths. |
898 class _PosixStyle extends Style { | 925 class _PosixStyle extends Style { |
899 _PosixStyle(); | 926 _PosixStyle(); |
900 | 927 |
901 static final _builder = new Builder(style: Style.posix); | 928 static final _builder = new Builder(style: Style.posix); |
902 | 929 |
903 final name = 'posix'; | 930 final name = 'posix'; |
904 final separator = '/'; | 931 final separator = '/'; |
905 final separatorPattern = new RegExp(r'/'); | 932 final separatorPattern = new RegExp(r'/'); |
906 final needsSeparatorPattern = new RegExp(r'[^/]$'); | 933 final needsSeparatorPattern = new RegExp(r'[^/]$'); |
907 final rootPattern = new RegExp(r'^/'); | 934 final rootPattern = new RegExp(r'^/'); |
908 | 935 |
909 String pathFromUri(Uri uri) { | 936 String pathFromUri(Uri uri) { |
910 if (uri.scheme == '' || uri.scheme == 'file') { | 937 if (uri.scheme == '' || uri.scheme == 'file') { |
911 return Uri.decodeComponent(uri.path); | 938 return Uri.decodeComponent(uri.path); |
912 } | 939 } |
913 throw new ArgumentError("Uri $uri must have scheme 'file:'."); | 940 throw new ArgumentError("Uri $uri must have scheme 'file:'."); |
914 } | 941 } |
915 | 942 |
916 Uri pathToUri(String path) { | 943 Uri absolutePathToUri(String path) { |
917 var parsed = _builder._parse(path); | 944 var parsed = _builder._parse(path); |
918 | 945 |
919 if (parsed.parts.isEmpty) { | 946 if (parsed.parts.isEmpty) { |
920 // If the path is a bare root (e.g. "/"), [components] will | 947 // If the path is a bare root (e.g. "/"), [components] will |
921 // currently be empty. We add two empty components so the URL constructor | 948 // currently be empty. We add two empty components so the URL constructor |
922 // produces "file:///", with a trailing slash. | 949 // produces "file:///", with a trailing slash. |
923 parsed.parts.addAll(["", ""]); | 950 parsed.parts.addAll(["", ""]); |
924 } else if (parsed.hasTrailingSeparator) { | 951 } else if (parsed.hasTrailingSeparator) { |
925 // If the path has a trailing slash, add a single empty component so the | 952 // If the path has a trailing slash, add a single empty component so the |
926 // URI has a trailing slash as well. | 953 // URI has a trailing slash as well. |
(...skipping 26 matching lines...) Expand all Loading... | |
953 // Drive-letter paths look like "file:///C:/path/to/file". The | 980 // Drive-letter paths look like "file:///C:/path/to/file". The |
954 // replaceFirst removes the extra initial slash. | 981 // replaceFirst removes the extra initial slash. |
955 if (path.startsWith('/')) path = path.replaceFirst("/", ""); | 982 if (path.startsWith('/')) path = path.replaceFirst("/", ""); |
956 } else { | 983 } else { |
957 // Network paths look like "file://hostname/path/to/file". | 984 // Network paths look like "file://hostname/path/to/file". |
958 path = '\\\\${uri.host}$path'; | 985 path = '\\\\${uri.host}$path'; |
959 } | 986 } |
960 return Uri.decodeComponent(path.replaceAll("/", "\\")); | 987 return Uri.decodeComponent(path.replaceAll("/", "\\")); |
961 } | 988 } |
962 | 989 |
963 Uri pathToUri(String path) { | 990 Uri absolutePathToUri(String path) { |
964 var parsed = _builder._parse(path); | 991 var parsed = _builder._parse(path); |
965 if (parsed.root == r'\\') { | 992 if (parsed.root == r'\\') { |
966 // Network paths become "file://hostname/path/to/file". | 993 // Network paths become "file://hostname/path/to/file". |
967 | 994 |
968 var host = parsed.parts.removeAt(0); | 995 var host = parsed.parts.removeAt(0); |
969 | 996 |
970 if (parsed.parts.isEmpty) { | 997 if (parsed.parts.isEmpty) { |
971 // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will | 998 // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will |
972 // currently be empty. We add two empty components so the URL | 999 // currently be empty. We add two empty components so the URL |
973 // constructor produces "file://hostname/", with a trailing slash. | 1000 // constructor produces "file://hostname/", with a trailing slash. |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1006 final name = 'url'; | 1033 final name = 'url'; |
1007 final separator = '/'; | 1034 final separator = '/'; |
1008 final separatorPattern = new RegExp(r'/'); | 1035 final separatorPattern = new RegExp(r'/'); |
1009 final needsSeparatorPattern = new RegExp( | 1036 final needsSeparatorPattern = new RegExp( |
1010 r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); | 1037 r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); |
1011 final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); | 1038 final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); |
1012 final relativeRootPattern = new RegExp(r"^/"); | 1039 final relativeRootPattern = new RegExp(r"^/"); |
1013 | 1040 |
1014 String pathFromUri(Uri uri) => uri.toString(); | 1041 String pathFromUri(Uri uri) => uri.toString(); |
1015 | 1042 |
1016 Uri pathToUri(String path) => Uri.parse(path); | 1043 Uri relativePathToUri(Iterable<String> parts) { |
1044 // Since [parts] is itself from a URI, it may contain percent-encoded | |
1045 // components. We don't want to double percent-encode them, so we | |
1046 // percent-decode first. | |
1047 return super.relativePathToUri(parts.map(Uri.decodeComponent)); | |
nweiz
2013/07/25 20:47:02
Seems unnecessary to parse and then re-serialize t
Bob Nystrom
2013/07/25 23:20:10
Done.
| |
1048 } | |
1049 | |
1050 Uri absolutePathToUri(String path) => Uri.parse(path); | |
1017 } | 1051 } |
1018 | 1052 |
1019 // TODO(rnystrom): Make this public? | 1053 // TODO(rnystrom): Make this public? |
1020 class _ParsedPath { | 1054 class _ParsedPath { |
1021 /// The [Style] that was used to parse this path. | 1055 /// The [Style] that was used to parse this path. |
1022 Style style; | 1056 Style style; |
1023 | 1057 |
1024 /// The absolute root portion of the path, or `null` if the path is relative. | 1058 /// The absolute root portion of the path, or `null` if the path is relative. |
1025 /// On POSIX systems, this will be `null` or "/". On Windows, it can be | 1059 /// On POSIX systems, this will be `null` or "/". On Windows, it can be |
1026 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive | 1060 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1152 // doesn't count. | 1186 // doesn't count. |
1153 if (lastDot <= 0) return [file, '']; | 1187 if (lastDot <= 0) return [file, '']; |
1154 | 1188 |
1155 return [file.substring(0, lastDot), file.substring(lastDot)]; | 1189 return [file.substring(0, lastDot), file.substring(lastDot)]; |
1156 } | 1190 } |
1157 | 1191 |
1158 _ParsedPath clone() => new _ParsedPath( | 1192 _ParsedPath clone() => new _ParsedPath( |
1159 style, root, isRootRelative, | 1193 style, root, isRootRelative, |
1160 new List.from(parts), new List.from(separators)); | 1194 new List.from(parts), new List.from(separators)); |
1161 } | 1195 } |
OLD | NEW |