Skip to content

Commit

Permalink
Add support for multiple extensions (dart-lang/path#69)
Browse files Browse the repository at this point in the history
Closes dart-lang/path#13
  • Loading branch information
ayan-b authored Apr 6, 2020
1 parent d1bdce2 commit 3bd6f0d
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 5 deletions.
4 changes: 4 additions & 0 deletions pkgs/path/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
13 changes: 12 additions & 1 deletion pkgs/path/lib/path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,18 @@ String dirname(String path) => context.dirname(path);
///
/// 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
Expand Down
13 changes: 12 additions & 1 deletion pkgs/path/lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,18 @@ class Context {
///
/// 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
Expand Down
35 changes: 32 additions & 3 deletions pkgs/path/lib/src/parsed_path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ParsedPath {

/// 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;
Expand Down Expand Up @@ -161,18 +161,47 @@ class ParsedPath {
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.
Expand Down
8 changes: 8 additions & 0 deletions pkgs/path/test/posix_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ void main() {
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', () {
Expand Down
9 changes: 9 additions & 0 deletions pkgs/path/test/windows_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ void main() {
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', () {
Expand Down

0 comments on commit 3bd6f0d

Please sign in to comment.