Skip to content

Commit d36892d

Browse files
authored
Add support for multiple extensions (flutter#69)
Closes flutter#13
1 parent 81b57c5 commit d36892d

File tree

6 files changed

+77
-5
lines changed

6 files changed

+77
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.7.0
2+
3+
* Add support for multiple extension in `context.extension()`.
4+
15
## 1.6.4
26

37
* Fixed a number of lints that affect the package health score.

lib/path.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,18 @@ String dirname(String path) => context.dirname(path);
194194
///
195195
/// p.extension('~/.bashrc'); // -> ''
196196
/// p.extension('~/.notes.txt'); // -> '.txt'
197-
String extension(String path) => context.extension(path);
197+
///
198+
/// Takes an optional parameter `level` which makes possible to return
199+
/// multiple extensions having `level` number of dots. If `level` exceeds the
200+
/// number of dots, the full extension is returned. The value of `level` must
201+
/// be greater than 0, else `RangeError` is thrown.
202+
///
203+
/// p.extension('foo.bar.dart.js', 2); // -> '.dart.js
204+
/// p.extension('foo.bar.dart.js', 3); // -> '.bar.dart.js'
205+
/// p.extension('foo.bar.dart.js', 10); // -> '.bar.dart.js'
206+
/// p.extension('path/to/foo.bar.dart.js', 2); // -> '.dart.js'
207+
String extension(String path, [int level = 1]) =>
208+
context.extension(path, level);
198209

199210
// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
200211
/// Returns the root of [path], if it's absolute, or the empty string if it's

lib/src/context.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,18 @@ class Context {
145145
///
146146
/// context.extension('~/.bashrc'); // -> ''
147147
/// context.extension('~/.notes.txt'); // -> '.txt'
148-
String extension(String path) => _parse(path).extension;
148+
///
149+
/// Takes an optional parameter `level` which makes possible to return
150+
/// multiple extensions having `level` number of dots. If `level` exceeds the
151+
/// number of dots, the full extension is returned. The value of `level` must
152+
/// be greater than 0, else `RangeError` is thrown.
153+
///
154+
/// context.extension('foo.bar.dart.js', 2); // -> '.dart.js
155+
/// context.extension('foo.bar.dart.js', 3); // -> '.bar.dart.js'
156+
/// context.extension('foo.bar.dart.js', 10); // -> '.bar.dart.js'
157+
/// context.extension('path/to/foo.bar.dart.js', 2); // -> '.dart.js'
158+
String extension(String path, [int level = 1]) =>
159+
_parse(path).extension(level);
149160

150161
// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
151162
/// Returns the root of [path] if it's absolute, or an empty string if it's

lib/src/parsed_path.dart

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ParsedPath {
3333

3434
/// The file extension of the last non-empty part, or "" if it doesn't have
3535
/// one.
36-
String get extension => _splitExtension()[1];
36+
String extension([int level]) => _splitExtension(level)[1];
3737

3838
/// `true` if this is an absolute path.
3939
bool get isAbsolute => root != null;
@@ -161,18 +161,47 @@ class ParsedPath {
161161
return builder.toString();
162162
}
163163

164+
/// Returns k-th last index of the `character` in the `path`.
165+
///
166+
/// If `k` exceeds the count of `character`s in `path`, the left most index
167+
/// of the `character` is returned.
168+
int _kthLastIndexOf(String path, String character, int k) {
169+
var count = 0, leftMostIndexedCharacter = 0;
170+
for (var index = path.length - 1; index >= 0; --index) {
171+
if (path[index] == character) {
172+
leftMostIndexedCharacter = index;
173+
++count;
174+
if (count == k) {
175+
return index;
176+
}
177+
}
178+
}
179+
return leftMostIndexedCharacter;
180+
}
181+
164182
/// Splits the last non-empty part of the path into a `[basename, extension`]
165183
/// pair.
166184
///
185+
/// Takes an optional parameter `level` which makes possible to return
186+
/// multiple extensions having `level` number of dots. If `level` exceeds the
187+
/// number of dots, the path is splitted into the left most dot. The value of
188+
/// `level` must be greater than 0, else `RangeError` is thrown.
189+
///
167190
/// Returns a two-element list. The first is the name of the file without any
168191
/// extension. The second is the extension or "" if it has none.
169-
List<String> _splitExtension() {
192+
List<String> _splitExtension([int level = 1]) {
193+
if (level == null) throw ArgumentError.notNull('level');
194+
if (level <= 0) {
195+
throw RangeError.value(
196+
level, 'level', "level's value must be greater than 0");
197+
}
198+
170199
final file = parts.lastWhere((p) => p != '', orElse: () => null);
171200

172201
if (file == null) return ['', ''];
173202
if (file == '..') return ['..', ''];
174203

175-
final lastDot = file.lastIndexOf('.');
204+
final lastDot = _kthLastIndexOf(file, '.', level);
176205

177206
// If there is no dot, or it's the first character, like '.bashrc', it
178207
// doesn't count.

test/posix_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ void main() {
2626
expect(context.extension(r'a.b\c'), r'.b\c');
2727
expect(context.extension('foo.dart/'), '.dart');
2828
expect(context.extension('foo.dart//'), '.dart');
29+
expect(context.extension('foo.bar.dart.js', 2), '.dart.js');
30+
expect(context.extension(r'foo.bar.dart.js', 3), '.bar.dart.js');
31+
expect(context.extension(r'foo.bar.dart.js', 10), '.bar.dart.js');
32+
expect(context.extension('a.b/c.d', 2), '.d');
33+
expect(() => context.extension(r'foo.bar.dart.js', 0), throwsRangeError);
34+
expect(() => context.extension(r'foo.bar.dart.js', -1), throwsRangeError);
35+
expect(
36+
() => context.extension(r'foo.bar.dart.js', null), throwsArgumentError);
2937
});
3038

3139
test('rootPrefix', () {

test/windows_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ void main() {
2929
expect(context.extension(r'a.b/c'), r'');
3030
expect(context.extension(r'foo.dart\'), '.dart');
3131
expect(context.extension(r'foo.dart\\'), '.dart');
32+
expect(context.extension('a.b/..', 2), '');
33+
expect(context.extension('foo.bar.dart.js', 2), '.dart.js');
34+
expect(context.extension(r'foo.bar.dart.js', 3), '.bar.dart.js');
35+
expect(context.extension(r'foo.bar.dart.js', 10), '.bar.dart.js');
36+
expect(context.extension('a.b/c.d', 2), '.d');
37+
expect(() => context.extension(r'foo.bar.dart.js', 0), throwsRangeError);
38+
expect(() => context.extension(r'foo.bar.dart.js', -1), throwsRangeError);
39+
expect(
40+
() => context.extension(r'foo.bar.dart.js', null), throwsArgumentError);
3241
});
3342

3443
test('rootPrefix', () {

0 commit comments

Comments
 (0)