Add support for multiple extensions (#69)
Closes #13
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0804f02..7564f60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.7.0
+
+* Add support for multiple extension in `context.extension()`.
+
## 1.6.4
* Fixed a number of lints that affect the package health score.
diff --git a/lib/path.dart b/lib/path.dart
index a501e0c..1cecf66 100644
--- a/lib/path.dart
+++ b/lib/path.dart
@@ -194,7 +194,18 @@
///
/// p.extension('~/.bashrc'); // -> ''
/// p.extension('~/.notes.txt'); // -> '.txt'
-String extension(String path) => context.extension(path);
+///
+/// Takes an optional parameter `level` which makes possible to return
+/// multiple extensions having `level` number of dots. If `level` exceeds the
+/// number of dots, the full extension is returned. The value of `level` must
+/// be greater than 0, else `RangeError` is thrown.
+///
+/// p.extension('foo.bar.dart.js', 2); // -> '.dart.js
+/// p.extension('foo.bar.dart.js', 3); // -> '.bar.dart.js'
+/// p.extension('foo.bar.dart.js', 10); // -> '.bar.dart.js'
+/// p.extension('path/to/foo.bar.dart.js', 2); // -> '.dart.js'
+String extension(String path, [int level = 1]) =>
+ context.extension(path, level);
// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
/// Returns the root of [path], if it's absolute, or the empty string if it's
diff --git a/lib/src/context.dart b/lib/src/context.dart
index 9e58e4a..3a99804 100644
--- a/lib/src/context.dart
+++ b/lib/src/context.dart
@@ -145,7 +145,18 @@
///
/// context.extension('~/.bashrc'); // -> ''
/// context.extension('~/.notes.txt'); // -> '.txt'
- String extension(String path) => _parse(path).extension;
+ ///
+ /// Takes an optional parameter `level` which makes possible to return
+ /// multiple extensions having `level` number of dots. If `level` exceeds the
+ /// number of dots, the full extension is returned. The value of `level` must
+ /// be greater than 0, else `RangeError` is thrown.
+ ///
+ /// context.extension('foo.bar.dart.js', 2); // -> '.dart.js
+ /// context.extension('foo.bar.dart.js', 3); // -> '.bar.dart.js'
+ /// context.extension('foo.bar.dart.js', 10); // -> '.bar.dart.js'
+ /// context.extension('path/to/foo.bar.dart.js', 2); // -> '.dart.js'
+ String extension(String path, [int level = 1]) =>
+ _parse(path).extension(level);
// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
/// Returns the root of [path] if it's absolute, or an empty string if it's
diff --git a/lib/src/parsed_path.dart b/lib/src/parsed_path.dart
index 9efd529..a9d443a 100644
--- a/lib/src/parsed_path.dart
+++ b/lib/src/parsed_path.dart
@@ -33,7 +33,7 @@
/// The file extension of the last non-empty part, or "" if it doesn't have
/// one.
- String get extension => _splitExtension()[1];
+ String extension([int level]) => _splitExtension(level)[1];
/// `true` if this is an absolute path.
bool get isAbsolute => root != null;
@@ -161,18 +161,47 @@
return builder.toString();
}
+ /// Returns k-th last index of the `character` in the `path`.
+ ///
+ /// If `k` exceeds the count of `character`s in `path`, the left most index
+ /// of the `character` is returned.
+ int _kthLastIndexOf(String path, String character, int k) {
+ var count = 0, leftMostIndexedCharacter = 0;
+ for (var index = path.length - 1; index >= 0; --index) {
+ if (path[index] == character) {
+ leftMostIndexedCharacter = index;
+ ++count;
+ if (count == k) {
+ return index;
+ }
+ }
+ }
+ return leftMostIndexedCharacter;
+ }
+
/// Splits the last non-empty part of the path into a `[basename, extension`]
/// pair.
///
+ /// Takes an optional parameter `level` which makes possible to return
+ /// multiple extensions having `level` number of dots. If `level` exceeds the
+ /// number of dots, the path is splitted into the left most dot. The value of
+ /// `level` must be greater than 0, else `RangeError` is thrown.
+ ///
/// Returns a two-element list. The first is the name of the file without any
/// extension. The second is the extension or "" if it has none.
- List<String> _splitExtension() {
+ List<String> _splitExtension([int level = 1]) {
+ if (level == null) throw ArgumentError.notNull('level');
+ if (level <= 0) {
+ throw RangeError.value(
+ level, 'level', "level's value must be greater than 0");
+ }
+
final file = parts.lastWhere((p) => p != '', orElse: () => null);
if (file == null) return ['', ''];
if (file == '..') return ['..', ''];
- final lastDot = file.lastIndexOf('.');
+ final lastDot = _kthLastIndexOf(file, '.', level);
// If there is no dot, or it's the first character, like '.bashrc', it
// doesn't count.
diff --git a/test/posix_test.dart b/test/posix_test.dart
index bd6c3e0..5138086 100644
--- a/test/posix_test.dart
+++ b/test/posix_test.dart
@@ -26,6 +26,14 @@
expect(context.extension(r'a.b\c'), r'.b\c');
expect(context.extension('foo.dart/'), '.dart');
expect(context.extension('foo.dart//'), '.dart');
+ expect(context.extension('foo.bar.dart.js', 2), '.dart.js');
+ expect(context.extension(r'foo.bar.dart.js', 3), '.bar.dart.js');
+ expect(context.extension(r'foo.bar.dart.js', 10), '.bar.dart.js');
+ expect(context.extension('a.b/c.d', 2), '.d');
+ expect(() => context.extension(r'foo.bar.dart.js', 0), throwsRangeError);
+ expect(() => context.extension(r'foo.bar.dart.js', -1), throwsRangeError);
+ expect(
+ () => context.extension(r'foo.bar.dart.js', null), throwsArgumentError);
});
test('rootPrefix', () {
diff --git a/test/windows_test.dart b/test/windows_test.dart
index 2d6b90f..5710f06 100644
--- a/test/windows_test.dart
+++ b/test/windows_test.dart
@@ -29,6 +29,15 @@
expect(context.extension(r'a.b/c'), r'');
expect(context.extension(r'foo.dart\'), '.dart');
expect(context.extension(r'foo.dart\\'), '.dart');
+ expect(context.extension('a.b/..', 2), '');
+ expect(context.extension('foo.bar.dart.js', 2), '.dart.js');
+ expect(context.extension(r'foo.bar.dart.js', 3), '.bar.dart.js');
+ expect(context.extension(r'foo.bar.dart.js', 10), '.bar.dart.js');
+ expect(context.extension('a.b/c.d', 2), '.d');
+ expect(() => context.extension(r'foo.bar.dart.js', 0), throwsRangeError);
+ expect(() => context.extension(r'foo.bar.dart.js', -1), throwsRangeError);
+ expect(
+ () => context.extension(r'foo.bar.dart.js', null), throwsArgumentError);
});
test('rootPrefix', () {