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 |
11 /// functional interface and not require users to create one. | 11 /// functional interface and not require users to create one. |
12 final _builder = new Builder(); | 12 final _builder = new Builder(); |
13 | 13 |
14 /// Gets the path to the current working directory. | 14 /// Gets the path to the current working directory. |
15 String get current => new io.Directory.current().path; | 15 String get current => new io.Directory.current().path; |
16 | 16 |
17 /// Gets the path separator for the current platform. On Mac and Linux, this | 17 /// Gets the path separator for the current platform. On Mac and Linux, this |
18 /// is `/`. On Windows, it's `\`. | 18 /// is `/`. On Windows, it's `\`. |
19 String get separator => _builder.separator; | 19 String get separator => _builder.separator; |
20 | 20 |
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 file extension of [path]; the portion after the last `.` in the | 27 /// Gets the part of [path] after the last separator. |
28 /// [basename] of the path. | 28 /// |
| 29 /// path.basename('path/to/foo.dart'); // -> 'foo.dart' |
| 30 /// path.basename('path/to'); // -> 'to' |
| 31 String basename(String path) => _builder.basename(path); |
| 32 |
| 33 /// Gets the part of [path] after the last separator, and without any trailing |
| 34 /// file extension. |
| 35 /// |
| 36 /// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' |
| 37 String basenameWithoutExtension(String path) => |
| 38 _builder.basenameWithoutExtension(path); |
| 39 |
| 40 /// Gets the file extension of [path]: the portion of [basename] from the last |
| 41 /// `.` to the end (including the `.` itself). |
29 /// | 42 /// |
30 /// path.extension('path/to/foo.dart'); // -> '.dart' | 43 /// path.extension('path/to/foo.dart'); // -> '.dart' |
31 /// path.extension('path/to/foo'); // -> '' | 44 /// path.extension('path/to/foo'); // -> '' |
32 /// path.extension('path.to/foo'); // -> '' | 45 /// path.extension('path.to/foo'); // -> '' |
33 /// path.extension('path/to/foo.dart.js'); // -> '.js' | 46 /// path.extension('path/to/foo.dart.js'); // -> '.js' |
34 /// | 47 /// |
35 /// If the file name starts with a `.`, then it is not considered an extension: | 48 /// If the file name starts with a `.`, then that is not considered the |
| 49 /// extension: |
36 /// | 50 /// |
37 /// path.extension('~/.bashrc'); // -> '' | 51 /// path.extension('~/.bashrc'); // -> '' |
| 52 /// path.extension('~/.notes.txt'); // -> '.txt' |
38 String extension(String path) => _builder.extension(path); | 53 String extension(String path) => _builder.extension(path); |
39 | 54 |
40 /// Gets the part of [path] after the last separator on the current platform. | |
41 /// | |
42 /// path.filename('path/to/foo.dart'); // -> 'foo.dart' | |
43 /// path.filename('path/to'); // -> 'to' | |
44 String filename(String path) => _builder.filename(path); | |
45 | |
46 /// Gets the part of [path] after the last separator on the current platform, | |
47 /// and without any trailing file extension. | |
48 /// | |
49 /// path.filenameWithoutExtension('path/to/foo.dart'); // -> 'foo' | |
50 String filenameWithoutExtension(String path) => | |
51 _builder.filenameWithoutExtension(path); | |
52 | |
53 /// Returns `true` if [path] is an absolute path and `false` if it is a | 55 /// Returns `true` if [path] is an absolute path and `false` if it is a |
54 /// relative path. On Mac and Unix systems, relative paths start with a `/` | 56 /// relative path. On POSIX systems, absolute paths start with a `/` (forward |
55 /// (forward slash). On Windows, an absolute path starts with `\\`, or a drive | 57 /// slash). On Windows, an absolute path starts with `\\`, or a drive letter |
56 /// letter followed by `:/` or `:\`. | 58 /// followed by `:/` or `:\`. |
57 bool isAbsolute(String path) => _builder.isAbsolute(path); | 59 bool isAbsolute(String path) => _builder.isAbsolute(path); |
58 | 60 |
59 /// Returns `true` if [path] is a relative path and `false` if it is absolute. | 61 /// Returns `true` if [path] is a relative path and `false` if it is absolute. |
60 /// On Mac and Unix systems, relative paths start with a `/` (forward slash). | 62 /// On POSIX systems, absolute paths start with a `/` (forward slash). On |
61 /// On Windows, an absolute path starts with `\\`, or a drive letter followed | 63 /// Windows, an absolute path starts with `\\`, or a drive letter followed by |
62 /// by `:/` or `:\`. | 64 /// `:/` or `:\`. |
63 bool isRelative(String path) => _builder.isRelative(path); | 65 bool isRelative(String path) => _builder.isRelative(path); |
64 | 66 |
65 /// Joins the given path parts into a single path using the current platform's | 67 /// Joins the given path parts into a single path using the current platform's |
66 /// [separator]. Example: | 68 /// [separator]. Example: |
67 /// | 69 /// |
68 /// path.join('path', 'to', 'foo'); // -> 'path/to/foo' | 70 /// path.join('path', 'to', 'foo'); // -> 'path/to/foo' |
69 /// | 71 /// |
70 /// If any part ends in a path separator, then a redundant separator will not | 72 /// If any part ends in a path separator, then a redundant separator will not |
71 /// be added: | 73 /// be added: |
72 /// | 74 /// |
73 /// path.join('path/', 'to', 'foo'); // -> 'path/to/foo | 75 /// path.join('path/', 'to', 'foo'); // -> 'path/to/foo |
74 /// | 76 /// |
75 /// If a part is an absolute path, then anything before that will be ignored: | 77 /// If a part is an absolute path, then anything before that will be ignored: |
76 /// | 78 /// |
77 /// path.join('path', '/to', 'foo'); // -> '/to/foo' | 79 /// path.join('path', '/to', 'foo'); // -> '/to/foo' |
78 /// | |
79 String join(String part1, [String part2, String part3, String part4, | 80 String join(String part1, [String part2, String part3, String part4, |
80 String part5, String part6, String part7, String part8]) { | 81 String part5, String part6, String part7, String part8]) { |
81 if (!?part2) return _builder.join(part1); | 82 if (!?part2) return _builder.join(part1); |
82 if (!?part3) return _builder.join(part1, part2); | 83 if (!?part3) return _builder.join(part1, part2); |
83 if (!?part4) return _builder.join(part1, part2, part3); | 84 if (!?part4) return _builder.join(part1, part2, part3); |
84 if (!?part5) return _builder.join(part1, part2, part3, part4); | 85 if (!?part5) return _builder.join(part1, part2, part3, part4); |
85 if (!?part6) return _builder.join(part1, part2, part3, part4, part5); | 86 if (!?part6) return _builder.join(part1, part2, part3, part4, part5); |
86 if (!?part7) return _builder.join(part1, part2, part3, part4, part5, part6); | 87 if (!?part7) return _builder.join(part1, part2, part3, part4, part5, part6); |
87 if (!?part8) return _builder.join(part1, part2, part3, part4, part5, part6, | 88 if (!?part8) return _builder.join(part1, part2, part3, part4, part5, part6, |
88 part7); | 89 part7); |
89 return _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); | 90 return _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); |
90 } | 91 } |
91 | 92 |
92 /// Normalizes [path], simplifying it by handling `..`, and `.`, and | 93 /// Normalizes [path], simplifying it by handling `..`, and `.`, and |
93 /// removing redundant path separators whenever possible. | 94 /// removing redundant path separators whenever possible. |
94 /// | 95 /// |
95 /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' | 96 /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' |
96 String normalize(String path) => _builder.normalize(path); | 97 String normalize(String path) => _builder.normalize(path); |
97 | 98 |
98 /// Converts [path] to an equivalent relative path from the current directory. | 99 /// Attempts to convert [path] to an equivalent relative path from the current |
| 100 /// directory. |
99 /// | 101 /// |
100 /// // Given current directory is /root/path: | 102 /// // Given current directory is /root/path: |
101 /// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' | 103 /// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' |
102 /// path.relative('/root/other.dart'); // -> '../other.dart' | 104 /// path.relative('/root/other.dart'); // -> '../other.dart' |
| 105 /// |
| 106 /// Since there is no relative path from one drive letter to another on Windows, |
| 107 /// this will return an absolute path in that case. |
| 108 /// |
| 109 /// // Given current directory is C:\home: |
| 110 /// path.relative(r'D:\other'); // -> 'D:\other' |
103 String relative(String path) => _builder.relative(path); | 111 String relative(String path) => _builder.relative(path); |
104 | 112 |
105 /// Removes a trailing extension from the last part of [path]. | 113 /// Removes a trailing extension from the last part of [path]. |
106 /// | 114 /// |
107 /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | 115 /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' |
108 String withoutExtension(String path) => _builder.withoutExtension(path); | 116 String withoutExtension(String path) => _builder.withoutExtension(path); |
109 | 117 |
110 /// An instantiable class for manipulating paths. Unlike the top-level | 118 /// An instantiable class for manipulating paths. Unlike the top-level |
111 /// functions, this lets you explicitly select what platform the paths will use. | 119 /// functions, this lets you explicitly select what platform the paths will use. |
112 class Builder { | 120 class Builder { |
113 /// Creates a new path builder for the given style and root directory. | 121 /// Creates a new path builder for the given style and root directory. |
114 /// | 122 /// |
115 /// If [style] is omitted, it uses the host operating system's path style. If | 123 /// If [style] is omitted, it uses the host operating system's path style. If |
116 /// [root] is omitted, it defaults to the current working directory. | 124 /// [root] is omitted, it defaults to the current working directory. If [root] |
| 125 /// is relative, it is considered relative to the current working directory. |
117 factory Builder({Style style, String root}) { | 126 factory Builder({Style style, String root}) { |
118 if (style == null) { | 127 if (style == null) { |
119 if (io.Platform.operatingSystem == 'windows') { | 128 if (io.Platform.operatingSystem == 'windows') { |
120 style = Style.windows; | 129 style = Style.windows; |
121 } else { | 130 } else { |
122 style = Style.posix; | 131 style = Style.posix; |
123 } | 132 } |
124 } | 133 } |
125 | 134 |
126 if (root == null) root = new io.Directory.current().path; | 135 if (root == null) root = current; |
127 | 136 |
128 return new Builder._(style, root); | 137 return new Builder._(style, root); |
129 } | 138 } |
130 | 139 |
131 Builder._(this.style, this.root); | 140 Builder._(this.style, this.root); |
132 | 141 |
133 /// The style of path that this builder works with. | 142 /// The style of path that this builder works with. |
134 final Style style; | 143 final Style style; |
135 | 144 |
136 /// The root directory that relative paths will be relative to. | 145 /// The root directory that relative paths will be relative to. |
137 final String root; | 146 final String root; |
138 | 147 |
139 /// Gets the path separator for the builder's [style]. On Mac and Linux, | 148 /// Gets the path separator for the builder's [style]. On Mac and Linux, |
140 /// this is `/`. On Windows, it's `\`. | 149 /// this is `/`. On Windows, it's `\`. |
141 String get separator => style.separator; | 150 String get separator => style.separator; |
142 | 151 |
143 /// Gets the file extension of [path]; the portion after the last `.` in the | 152 /// Gets the part of [path] after the last separator on the builder's |
144 /// [basename] of the path. | 153 /// platform. |
| 154 /// |
| 155 /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' |
| 156 /// builder.basename('path/to'); // -> 'to' |
| 157 String basename(String path) => _parse(path).basename; |
| 158 |
| 159 /// Gets the part of [path] after the last separator on the builder's |
| 160 /// platform, and without any trailing file extension. |
| 161 /// |
| 162 /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' |
| 163 String basenameWithoutExtension(String path) => |
| 164 _parse(path).basenameWithoutExtension; |
| 165 |
| 166 /// Gets the file extension of [path]: the portion of [basename] from the last |
| 167 /// `.` to the end (including the `.` itself). |
145 /// | 168 /// |
146 /// builder.extension('path/to/foo.dart'); // -> '.dart' | 169 /// builder.extension('path/to/foo.dart'); // -> '.dart' |
147 /// builder.extension('path/to/foo'); // -> '' | 170 /// builder.extension('path/to/foo'); // -> '' |
148 /// builder.extension('path.to/foo'); // -> '' | 171 /// builder.extension('path.to/foo'); // -> '' |
149 /// builder.extension('path/to/foo.dart.js'); // -> '.js' | 172 /// builder.extension('path/to/foo.dart.js'); // -> '.js' |
150 /// | 173 /// |
151 /// If the file name starts with a `.`, then it is not considered an | 174 /// If the file name starts with a `.`, then it is not considered an |
152 /// extension: | 175 /// extension: |
153 /// | 176 /// |
154 /// builder.extension('~/.bashrc'); // -> '' | 177 /// builder.extension('~/.bashrc'); // -> '' |
| 178 /// builder.extension('~/.notes.txt'); // -> '.txt' |
155 String extension(String path) => _parse(path).extension; | 179 String extension(String path) => _parse(path).extension; |
156 | 180 |
157 /// Gets the part of [path] after the last separator on the builder's | |
158 /// platform. | |
159 /// | |
160 /// builder.filename('path/to/foo.dart'); // -> 'foo.dart' | |
161 /// builder.filename('path/to'); // -> 'to' | |
162 String filename(String path) => _parse(path).filename; | |
163 | |
164 /// Gets the part of [path] after the last separator on the builder's | |
165 /// platform, and without any trailing file extension. | |
166 /// | |
167 /// builder.filenameWithoutExtension('path/to/foo.dart'); // -> 'foo' | |
168 String filenameWithoutExtension(String path) => | |
169 _parse(path).filenameWithoutExtension; | |
170 | |
171 /// Returns `true` if [path] is an absolute path and `false` if it is a | 181 /// Returns `true` if [path] is an absolute path and `false` if it is a |
172 /// relative path. On Mac and Unix systems, relative paths start with a `/` | 182 /// relative path. On POSIX systems, absolute paths start with a `/` (forward |
173 /// (forward slash). On Windows, an absolute path starts with `\\`, or a drive | 183 /// slash). On Windows, an absolute path starts with `\\`, or a drive letter |
174 /// letter followed by `:/` or `:\`. | 184 /// followed by `:/` or `:\`. |
175 bool isAbsolute(String path) => _parse(path).isAbsolute; | 185 bool isAbsolute(String path) => _parse(path).isAbsolute; |
176 | 186 |
177 /// Returns `true` if [path] is a relative path and `false` if it is absolute. | 187 /// Returns `true` if [path] is a relative path and `false` if it is absolute. |
178 /// On Mac and Unix systems, relative paths start with a `/` (forward slash). | 188 /// On POSIX systems, absolute paths start with a `/` (forward slash). On |
179 /// On Windows, an absolute path starts with `\\`, or a drive letter followed | 189 /// Windows, an absolute path starts with `\\`, or a drive letter followed by |
180 /// by `:/` or `:\`. | 190 /// `:/` or `:\`. |
181 bool isRelative(String path) => !isAbsolute(path); | 191 bool isRelative(String path) => !isAbsolute(path); |
182 | 192 |
183 /// Joins the given path parts into a single path. Example: | 193 /// Joins the given path parts into a single path. Example: |
184 /// | 194 /// |
185 /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' | 195 /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' |
186 /// | 196 /// |
187 /// If any part ends in a path separator, then a redundant separator will not | 197 /// If any part ends in a path separator, then a redundant separator will not |
188 /// be added: | 198 /// be added: |
189 /// | 199 /// |
190 /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo | 200 /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 | 252 |
243 var parsed = _parse(path); | 253 var parsed = _parse(path); |
244 parsed.normalize(); | 254 parsed.normalize(); |
245 return parsed.toString(); | 255 return parsed.toString(); |
246 } | 256 } |
247 | 257 |
248 /// Creates a new path by appending the given path parts to the [root]. | 258 /// Creates a new path by appending the given path parts to the [root]. |
249 /// Equivalent to [join()] with [root] as the first argument. Example: | 259 /// Equivalent to [join()] with [root] as the first argument. Example: |
250 /// | 260 /// |
251 /// var builder = new Builder(root: 'root'); | 261 /// var builder = new Builder(root: 'root'); |
252 /// builder.join('path', 'to', 'foo'); // -> 'root/path/to/foo' | 262 /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' |
253 String resolve(String part1, [String part2, String part3, String part4, | 263 String resolve(String part1, [String part2, String part3, String part4, |
254 String part5, String part6, String part7]) { | 264 String part5, String part6, String part7]) { |
255 if (!?part2) return join(root, part1); | 265 if (!?part2) return join(root, part1); |
256 if (!?part3) return join(root, part1, part2); | 266 if (!?part3) return join(root, part1, part2); |
257 if (!?part4) return join(root, part1, part2, part3); | 267 if (!?part4) return join(root, part1, part2, part3); |
258 if (!?part5) return join(root, part1, part2, part3, part4); | 268 if (!?part5) return join(root, part1, part2, part3, part4); |
259 if (!?part6) return join(root, part1, part2, part3, part4, part5); | 269 if (!?part6) return join(root, part1, part2, part3, part4, part5); |
260 if (!?part7) return join(root, part1, part2, part3, part4, part5, part6); | 270 if (!?part7) return join(root, part1, part2, part3, part4, part5, part6); |
261 return join(root, part1, part2, part3, part4, part5, part6, part7); | 271 return join(root, part1, part2, part3, part4, part5, part6, part7); |
262 } | 272 } |
263 | 273 |
264 /// Converts [path] to an equivalent relative path starting at [root]. | 274 /// Attempts to convert [path] to an equivalent relative path relative to |
| 275 /// [root]. |
265 /// | 276 /// |
266 /// var builder = new Builder(root: '/root/path'); | 277 /// var builder = new Builder(root: '/root/path'); |
267 /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart' | 278 /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart' |
268 /// builder.relative('/root/other.dart'); // -> '../other.dart' | 279 /// builder.relative('/root/other.dart'); // -> '../other.dart' |
| 280 /// |
| 281 /// Since there is no relative path from one drive letter to another on |
| 282 /// Windows, this will return an absolute path in that case. |
| 283 /// |
| 284 /// var builder = new Builder(root: r'C:\home'); |
| 285 /// builder.relative(r'D:\other'); // -> 'D:\other' |
269 String relative(String path) { | 286 String relative(String path) { |
| 287 if (path == '') return '.'; |
| 288 |
270 // If the base path is relative, resolve it relative to the current | 289 // If the base path is relative, resolve it relative to the current |
271 // directory. | 290 // directory. |
272 var base = root; | 291 var base = root; |
273 if (this.isRelative(base)) base = absolute(base); | 292 if (this.isRelative(base)) base = absolute(base); |
274 | 293 |
275 // If the given path is relative, resolve it relative to the base. | 294 // If the given path is relative, resolve it relative to the base. |
276 path = this.join(base, path); | 295 if (this.isRelative(path)) return this.normalize(path); |
277 | 296 |
278 var baseParsed = _parse(base)..normalize(); | 297 var baseParsed = _parse(base)..normalize(); |
279 var pathParsed = _parse(path)..normalize(); | 298 var pathParsed = _parse(path)..normalize(); |
280 | 299 |
281 // If the root prefixes don't match (for example, different drive letters | 300 // If the root prefixes don't match (for example, different drive letters |
282 // on Windows), then there is no relative path, so just return the absolute | 301 // on Windows), then there is no relative path, so just return the absolute |
283 // one. | 302 // one. |
| 303 // TODO(rnystrom): Drive letters are case-insentive on Windows. Should |
| 304 // handle "C:\" and "c:\" being the same root. |
284 if (baseParsed.root != pathParsed.root) return pathParsed.toString(); | 305 if (baseParsed.root != pathParsed.root) return pathParsed.toString(); |
285 | 306 |
286 // Strip off their common prefix. | 307 // Strip off their common prefix. |
287 while (baseParsed.parts.length > 0 && pathParsed.parts.length > 0) { | 308 while (baseParsed.parts.length > 0 && pathParsed.parts.length > 0 && |
288 if (baseParsed.parts[0] != pathParsed.parts[0]) break; | 309 baseParsed.parts[0] == pathParsed.parts[0]) { |
289 baseParsed.parts.removeAt(0); | 310 baseParsed.parts.removeAt(0); |
290 baseParsed.separators.removeAt(0); | 311 baseParsed.separators.removeAt(0); |
291 pathParsed.parts.removeAt(0); | 312 pathParsed.parts.removeAt(0); |
292 pathParsed.separators.removeAt(0); | 313 pathParsed.separators.removeAt(0); |
293 } | 314 } |
294 | 315 |
295 // If there are any directories left in the root path, we need to walk up | 316 // If there are any directories left in the root path, we need to walk up |
296 // out of them. | 317 // out of them. |
297 pathParsed.parts.insertRange(0, baseParsed.parts.length, '..'); | 318 pathParsed.parts.insertRange(0, baseParsed.parts.length, '..'); |
298 pathParsed.separators.insertRange(0, baseParsed.parts.length, | 319 pathParsed.separators.insertRange(0, baseParsed.parts.length, |
299 style.separator); | 320 style.separator); |
300 | 321 |
301 // Corner case: the paths completely collapsed. | 322 // Corner case: the paths completely collapsed. |
302 if (pathParsed.parts.length == 0) return '.'; | 323 if (pathParsed.parts.length == 0) return '.'; |
303 | 324 |
304 // Make it relative. | 325 // Make it relative. |
305 pathParsed.root = ''; | 326 pathParsed.root = ''; |
306 pathParsed.removeTrailingSeparator(); | 327 pathParsed.removeTrailingSeparator(); |
307 | 328 |
308 return pathParsed.toString(); | 329 return pathParsed.toString(); |
309 } | 330 } |
310 | 331 |
311 /// Removes a trailing extension from the last part of [path]. | 332 /// Removes a trailing extension from the last part of [path]. |
312 /// | 333 /// |
313 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | 334 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' |
314 String withoutExtension(String path) { | 335 String withoutExtension(String path) { |
315 var lastSeparator = path.lastIndexOf(separator); | 336 var parsed = _parse(path); |
316 var lastDot = path.lastIndexOf('.'); | 337 parsed.extension = null; |
317 | 338 return parsed.toString(); |
318 // Ignore '.' in anything but the last component. | |
319 if (lastSeparator != -1 && lastDot <= lastSeparator + 1) lastDot = -1; | |
320 | |
321 if (lastDot <= 0) return path; | |
322 return path.substring(0, lastDot); | |
323 } | 339 } |
324 | 340 |
325 _ParsedPath _parse(String path) { | 341 _ParsedPath _parse(String path) { |
326 var before = path; | 342 var before = path; |
327 | 343 |
328 // Remove the root prefix, if any. | 344 // Remove the root prefix, if any. |
329 var root = style.getRoot(path); | 345 var root = style.getRoot(path); |
330 if (root != null) path = path.substring(root.length); | 346 if (root != null) path = path.substring(root.length); |
331 | 347 |
332 // Split the parts on path separators. | 348 // Split the parts on path separators. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 | 382 |
367 /// An enum type describing a "flavor" of path. | 383 /// An enum type describing a "flavor" of path. |
368 class Style { | 384 class Style { |
369 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths | 385 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths |
370 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. | 386 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. |
371 static final posix = new Style._('posix', '/', '/', '/'); | 387 static final posix = new Style._('posix', '/', '/', '/'); |
372 | 388 |
373 /// Windows paths use "\" (backslash) as separators. Absolute paths start with | 389 /// Windows paths use "\" (backslash) as separators. Absolute paths start with |
374 /// a drive letter followed by a colon (example, "C:") or two backslashes | 390 /// a drive letter followed by a colon (example, "C:") or two backslashes |
375 /// ("\\") for UNC paths. | 391 /// ("\\") for UNC paths. |
| 392 // TODO(rnystrom): The UNC root prefix should include the drive name too, not |
| 393 // just the "\\". |
376 static final windows = new Style._('windows', '\\', r'[/\\]', | 394 static final windows = new Style._('windows', '\\', r'[/\\]', |
377 r'\\\\|[a-zA-Z]:[/\\]'); | 395 r'\\\\|[a-zA-Z]:[/\\]'); |
378 | 396 |
379 Style._(this.name, this.separator, String separatorPattern, String rootPattern
) | 397 Style._(this.name, this.separator, String separatorPattern, |
| 398 String rootPattern) |
380 : separatorPattern = new RegExp(separatorPattern), | 399 : separatorPattern = new RegExp(separatorPattern), |
381 _rootPattern = new RegExp('^$rootPattern'); | 400 _rootPattern = new RegExp('^$rootPattern'); |
382 | 401 |
383 /// The name of this path style. Will be "posix" or "windows". | 402 /// The name of this path style. Will be "posix" or "windows". |
384 final String name; | 403 final String name; |
385 | 404 |
386 /// The path separator for this style. On POSIX, this is `/`. On Windows, | 405 /// The path separator for this style. On POSIX, this is `/`. On Windows, |
387 /// it's `\`. | 406 /// it's `\`. |
388 final String separator; | 407 final String separator; |
389 | 408 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
435 if (separators.length == 0) return false; | 454 if (separators.length == 0) return false; |
436 return separators[separators.length - 1] != ''; | 455 return separators[separators.length - 1] != ''; |
437 } | 456 } |
438 | 457 |
439 /// `true` if this is an absolute path. | 458 /// `true` if this is an absolute path. |
440 bool get isAbsolute => root != null; | 459 bool get isAbsolute => root != null; |
441 | 460 |
442 _ParsedPath(this.style, this.root, this.parts, this.separators, | 461 _ParsedPath(this.style, this.root, this.parts, this.separators, |
443 this.extension); | 462 this.extension); |
444 | 463 |
445 String get filename { | 464 String get basename { |
446 if (parts.length == 0) return extension; | 465 if (parts.length == 0) return extension; |
447 if (hasTrailingSeparator) return ''; | 466 if (hasTrailingSeparator) return ''; |
448 return '${parts.last}$extension'; | 467 return '${parts.last}$extension'; |
449 } | 468 } |
450 | 469 |
451 String get filenameWithoutExtension { | 470 String get basenameWithoutExtension { |
452 if (parts.length == 0) return ''; | 471 if (parts.length == 0) return ''; |
453 if (hasTrailingSeparator) return ''; | 472 if (hasTrailingSeparator) return ''; |
454 return parts.last; | 473 return parts.last; |
455 } | 474 } |
456 | 475 |
457 void removeTrailingSeparator() { | 476 void removeTrailingSeparator() { |
458 if (separators.length > 0) { | 477 if (separators.length > 0) { |
459 separators[separators.length - 1] = ''; | 478 separators[separators.length - 1] = ''; |
460 } | 479 } |
461 } | 480 } |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 if (root != null) builder.add(root); | 524 if (root != null) builder.add(root); |
506 for (var i = 0; i < parts.length; i++) { | 525 for (var i = 0; i < parts.length; i++) { |
507 builder.add(parts[i]); | 526 builder.add(parts[i]); |
508 if (extension != null && i == parts.length - 1) builder.add(extension); | 527 if (extension != null && i == parts.length - 1) builder.add(extension); |
509 builder.add(separators[i]); | 528 builder.add(separators[i]); |
510 } | 529 } |
511 | 530 |
512 return builder.toString(); | 531 return builder.toString(); |
513 } | 532 } |
514 } | 533 } |
OLD | NEW |