From f9fe5281667dd01c4ab8e5821b5d9f110ba1dbc9 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 19 Dec 2012 00:04:37 +0000 Subject: [PATCH 001/183] Move path ("pathos") to pkg/. Review URL: https://codereview.chromium.org//11647003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16297 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 651 ++++++++++++++++++++++++++ pkgs/path/pubspec.yaml | 10 + pkgs/path/test/path_posix_test.dart | 362 ++++++++++++++ pkgs/path/test/path_test.dart | 60 +++ pkgs/path/test/path_windows_test.dart | 396 ++++++++++++++++ 5 files changed, 1479 insertions(+) create mode 100644 pkgs/path/lib/path.dart create mode 100644 pkgs/path/pubspec.yaml create mode 100644 pkgs/path/test/path_posix_test.dart create mode 100644 pkgs/path/test/path_test.dart create mode 100644 pkgs/path/test/path_windows_test.dart diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart new file mode 100644 index 00000000..593396d0 --- /dev/null +++ b/pkgs/path/lib/path.dart @@ -0,0 +1,651 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// A comprehensive, cross-platform path manipulation library. +library path; + +import 'dart:io' as io; + +/// An internal builder for the current OS so we can provide a straight +/// functional interface and not require users to create one. +final _builder = new Builder(); + +/// Gets the path to the current working directory. +String get current => new io.Directory.current().path; + +/// Gets the path separator for the current platform. On Mac and Linux, this +/// is `/`. On Windows, it's `\`. +String get separator => _builder.separator; + +/// Converts [path] to an absolute path by resolving it relative to the current +/// working directory. If [path] is already an absolute path, just returns it. +/// +/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt +String absolute(String path) => join(current, path); + +/// Gets the part of [path] after the last separator. +/// +/// path.basename('path/to/foo.dart'); // -> 'foo.dart' +/// path.basename('path/to'); // -> 'to' +String basename(String path) => _builder.basename(path); + +/// Gets the part of [path] after the last separator, and without any trailing +/// file extension. +/// +/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' +String basenameWithoutExtension(String path) => + _builder.basenameWithoutExtension(path); + +/// Gets the part of [path] before the last separator. +/// +/// path.dirname('path/to/foo.dart'); // -> 'path/to' +/// path.dirname('path/to'); // -> 'to' +String dirname(String path) => _builder.dirname(path); + +/// Gets the file extension of [path]: the portion of [basename] from the last +/// `.` to the end (including the `.` itself). +/// +/// path.extension('path/to/foo.dart'); // -> '.dart' +/// path.extension('path/to/foo'); // -> '' +/// path.extension('path.to/foo'); // -> '' +/// path.extension('path/to/foo.dart.js'); // -> '.js' +/// +/// If the file name starts with a `.`, then that is not considered the +/// extension: +/// +/// path.extension('~/.bashrc'); // -> '' +/// path.extension('~/.notes.txt'); // -> '.txt' +String extension(String path) => _builder.extension(path); + +// 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 +/// relative. +/// +/// // Unix +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('/path/to/foo'); // -> '/' +/// +/// // Windows +/// path.rootPrefix(r'path\to\foo'); // -> '' +/// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +String rootPrefix(String path) => _builder.rootPrefix(path); + +/// Returns `true` if [path] is an absolute path and `false` if it is a +/// relative path. On POSIX systems, absolute paths start with a `/` (forward +/// slash). On Windows, an absolute path starts with `\\`, or a drive letter +/// followed by `:/` or `:\`. +bool isAbsolute(String path) => _builder.isAbsolute(path); + +/// Returns `true` if [path] is a relative path and `false` if it is absolute. +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. +bool isRelative(String path) => _builder.isRelative(path); + +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.join('path', 'to', 'foo'); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.join('path', '/to', 'foo'); // -> '/to/foo' +String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) => + _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); + +// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. +/// Splits [path] into its components using the current platform's [separator]. +/// +/// path.split('path/to/foo'); // -> ['path', 'to', 'foo'] +/// +/// The path will *not* be normalized before splitting. +/// +/// path.split('path/../foo'); // -> ['path', '..', 'foo'] +/// +/// If [path] is absolute, the root directory will be the first element in the +/// array. Example: +/// +/// // Unix +/// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] +/// +/// // Windows +/// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +List split(String path) => _builder.split(path); + +/// Normalizes [path], simplifying it by handling `..`, and `.`, and +/// removing redundant path separators whenever possible. +/// +/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' +String normalize(String path) => _builder.normalize(path); + +/// Attempts to convert [path] to an equivalent relative path from the current +/// directory. +/// +/// // Given current directory is /root/path: +/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' +/// path.relative('/root/other.dart'); // -> '../other.dart' +/// +/// If the [from] argument is passed, [path] is made relative to that instead. +/// +/// path.relative('/root/path/a/b.dart', +/// from: '/root/path'); // -> 'a/b.dart' +/// path.relative('/root/other.dart', +/// from: '/root/path'); // -> '../other.dart' +/// +/// Since there is no relative path from one drive letter to another on Windows, +/// this will return an absolute path in that case. +/// +/// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' +String relative(String path, {String from}) => + _builder.relative(path, from: from); + +/// Removes a trailing extension from the last part of [path]. +/// +/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' +String withoutExtension(String path) => _builder.withoutExtension(path); + +/// An instantiable class for manipulating paths. Unlike the top-level +/// functions, this lets you explicitly select what platform the paths will use. +class Builder { + /// Creates a new path builder for the given style and root directory. + /// + /// If [style] is omitted, it uses the host operating system's path style. If + /// [root] is omitted, it defaults to the current working directory. If [root] + /// is relative, it is considered relative to the current working directory. + factory Builder({Style style, String root}) { + if (style == null) { + if (io.Platform.operatingSystem == 'windows') { + style = Style.windows; + } else { + style = Style.posix; + } + } + + if (root == null) root = current; + + return new Builder._(style, root); + } + + Builder._(this.style, this.root); + + /// The style of path that this builder works with. + final Style style; + + /// The root directory that relative paths will be relative to. + final String root; + + /// Gets the path separator for the builder's [style]. On Mac and Linux, + /// this is `/`. On Windows, it's `\`. + String get separator => style.separator; + + /// Gets the part of [path] after the last separator on the builder's + /// platform. + /// + /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' + /// builder.basename('path/to'); // -> 'to' + String basename(String path) => _parse(path).basename; + + /// Gets the part of [path] after the last separator on the builder's + /// platform, and without any trailing file extension. + /// + /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + String basenameWithoutExtension(String path) => + _parse(path).basenameWithoutExtension; + + /// Gets the part of [path] before the last separator. + /// + /// builder.dirname('path/to/foo.dart'); // -> 'path/to' + /// builder.dirname('path/to'); // -> 'to' + String dirname(String path) { + var parsed = _parse(path); + if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; + if (!parsed.hasTrailingSeparator) { + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; + } + parsed.parts.removeLast(); + parsed.separators.removeLast(); + } + parsed.separators[parsed.separators.length - 1] = ''; + return parsed.toString(); + } + + /// Gets the file extension of [path]: the portion of [basename] from the last + /// `.` to the end (including the `.` itself). + /// + /// builder.extension('path/to/foo.dart'); // -> '.dart' + /// builder.extension('path/to/foo'); // -> '' + /// builder.extension('path.to/foo'); // -> '' + /// builder.extension('path/to/foo.dart.js'); // -> '.js' + /// + /// If the file name starts with a `.`, then it is not considered an + /// extension: + /// + /// builder.extension('~/.bashrc'); // -> '' + /// builder.extension('~/.notes.txt'); // -> '.txt' + String extension(String path) => _parse(path).extension; + + // 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 + /// relative. + /// + /// // Unix + /// builder.rootPrefix('path/to/foo'); // -> '' + /// builder.rootPrefix('/path/to/foo'); // -> '/' + /// + /// // Windows + /// builder.rootPrefix(r'path\to\foo'); // -> '' + /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + String rootPrefix(String path) { + var root = _parse(path).root; + return root == null ? '' : root; + } + + /// Returns `true` if [path] is an absolute path and `false` if it is a + /// relative path. On POSIX systems, absolute paths start with a `/` (forward + /// slash). On Windows, an absolute path starts with `\\`, or a drive letter + /// followed by `:/` or `:\`. + bool isAbsolute(String path) => _parse(path).isAbsolute; + + /// Returns `true` if [path] is a relative path and `false` if it is absolute. + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. + bool isRelative(String path) => !isAbsolute(path); + + /// Joins the given path parts into a single path. Example: + /// + /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// builder.join('path', '/to', 'foo'); // -> '/to/foo' + /// + String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) { + var buffer = new StringBuffer(); + var needsSeparator = false; + + var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + for (var i = 1; i < parts.length; i++) { + if (parts[i] != null && parts[i - 1] == null) { + throw new ArgumentError("join(): part ${i - 1} was null, but part $i " + "was not."); + } + } + + for (var part in parts) { + if (part == null) continue; + + if (this.isAbsolute(part)) { + // An absolute path discards everything before it. + buffer.clear(); + buffer.add(part); + } else { + if (part.length > 0 && style.separatorPattern.hasMatch(part[0])) { + // The part starts with a separator, so we don't need to add one. + } else if (needsSeparator) { + buffer.add(separator); + } + + buffer.add(part); + } + + // Unless this part ends with a separator, we'll need to add one before + // the next part. + needsSeparator = part.length > 0 && + !style.separatorPattern.hasMatch(part[part.length - 1]); + } + + return buffer.toString(); + } + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Splits [path] into its components using the current platform's + /// [separator]. Example: + /// + /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo'] + /// + /// The path will *not* be normalized before splitting. + /// + /// builder.split('path/../foo'); // -> ['path', '..', 'foo'] + /// + /// If [path] is absolute, the root directory will be the first element in the + /// array. Example: + /// + /// // Unix + /// builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + /// + /// // Windows + /// builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + List split(String path) { + var parsed = _parse(path); + // Filter out empty parts that exist due to multiple separators in a row. + parsed.parts = parsed.parts.filter((part) => part != ''); + if (parsed.root != null) parsed.parts.insertRange(0, 1, parsed.root); + return parsed.parts; + } + + /// Normalizes [path], simplifying it by handling `..`, and `.`, and + /// removing redundant path separators whenever possible. + /// + /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt' + String normalize(String path) { + if (path == '') return path; + + var parsed = _parse(path); + parsed.normalize(); + return parsed.toString(); + } + + /// Creates a new path by appending the given path parts to the [root]. + /// Equivalent to [join()] with [root] as the first argument. Example: + /// + /// var builder = new Builder(root: 'root'); + /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' + String resolve(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) { + if (!?part2) return join(root, part1); + if (!?part3) return join(root, part1, part2); + if (!?part4) return join(root, part1, part2, part3); + if (!?part5) return join(root, part1, part2, part3, part4); + if (!?part6) return join(root, part1, part2, part3, part4, part5); + if (!?part7) return join(root, part1, part2, part3, part4, part5, part6); + return join(root, part1, part2, part3, part4, part5, part6, part7); + } + + /// Attempts to convert [path] to an equivalent relative path relative to + /// [root]. + /// + /// var builder = new Builder(root: '/root/path'); + /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + /// builder.relative('/root/other.dart'); // -> '../other.dart' + /// + /// If the [from] argument is passed, [path] is made relative to that instead. + /// + /// builder.relative('/root/path/a/b.dart', + /// from: '/root/path'); // -> 'a/b.dart' + /// builder.relative('/root/other.dart', + /// from: '/root/path'); // -> '../other.dart' + /// + /// Since there is no relative path from one drive letter to another on + /// Windows, this will return an absolute path in that case. + /// + /// builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' + /// + /// This will also return an absolute path if an absolute [path] is passed to + /// a builder with a relative [root]. + /// + /// var builder = new Builder(r'some/relative/path'); + /// builder.relative(r'/absolute/path'); // -> '/absolute/path' + String relative(String path, {String from}) { + if (path == '') return '.'; + + from = from == null ? root : this.join(root, from); + + // We can't determine the path from a relative path to an absolute path. + if (this.isRelative(from) && this.isAbsolute(path)) { + return this.normalize(path); + } + + // If the given path is relative, resolve it relative to the root of the + // builder. + if (this.isRelative(path)) path = this.resolve(path); + + // If the path is still relative and `from` is absolute, we're unable to + // find a path from `from` to `path`. + if (this.isRelative(path) && this.isAbsolute(from)) { + throw new ArgumentError('Unable to find a path to "$path" from "$from".'); + } + + var fromParsed = _parse(from)..normalize(); + var pathParsed = _parse(path)..normalize(); + + // If the root prefixes don't match (for example, different drive letters + // on Windows), then there is no relative path, so just return the absolute + // one. + // TODO(rnystrom): Drive letters are case-insentive on Windows. Should + // handle "C:\" and "c:\" being the same root. + if (fromParsed.root != pathParsed.root) return pathParsed.toString(); + + // Strip off their common prefix. + while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { + fromParsed.parts.removeAt(0); + fromParsed.separators.removeAt(0); + pathParsed.parts.removeAt(0); + pathParsed.separators.removeAt(0); + } + + // If there are any directories left in the root path, we need to walk up + // out of them. + pathParsed.parts.insertRange(0, fromParsed.parts.length, '..'); + pathParsed.separators.insertRange(0, fromParsed.parts.length, + style.separator); + + // Corner case: the paths completely collapsed. + if (pathParsed.parts.length == 0) return '.'; + + // Make it relative. + pathParsed.root = ''; + pathParsed.removeTrailingSeparator(); + + return pathParsed.toString(); + } + + /// Removes a trailing extension from the last part of [path]. + /// + /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + String withoutExtension(String path) { + var parsed = _parse(path); + if (parsed.hasTrailingSeparator) return parsed.toString(); + + if (!parsed.parts.isEmpty) { + parsed.parts[parsed.parts.length - 1] = parsed.basenameWithoutExtension; + } + + return parsed.toString(); + } + + _ParsedPath _parse(String path) { + var before = path; + + // Remove the root prefix, if any. + var root = style.getRoot(path); + if (root != null) path = path.substring(root.length); + + // Split the parts on path separators. + var parts = []; + var separators = []; + var start = 0; + for (var match in style.separatorPattern.allMatches(path)) { + parts.add(path.substring(start, match.start)); + separators.add(match[0]); + start = match.end; + } + + // Add the final part, if any. + if (start < path.length) { + parts.add(path.substring(start)); + separators.add(''); + } + + return new _ParsedPath(style, root, parts, separators); + } +} + +/// An enum type describing a "flavor" of path. +class Style { + /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths + /// start with "/". Used by UNIX, Linux, Mac OS X, and others. + static final posix = new Style._('posix', '/', '/', '/'); + + /// Windows paths use "\" (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, "C:") or two backslashes + /// ("\\") for UNC paths. + // TODO(rnystrom): The UNC root prefix should include the drive name too, not + // just the "\\". + static final windows = new Style._('windows', '\\', r'[/\\]', + r'\\\\|[a-zA-Z]:[/\\]'); + + Style._(this.name, this.separator, String separatorPattern, + String rootPattern) + : separatorPattern = new RegExp(separatorPattern), + _rootPattern = new RegExp('^$rootPattern'); + + /// The name of this path style. Will be "posix" or "windows". + final String name; + + /// The path separator for this style. On POSIX, this is `/`. On Windows, + /// it's `\`. + final String separator; + + /// The [Pattern] that can be used to match a separator for a path in this + /// style. Windows allows both "/" and "\" as path separators even though + /// "\" is the canonical one. + final Pattern separatorPattern; + + /// The [Pattern] that can be used to match the root prefix of an absolute + /// path in this style. + final Pattern _rootPattern; + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path) { + var match = _rootPattern.firstMatch(path); + if (match == null) return null; + return match[0]; + } + + String toString() => name; +} + +// TODO(rnystrom): Make this public? +class _ParsedPath { + /// The [Style] that was used to parse this path. + Style style; + + /// The absolute root portion of the path, or `null` if the path is relative. + /// On POSIX systems, this will be `null` or "/". On Windows, it can be + /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive + /// letters. + String root; + + /// The path-separated parts of the path. All but the last will be + /// directories. + List parts; + + /// The path separators following each part. The last one will be an empty + /// string unless the path ends with a trailing separator. + List separators; + + /// The file extension of the last part, or "" if it doesn't have one. + String get extension => _splitExtension()[1]; + + /// `true` if the path ends with a trailing separator. + bool get hasTrailingSeparator { + if (separators.length == 0) return false; + return separators[separators.length - 1] != ''; + } + + /// `true` if this is an absolute path. + bool get isAbsolute => root != null; + + _ParsedPath(this.style, this.root, this.parts, this.separators); + + String get basename { + if (parts.length == 0) return extension; + if (hasTrailingSeparator) return ''; + return parts.last; + } + + String get basenameWithoutExtension => _splitExtension()[0]; + + void removeTrailingSeparator() { + if (separators.length > 0) { + separators[separators.length - 1] = ''; + } + } + + void normalize() { + // Handle '.', '..', and empty parts. + var leadingDoubles = 0; + var newParts = []; + for (var part in parts) { + if (part == '.' || part == '') { + // Do nothing. Ignore it. + } else if (part == '..') { + // Pop the last part off. + if (newParts.length > 0) { + newParts.removeLast(); + } else { + // Backed out past the beginning, so preserve the "..". + leadingDoubles++; + } + } else { + newParts.add(part); + } + } + + // A relative path can back out from the start directory. + if (!isAbsolute) { + newParts.insertRange(0, leadingDoubles, '..'); + } + + // If we collapsed down to nothing, do ".". + if (newParts.length == 0 && !isAbsolute) { + newParts.add('.'); + } + + // Canonicalize separators. + var newSeparators = []; + newSeparators.insertRange(0, newParts.length, style.separator); + + parts = newParts; + separators = newSeparators; + + removeTrailingSeparator(); + } + + String toString() { + var builder = new StringBuffer(); + if (root != null) builder.add(root); + for (var i = 0; i < parts.length; i++) { + builder.add(parts[i]); + builder.add(separators[i]); + } + + return builder.toString(); + } + + /// Splits the last part of the path into 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 _splitExtension() { + if (parts.isEmpty) return ['', '']; + if (hasTrailingSeparator) return ['', '']; + + var file = parts.last; + if (file == '..') return ['..', '']; + + var lastDot = file.lastIndexOf('.'); + + // If there is no dot, or it's the first character, like '.bashrc', it + // doesn't count. + if (lastDot <= 0) return [file, '']; + + return [file.substring(0, lastDot), file.substring(lastDot)]; + } +} diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml new file mode 100644 index 00000000..3372302d --- /dev/null +++ b/pkgs/path/pubspec.yaml @@ -0,0 +1,10 @@ +name: pathos +author: "Dart Team " +homepage: http://www.dartlang.org +description: > + A string-based path manipulation library. All of the path operations you know + and love, with solid support on both Windows and POSIX (Linux and Mac OS X) + machines. + + Currently only runs on the standalone VM, but will run in a browser as soon as + configuration-specific code is supported by Dart. diff --git a/pkgs/path/test/path_posix_test.dart b/pkgs/path/test/path_posix_test.dart new file mode 100644 index 00000000..f929c2a5 --- /dev/null +++ b/pkgs/path/test/path_posix_test.dart @@ -0,0 +1,362 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path_test; + +import 'dart:io' as io; + +// TODO(rnystrom): Use "package:" path when #7491 is fixed. +import '../../unittest/lib/unittest.dart'; +import '../lib/path.dart' as path; + +main() { + var builder = new path.Builder(style: path.Style.posix, root: '/root/path'); + + if (new path.Builder().style == path.Style.posix) { + group('absolute', () { + expect(path.absolute('a/b.txt'), path.join(path.current, 'a/b.txt')); + expect(path.absolute('/a/b.txt'), '/a/b.txt'); + }); + } + + test('separator', () { + expect(builder.separator, '/'); + }); + + test('extension', () { + expect(builder.extension(''), ''); + expect(builder.extension('foo.dart'), '.dart'); + expect(builder.extension('foo.dart.js'), '.js'); + expect(builder.extension('a.b/c'), ''); + expect(builder.extension('a.b/c.d'), '.d'); + expect(builder.extension('~/.bashrc'), ''); + expect(builder.extension(r'a.b\c'), r'.b\c'); + }); + + test('rootPrefix', () { + expect(builder.rootPrefix(''), ''); + expect(builder.rootPrefix('a'), ''); + expect(builder.rootPrefix('a/b'), ''); + expect(builder.rootPrefix('/a/c'), '/'); + expect(builder.rootPrefix('/'), '/'); + }); + + test('dirname', () { + expect(builder.dirname(''), '.'); + expect(builder.dirname('a'), '.'); + expect(builder.dirname('a/b'), 'a'); + expect(builder.dirname('a/b/c'), 'a/b'); + expect(builder.dirname('a/b.c'), 'a'); + expect(builder.dirname('a/'), 'a'); + expect(builder.dirname('a/.'), 'a'); + expect(builder.dirname(r'a\b/c'), r'a\b'); + expect(builder.dirname('/a'), '/'); + expect(builder.dirname('/'), '/'); + expect(builder.dirname('a/b/'), 'a/b'); + expect(builder.dirname(r'a/b\c'), 'a'); + expect(builder.dirname('a//'), 'a/'); + }); + + test('basename', () { + expect(builder.basename(''), ''); + expect(builder.basename('a'), 'a'); + expect(builder.basename('a/b'), 'b'); + expect(builder.basename('a/b/c'), 'c'); + expect(builder.basename('a/b.c'), 'b.c'); + expect(builder.basename('a/'), ''); + expect(builder.basename('a/.'), '.'); + expect(builder.basename(r'a\b/c'), 'c'); + expect(builder.basename('/a'), 'a'); + // TODO(nweiz): this should actually return '/' + expect(builder.basename('/'), ''); + expect(builder.basename('a/b/'), ''); + expect(builder.basename(r'a/b\c'), r'b\c'); + expect(builder.basename('a//'), ''); + }); + + test('basenameWithoutExtension', () { + expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('a'), 'a'); + expect(builder.basenameWithoutExtension('a/b'), 'b'); + expect(builder.basenameWithoutExtension('a/b/c'), 'c'); + expect(builder.basenameWithoutExtension('a/b.c'), 'b'); + expect(builder.basenameWithoutExtension('a/'), ''); + expect(builder.basenameWithoutExtension('a/.'), '.'); + expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + }); + + test('isAbsolute', () { + expect(builder.isAbsolute(''), false); + expect(builder.isAbsolute('a'), false); + expect(builder.isAbsolute('a/b'), false); + expect(builder.isAbsolute('/a'), true); + expect(builder.isAbsolute('/a/b'), true); + expect(builder.isAbsolute('~'), false); + expect(builder.isAbsolute('.'), false); + expect(builder.isAbsolute('../a'), false); + expect(builder.isAbsolute('C:/a'), false); + expect(builder.isAbsolute(r'C:\a'), false); + expect(builder.isAbsolute(r'\\a'), false); + }); + + test('isRelative', () { + expect(builder.isRelative(''), true); + expect(builder.isRelative('a'), true); + expect(builder.isRelative('a/b'), true); + expect(builder.isRelative('/a'), false); + expect(builder.isRelative('/a/b'), false); + expect(builder.isRelative('~'), true); + expect(builder.isRelative('.'), true); + expect(builder.isRelative('../a'), true); + expect(builder.isRelative('C:/a'), true); + expect(builder.isRelative(r'C:\a'), true); + expect(builder.isRelative(r'\\a'), true); + }); + + group('join', () { + test('allows up to eight parts', () { + expect(builder.join('a'), 'a'); + expect(builder.join('a', 'b'), 'a/b'); + expect(builder.join('a', 'b', 'c'), 'a/b/c'); + expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + 'a/b/c/d/e/f/g/h'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(builder.join('a\\', 'b'), r'a\/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.join('a', '/', 'b', 'c'), '/b/c'); + expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); + expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + }); + + test('ignores trailing nulls', () { + expect(builder.join('a', null), equals('a')); + expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + }); + + test('disallows intermediate nulls', () { + expect(() => builder.join('a', null, 'b'), throwsArgumentError); + expect(() => builder.join(null, 'a'), throwsArgumentError); + }); + }); + + group('split', () { + test('simple cases', () { + expect(builder.split(''), []); + expect(builder.split('.'), ['.']); + expect(builder.split('..'), ['..']); + expect(builder.split('foo'), equals(['foo'])); + expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split('foo/../bar/./baz'), + equals(['foo', '..', 'bar', '.', 'baz'])); + expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(builder.split('.'), equals(['.'])); + expect(builder.split(''), equals([])); + expect(builder.split('foo/'), equals(['foo'])); + expect(builder.split('//'), equals(['/'])); + }); + + test('includes the root for absolute paths', () { + expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(builder.split('/'), equals(['/'])); + }); + }); + + group('normalize', () { + test('simple cases', () { + expect(builder.normalize(''), ''); + expect(builder.normalize('.'), '.'); + expect(builder.normalize('..'), '..'); + expect(builder.normalize('a'), 'a'); + expect(builder.normalize('/'), '/'); + expect(builder.normalize(r'\'), r'\'); + }); + + test('collapses redundant separators', () { + expect(builder.normalize(r'a/b/c'), r'a/b/c'); + expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + }); + + test('does not collapse separators for other platform', () { + expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + }); + + test('eliminates "." parts', () { + expect(builder.normalize('./'), '.'); + expect(builder.normalize('/.'), '/'); + expect(builder.normalize('/./'), '/'); + expect(builder.normalize('./.'), '.'); + expect(builder.normalize('a/./b'), 'a/b'); + expect(builder.normalize('a/.b/c'), 'a/.b/c'); + expect(builder.normalize('a/././b/./c'), 'a/b/c'); + expect(builder.normalize('././a'), 'a'); + expect(builder.normalize('a/./.'), 'a'); + }); + + test('eliminates ".." parts', () { + expect(builder.normalize('..'), '..'); + expect(builder.normalize('../'), '..'); + expect(builder.normalize('../../..'), '../../..'); + expect(builder.normalize('../../../'), '../../..'); + expect(builder.normalize('/..'), '/'); + expect(builder.normalize('/../../..'), '/'); + expect(builder.normalize('/../../../a'), '/a'); + expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('a/../b'), 'b'); + expect(builder.normalize('a/./../b'), 'b'); + expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(builder.normalize('a/b/../../../../c'), '../../c'); + }); + + test('does not walk before root on absolute paths', () { + expect(builder.normalize('..'), '..'); + expect(builder.normalize('../'), '..'); + expect(builder.normalize('/..'), '/'); + expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('a/../b'), 'b'); + expect(builder.normalize('a/./../b'), 'b'); + expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(builder.normalize('a/b/../../../../c'), '../../c'); + }); + + test('removes trailing separators', () { + expect(builder.normalize('./'), '.'); + expect(builder.normalize('.//'), '.'); + expect(builder.normalize('a/'), 'a'); + expect(builder.normalize('a/b/'), 'a/b'); + expect(builder.normalize('a/b///'), 'a/b'); + }); + }); + + group('relative', () { + group('from absolute root', () { + test('given absolute path in root', () { + expect(builder.relative('/'), '../..'); + expect(builder.relative('/root'), '..'); + expect(builder.relative('/root/path'), '.'); + expect(builder.relative('/root/path/a'), 'a'); + expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + }); + + test('given absolute path outside of root', () { + expect(builder.relative('/a/b'), '../../a/b'); + expect(builder.relative('/root/path/a'), 'a'); + expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(builder.relative(''), '.'); + expect(builder.relative('.'), '.'); + expect(builder.relative('a'), 'a'); + expect(builder.relative('a/b.txt'), 'a/b.txt'); + expect(builder.relative('../a/b.txt'), '../a/b.txt'); + expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + }); + }); + + group('from relative root', () { + var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); + + test('given absolute path', () { + expect(r.relative('/'), equals('/')); + expect(r.relative('/a/b'), equals('/a/b')); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(r.relative(''), '.'); + expect(r.relative('.'), '.'); + expect(r.relative('..'), '..'); + expect(r.relative('a'), 'a'); + expect(r.relative('a/b.txt'), 'a/b.txt'); + expect(r.relative('../a/b.txt'), '../a/b.txt'); + expect(r.relative('a/./b/../c.txt'), 'a/c.txt'); + }); + }); + + test('from a root with extension', () { + var r = new path.Builder(style: path.Style.posix, root: '/dir.ext'); + expect(r.relative('/dir.ext/file'), 'file'); + }); + + test('with a root parameter', () { + expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); + expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + equals('../../../../foo/bar/baz')); + expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + }); + + test('with a root parameter and a relative root', () { + var r = new path.Builder(style: path.Style.posix, root: 'relative/root'); + expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(() => r.relative('..', from: '/foo/bar'), throwsArgumentError); + expect(r.relative('/foo/bar/baz', from: 'foo/bar'), + equals('/foo/bar/baz')); + expect(r.relative('..', from: 'foo/bar'), equals('../../..')); + }); + }); + + group('resolve', () { + test('allows up to seven parts', () { + expect(builder.resolve('a'), '/root/path/a'); + expect(builder.resolve('a', 'b'), '/root/path/a/b'); + expect(builder.resolve('a', 'b', 'c'), '/root/path/a/b/c'); + expect(builder.resolve('a', 'b', 'c', 'd'), '/root/path/a/b/c/d'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + '/root/path/a/b/c/d/e/f'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + '/root/path/a/b/c/d/e/f/g'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.resolve('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d'); + expect(builder.resolve(r'a\', 'b'), r'/root/path/a\/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.resolve('a', '/b', '/c', 'd'), '/c/d'); + expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d'); + expect(builder.resolve('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); + }); + }); + + test('withoutExtension', () { + expect(builder.withoutExtension(''), ''); + expect(builder.withoutExtension('a'), 'a'); + expect(builder.withoutExtension('.a'), '.a'); + expect(builder.withoutExtension('a.b'), 'a'); + expect(builder.withoutExtension('a/b.c'), 'a/b'); + expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(builder.withoutExtension('a/'), 'a/'); + expect(builder.withoutExtension('a/b/'), 'a/b/'); + expect(builder.withoutExtension('a/.'), 'a/.'); + expect(builder.withoutExtension('a/.b'), 'a/.b'); + expect(builder.withoutExtension('a.b/c'), 'a.b/c'); + expect(builder.withoutExtension(r'a.b\c'), r'a'); + expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); + }); +} diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart new file mode 100644 index 00000000..bd099ef2 --- /dev/null +++ b/pkgs/path/test/path_test.dart @@ -0,0 +1,60 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library all_test; + +import 'dart:io' as io; + +// TODO(rnystrom): Use "package:" path when #7491 is fixed. +import '../../unittest/lib/unittest.dart'; +import '../lib/path.dart' as path; + +main() { + group('path.Style', () { + test('name', () { + expect(path.Style.posix.name, 'posix'); + expect(path.Style.windows.name, 'windows'); + }); + + test('separator', () { + expect(path.Style.posix.separator, '/'); + expect(path.Style.windows.separator, '\\'); + }); + + test('toString()', () { + expect(path.Style.posix.toString(), 'posix'); + expect(path.Style.windows.toString(), 'windows'); + }); + }); + + group('new Builder()', () { + test('uses the given root directory', () { + var builder = new path.Builder(root: '/a/b/c'); + expect(builder.root, '/a/b/c'); + }); + + test('uses the given style', () { + var builder = new path.Builder(style: path.Style.windows); + expect(builder.style, path.Style.windows); + }); + + test('uses the current working directory if root is omitted', () { + var builder = new path.Builder(); + expect(builder.root, new io.Directory.current().path); + }); + + test('uses the host OS if style is omitted', () { + var builder = new path.Builder(); + if (io.Platform.operatingSystem == 'windows') { + expect(builder.style, path.Style.windows); + } else { + expect(builder.style, path.Style.posix); + } + }); + }); + + test('current', () { + expect(path.current, new io.Directory.current().path); + }); +} diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/path_windows_test.dart new file mode 100644 index 00000000..a94d126b --- /dev/null +++ b/pkgs/path/test/path_windows_test.dart @@ -0,0 +1,396 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path_test; + +import 'dart:io' as io; + +// TODO(rnystrom): Use "package:" path when #7491 is fixed. +import '../../unittest/lib/unittest.dart'; +import '../lib/path.dart' as path; + +main() { + var builder = new path.Builder(style: path.Style.windows, + root: r'C:\root\path'); + + if (new path.Builder().style == path.Style.windows) { + group('absolute', () { + expect(path.absolute(r'a\b.txt'), path.join(path.current, r'a\b.txt')); + expect(path.absolute(r'C:\a\b.txt'), r'C:\a\b.txt'); + expect(path.absolute(r'\\a\b.txt'), r'\\a\b.txt'); + }); + } + + group('separator', () { + expect(builder.separator, '\\'); + }); + + test('extension', () { + expect(builder.extension(''), ''); + expect(builder.extension('foo.dart'), '.dart'); + expect(builder.extension('foo.dart.js'), '.js'); + expect(builder.extension(r'a.b\c'), ''); + expect(builder.extension('a.b/c.d'), '.d'); + expect(builder.extension(r'~\.bashrc'), ''); + expect(builder.extension(r'a.b/c'), r''); + }); + + test('rootPrefix', () { + expect(builder.rootPrefix(''), ''); + expect(builder.rootPrefix('a'), ''); + expect(builder.rootPrefix(r'a\b'), ''); + expect(builder.rootPrefix(r'C:\a\c'), r'C:\'); + expect(builder.rootPrefix('C:\\'), r'C:\'); + expect(builder.rootPrefix('C:/'), 'C:/'); + + // TODO(nweiz): enable this once issue 7323 is fixed. + // expect(builder.rootPrefix(r'\\server\a\b'), r'\\server\'); + }); + + test('dirname', () { + expect(builder.dirname(r''), '.'); + expect(builder.dirname(r'a'), '.'); + expect(builder.dirname(r'a\b'), 'a'); + expect(builder.dirname(r'a\b\c'), r'a\b'); + expect(builder.dirname(r'a\b.c'), 'a'); + expect(builder.dirname(r'a\'), 'a'); + expect(builder.dirname('a/'), 'a'); + expect(builder.dirname(r'a\.'), 'a'); + expect(builder.dirname(r'a\b/c'), r'a\b'); + expect(builder.dirname(r'C:\a'), r'C:\'); + expect(builder.dirname('C:\\'), r'C:\'); + expect(builder.dirname(r'a\b\'), r'a\b'); + expect(builder.dirname(r'a/b\c'), 'a/b'); + expect(builder.dirname(r'a\\'), r'a\'); + }); + + test('basename', () { + expect(builder.basename(r''), ''); + expect(builder.basename(r'a'), 'a'); + expect(builder.basename(r'a\b'), 'b'); + expect(builder.basename(r'a\b\c'), 'c'); + expect(builder.basename(r'a\b.c'), 'b.c'); + expect(builder.basename(r'a\'), ''); + expect(builder.basename(r'a/'), ''); + expect(builder.basename(r'a\.'), '.'); + expect(builder.basename(r'a\b/c'), r'c'); + expect(builder.basename(r'C:\a'), 'a'); + // TODO(nweiz): this should actually return 'C:\' + expect(builder.basename(r'C:\'), ''); + expect(builder.basename(r'a\b\'), ''); + expect(builder.basename(r'a/b\c'), 'c'); + expect(builder.basename(r'a\\'), ''); + }); + + test('basenameWithoutExtension', () { + expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('a'), 'a'); + expect(builder.basenameWithoutExtension(r'a\b'), 'b'); + expect(builder.basenameWithoutExtension(r'a\b\c'), 'c'); + expect(builder.basenameWithoutExtension(r'a\b.c'), 'b'); + expect(builder.basenameWithoutExtension(r'a\'), ''); + expect(builder.basenameWithoutExtension(r'a\.'), '.'); + expect(builder.basenameWithoutExtension(r'a\b/c'), r'c'); + expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); + expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); + }); + + test('isAbsolute', () { + expect(builder.isAbsolute(''), false); + expect(builder.isAbsolute('a'), false); + expect(builder.isAbsolute(r'a\b'), false); + expect(builder.isAbsolute(r'\a'), false); + expect(builder.isAbsolute(r'\a\b'), false); + expect(builder.isAbsolute('~'), false); + expect(builder.isAbsolute('.'), false); + expect(builder.isAbsolute(r'..\a'), false); + expect(builder.isAbsolute(r'a:/a\b'), true); + expect(builder.isAbsolute(r'D:/a/b'), true); + expect(builder.isAbsolute(r'c:\'), true); + expect(builder.isAbsolute(r'B:\'), true); + expect(builder.isAbsolute(r'c:\a'), true); + expect(builder.isAbsolute(r'C:\a'), true); + expect(builder.isAbsolute(r'\\a'), true); + expect(builder.isAbsolute(r'\\'), true); + }); + + test('isRelative', () { + expect(builder.isRelative(''), true); + expect(builder.isRelative('a'), true); + expect(builder.isRelative(r'a\b'), true); + expect(builder.isRelative(r'\a'), true); + expect(builder.isRelative(r'\a\b'), true); + expect(builder.isRelative('~'), true); + expect(builder.isRelative('.'), true); + expect(builder.isRelative(r'..\a'), true); + expect(builder.isRelative(r'a:/a\b'), false); + expect(builder.isRelative(r'D:/a/b'), false); + expect(builder.isRelative(r'c:\'), false); + expect(builder.isRelative(r'B:\'), false); + expect(builder.isRelative(r'c:\a'), false); + expect(builder.isRelative(r'C:\a'), false); + expect(builder.isRelative(r'\\a'), false); + expect(builder.isRelative(r'\\'), false); + }); + + group('join', () { + test('allows up to eight parts', () { + expect(builder.join('a'), 'a'); + expect(builder.join('a', 'b'), r'a\b'); + expect(builder.join('a', 'b', 'c'), r'a\b\c'); + expect(builder.join('a', 'b', 'c', 'd'), r'a\b\c\d'); + expect(builder.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + r'a\b\c\d\e\f\g\h'); + }); + + test('does not add separator if a part ends or begins in one', () { + expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); + expect(builder.join('a/', 'b'), r'a/b'); + expect(builder.join('a', '/b'), 'a/b'); + expect(builder.join('a', r'\b'), r'a\b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.join('a', '/b', '/c', 'd'), r'a/b/c\d'); + expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(builder.join('a', r'\\b', r'\\c', 'd'), r'\\c\d'); + }); + + test('ignores trailing nulls', () { + expect(builder.join('a', null), equals('a')); + expect(builder.join('a', 'b', 'c', null, null), equals(r'a\b\c')); + }); + + test('disallows intermediate nulls', () { + expect(() => builder.join('a', null, 'b'), throwsArgumentError); + expect(() => builder.join(null, 'a'), throwsArgumentError); + }); + }); + + group('split', () { + test('simple cases', () { + expect(builder.split(''), []); + expect(builder.split('.'), ['.']); + expect(builder.split('..'), ['..']); + expect(builder.split('foo'), equals(['foo'])); + expect(builder.split(r'foo\bar.txt'), equals(['foo', 'bar.txt'])); + expect(builder.split(r'foo\bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split(r'foo\..\bar\.\baz'), + equals(['foo', '..', 'bar', '.', 'baz'])); + expect(builder.split(r'foo\\bar\\\baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split(r'foo\/\baz'), equals(['foo', 'baz'])); + expect(builder.split('.'), equals(['.'])); + expect(builder.split(''), equals([])); + expect(builder.split('foo/'), equals(['foo'])); + expect(builder.split(r'C:\'), equals([r'C:\'])); + }); + + test('includes the root for absolute paths', () { + expect(builder.split(r'C:\foo\bar\baz'), + equals([r'C:\', 'foo', 'bar', 'baz'])); + expect(builder.split(r'C:\\'), equals([r'C:\'])); + + // TODO(nweiz): enable these once issue 7323 is fixed. + // expect(builder.split(r'\\server\foo\bar\baz'), + // equals([r'\\server\', 'foo', 'bar', 'baz'])); + // expect(builder.split(r'\\server\'), equals([r'\\server\'])); + }); + }); + + group('normalize', () { + test('simple cases', () { + expect(builder.normalize(''), ''); + expect(builder.normalize('.'), '.'); + expect(builder.normalize('..'), '..'); + expect(builder.normalize('a'), 'a'); + expect(builder.normalize('C:/'), r'C:/'); + expect(builder.normalize(r'C:\'), r'C:\'); + expect(builder.normalize(r'\\'), r'\\'); + }); + + test('collapses redundant separators', () { + expect(builder.normalize(r'a\b\c'), r'a\b\c'); + expect(builder.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d'); + }); + + test('eliminates "." parts', () { + expect(builder.normalize(r'.\'), '.'); + expect(builder.normalize(r'c:\.'), r'c:\'); + expect(builder.normalize(r'B:\.\'), r'B:\'); + expect(builder.normalize(r'\\.'), r'\\'); + expect(builder.normalize(r'\\.\'), r'\\'); + expect(builder.normalize(r'.\.'), '.'); + expect(builder.normalize(r'a\.\b'), r'a\b'); + expect(builder.normalize(r'a\.b\c'), r'a\.b\c'); + expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c'); + expect(builder.normalize(r'.\./a'), 'a'); + expect(builder.normalize(r'a/.\.'), 'a'); + }); + + test('eliminates ".." parts', () { + expect(builder.normalize('..'), '..'); + expect(builder.normalize(r'..\'), '..'); + expect(builder.normalize(r'..\..\..'), r'..\..\..'); + expect(builder.normalize(r'../..\..\'), r'..\..\..'); + // TODO(rnystrom): Is this how Python handles absolute paths on Windows? + expect(builder.normalize(r'\\..'), r'\\'); + expect(builder.normalize(r'\\..\..\..'), r'\\'); + expect(builder.normalize(r'\\..\../..\a'), r'\\a'); + expect(builder.normalize(r'c:\..'), r'c:\'); + expect(builder.normalize(r'A:/..\..\..'), r'A:/'); + expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); + expect(builder.normalize(r'a\..'), '.'); + expect(builder.normalize(r'a\b\..'), 'a'); + expect(builder.normalize(r'a\..\b'), 'b'); + expect(builder.normalize(r'a\.\..\b'), 'b'); + expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); + expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); + }); + + test('removes trailing separators', () { + expect(builder.normalize(r'.\'), '.'); + expect(builder.normalize(r'.\\'), '.'); + expect(builder.normalize(r'a/'), 'a'); + expect(builder.normalize(r'a\b\'), r'a\b'); + expect(builder.normalize(r'a\b\\\'), r'a\b'); + }); + + test('normalizes separators', () { + expect(builder.normalize(r'a/b\c'), r'a\b\c'); + }); + }); + + group('relative', () { + group('from absolute root', () { + test('given absolute path in root', () { + expect(builder.relative(r'C:\'), r'..\..'); + expect(builder.relative(r'C:\root'), '..'); + expect(builder.relative(r'C:\root\path'), '.'); + expect(builder.relative(r'C:\root\path\a'), 'a'); + expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + }); + + test('given absolute path outside of root', () { + expect(builder.relative(r'C:\a\b'), r'..\..\a\b'); + expect(builder.relative(r'C:\root\path\a'), 'a'); + expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + }); + + test('given absolute path on different drive', () { + expect(builder.relative(r'D:\a\b'), r'D:\a\b'); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(builder.relative(''), '.'); + expect(builder.relative('.'), '.'); + expect(builder.relative('a'), 'a'); + expect(builder.relative(r'a\b.txt'), r'a\b.txt'); + expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt'); + expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); + }); + }); + + group('from relative root', () { + var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); + + test('given absolute path', () { + expect(r.relative(r'C:\'), equals(r'C:\')); + expect(r.relative(r'C:\a\b'), equals(r'C:\a\b')); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(r.relative(''), '.'); + expect(r.relative('.'), '.'); + expect(r.relative('..'), '..'); + expect(r.relative('a'), 'a'); + expect(r.relative(r'a\b.txt'), r'a\b.txt'); + expect(r.relative(r'..\a/b.txt'), r'..\a\b.txt'); + expect(r.relative(r'a\./b\../c.txt'), r'a\c.txt'); + }); + }); + + test('from a root with extension', () { + var r = new path.Builder(style: path.Style.windows, root: r'C:\dir.ext'); + expect(r.relative(r'C:\dir.ext\file'), 'file'); + }); + + test('with a root parameter', () { + expect(builder.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), + equals('baz')); + expect(builder.relative('..', from: r'C:\foo\bar'), + equals(r'..\..\root')); + expect(builder.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); + expect(builder.relative(r'C:\foo\bar\baz', from: r'foo\bar'), + equals(r'..\..\..\..\foo\bar\baz')); + expect(builder.relative('..', from: r'foo\bar'), equals(r'..\..\..')); + }); + + test('with a root parameter and a relative root', () { + var r = new path.Builder(style: path.Style.windows, root: r'relative\root'); + expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); + expect(() => r.relative('..', from: r'C:\foo\bar'), throwsArgumentError); + expect(r.relative(r'C:\foo\bar\baz', from: r'foo\bar'), + equals(r'C:\foo\bar\baz')); + expect(r.relative('..', from: r'foo\bar'), equals(r'..\..\..')); + }); + + test('given absolute with different root prefix', () { + expect(builder.relative(r'D:\a\b'), r'D:\a\b'); + expect(builder.relative(r'\\a\b'), r'\\a\b'); + }); + }); + + group('resolve', () { + test('allows up to seven parts', () { + expect(builder.resolve('a'), r'C:\root\path\a'); + expect(builder.resolve('a', 'b'), r'C:\root\path\a\b'); + expect(builder.resolve('a', 'b', 'c'), r'C:\root\path\a\b\c'); + expect(builder.resolve('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + r'C:\root\path\a\b\c\d\e'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + r'C:\root\path\a\b\c\d\e\f'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + r'C:\root\path\a\b\c\d\e\f\g'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.resolve(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d'); + expect(builder.resolve('a/', 'b'), r'C:\root\path\a/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\root\path\a/b/c\d'); + expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(builder.resolve('a', r'\\b', r'\\c', 'd'), r'\\c\d'); + }); + }); + + test('withoutExtension', () { + expect(builder.withoutExtension(''), ''); + expect(builder.withoutExtension('a'), 'a'); + expect(builder.withoutExtension('.a'), '.a'); + expect(builder.withoutExtension('a.b'), 'a'); + expect(builder.withoutExtension(r'a\b.c'), r'a\b'); + expect(builder.withoutExtension(r'a\b.c.d'), r'a\b.c'); + expect(builder.withoutExtension(r'a\'), r'a\'); + expect(builder.withoutExtension(r'a\b\'), r'a\b\'); + expect(builder.withoutExtension(r'a\.'), r'a\.'); + expect(builder.withoutExtension(r'a\.b'), r'a\.b'); + expect(builder.withoutExtension(r'a.b\c'), r'a.b\c'); + expect(builder.withoutExtension(r'a/b.c/d'), r'a/b.c/d'); + expect(builder.withoutExtension(r'a\b/c'), r'a\b/c'); + expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c'); + expect(builder.withoutExtension(r'a.b/c'), r'a.b/c'); + }); +} From 56c6d80f80f91917e4361108906d8e5cf23c70ad Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 19 Dec 2012 01:08:42 +0000 Subject: [PATCH 002/183] Fix analyzer errors/warnings for Pub. Review URL: https://codereview.chromium.org//11609005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16299 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 593396d0..388c0841 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -294,7 +294,7 @@ class Builder { buffer.clear(); buffer.add(part); } else { - if (part.length > 0 && style.separatorPattern.hasMatch(part[0])) { + if (part.length > 0 && part[0].contains(style.separatorPattern)) { // The part starts with a separator, so we don't need to add one. } else if (needsSeparator) { buffer.add(separator); @@ -306,7 +306,7 @@ class Builder { // Unless this part ends with a separator, we'll need to add one before // the next part. needsSeparator = part.length > 0 && - !style.separatorPattern.hasMatch(part[part.length - 1]); + !part[part.length - 1].contains(style.separatorPattern); } return buffer.toString(); @@ -517,9 +517,10 @@ class Style { /// "\" is the canonical one. final Pattern separatorPattern; - /// The [Pattern] that can be used to match the root prefix of an absolute + // TODO(nweiz): make this a Pattern when issue 7080 is fixed. + /// The [RegExp] that can be used to match the root prefix of an absolute /// path in this style. - final Pattern _rootPattern; + final RegExp _rootPattern; /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. From 4aa68cda807d8f73cb4be1aa4da075ad3efdc925 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 20 Dec 2012 20:55:00 +0000 Subject: [PATCH 003/183] Make dirname and basename ignore trailing separators and double slashes. Separators are still preserved for other path methods. Review URL: https://codereview.chromium.org//11638021 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16397 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 85 ++++++++++++++++++--------- pkgs/path/test/path_posix_test.dart | 30 +++++++--- pkgs/path/test/path_windows_test.dart | 35 +++++++---- 3 files changed, 101 insertions(+), 49 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 388c0841..99eee7be 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -28,12 +28,20 @@ String absolute(String path) => join(current, path); /// /// path.basename('path/to/foo.dart'); // -> 'foo.dart' /// path.basename('path/to'); // -> 'to' +/// +/// Trailing separators are ignored. +/// +/// builder.dirname('path/to/'); // -> 'to' String basename(String path) => _builder.basename(path); /// Gets the part of [path] after the last separator, and without any trailing /// file extension. /// /// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' +/// +/// Trailing separators are ignored. +/// +/// builder.dirname('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => _builder.basenameWithoutExtension(path); @@ -41,6 +49,10 @@ String basenameWithoutExtension(String path) => /// /// path.dirname('path/to/foo.dart'); // -> 'path/to' /// path.dirname('path/to'); // -> 'to' +/// +/// Trailing separators are ignored. +/// +/// builder.dirname('path/to/'); // -> 'path' String dirname(String path) => _builder.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last @@ -190,30 +202,41 @@ class Builder { /// /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' /// builder.basename('path/to'); // -> 'to' + /// + /// Trailing separators are ignored. + /// + /// builder.dirname('path/to/'); // -> 'to' String basename(String path) => _parse(path).basename; /// Gets the part of [path] after the last separator on the builder's /// platform, and without any trailing file extension. /// /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + /// + /// Trailing separators are ignored. + /// + /// builder.dirname('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => - _parse(path).basenameWithoutExtension; + _parse(path).basenameWithoutExtension; /// Gets the part of [path] before the last separator. /// /// builder.dirname('path/to/foo.dart'); // -> 'path/to' - /// builder.dirname('path/to'); // -> 'to' + /// builder.dirname('path/to'); // -> 'path' + /// + /// Trailing separators are ignored. + /// + /// builder.dirname('path/to/'); // -> 'path' String dirname(String path) { var parsed = _parse(path); + parsed.removeTrailingSeparators(); if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; - if (!parsed.hasTrailingSeparator) { - if (parsed.parts.length == 1) { - return parsed.root == null ? '.' : parsed.root; - } - parsed.parts.removeLast(); - parsed.separators.removeLast(); + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; } - parsed.separators[parsed.separators.length - 1] = ''; + parsed.parts.removeLast(); + parsed.separators.removeLast(); + parsed.removeTrailingSeparators(); return parsed.toString(); } @@ -440,7 +463,7 @@ class Builder { // Make it relative. pathParsed.root = ''; - pathParsed.removeTrailingSeparator(); + pathParsed.removeTrailingSeparators(); return pathParsed.toString(); } @@ -450,10 +473,12 @@ class Builder { /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) { var parsed = _parse(path); - if (parsed.hasTrailingSeparator) return parsed.toString(); - if (!parsed.parts.isEmpty) { - parsed.parts[parsed.parts.length - 1] = parsed.basenameWithoutExtension; + for (var i = parsed.parts.length - 1; i >= 0; i--) { + if (!parsed.parts[i].isEmpty) { + parsed.parts[i] = parsed.basenameWithoutExtension; + break; + } } return parsed.toString(); @@ -555,29 +580,31 @@ class _ParsedPath { /// The file extension of the last part, or "" if it doesn't have one. String get extension => _splitExtension()[1]; - /// `true` if the path ends with a trailing separator. - bool get hasTrailingSeparator { - if (separators.length == 0) return false; - return separators[separators.length - 1] != ''; - } - /// `true` if this is an absolute path. bool get isAbsolute => root != null; _ParsedPath(this.style, this.root, this.parts, this.separators); String get basename { - if (parts.length == 0) return extension; - if (hasTrailingSeparator) return ''; - return parts.last; + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy.parts.last; } - String get basenameWithoutExtension => _splitExtension()[0]; + String get basenameWithoutExtension { + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy._splitExtension()[0]; + } - void removeTrailingSeparator() { - if (separators.length > 0) { - separators[separators.length - 1] = ''; + void removeTrailingSeparators() { + while (!parts.isEmpty && parts.last == '') { + parts.removeLast(); + separators.removeLast(); } + if (separators.length > 0) separators[separators.length - 1] = ''; } void normalize() { @@ -617,7 +644,7 @@ class _ParsedPath { parts = newParts; separators = newSeparators; - removeTrailingSeparator(); + removeTrailingSeparators(); } String toString() { @@ -636,7 +663,6 @@ class _ParsedPath { /// or "" if it has none. List _splitExtension() { if (parts.isEmpty) return ['', '']; - if (hasTrailingSeparator) return ['', '']; var file = parts.last; if (file == '..') return ['..', '']; @@ -649,4 +675,7 @@ class _ParsedPath { return [file.substring(0, lastDot), file.substring(lastDot)]; } + + _ParsedPath clone() => new _ParsedPath( + style, root, new List.from(parts), new List.from(separators)); } diff --git a/pkgs/path/test/path_posix_test.dart b/pkgs/path/test/path_posix_test.dart index f929c2a5..b917e93a 100644 --- a/pkgs/path/test/path_posix_test.dart +++ b/pkgs/path/test/path_posix_test.dart @@ -48,14 +48,18 @@ main() { expect(builder.dirname('a/b'), 'a'); expect(builder.dirname('a/b/c'), 'a/b'); expect(builder.dirname('a/b.c'), 'a'); - expect(builder.dirname('a/'), 'a'); + expect(builder.dirname('a/'), '.'); expect(builder.dirname('a/.'), 'a'); expect(builder.dirname(r'a\b/c'), r'a\b'); expect(builder.dirname('/a'), '/'); + expect(builder.dirname('///a'), '/'); expect(builder.dirname('/'), '/'); - expect(builder.dirname('a/b/'), 'a/b'); + expect(builder.dirname('///'), '/'); + expect(builder.dirname('a/b/'), 'a'); expect(builder.dirname(r'a/b\c'), 'a'); - expect(builder.dirname('a//'), 'a/'); + expect(builder.dirname('a//'), '.'); + expect(builder.dirname('a/b//'), 'a'); + expect(builder.dirname('a//b'), 'a'); }); test('basename', () { @@ -64,15 +68,16 @@ main() { expect(builder.basename('a/b'), 'b'); expect(builder.basename('a/b/c'), 'c'); expect(builder.basename('a/b.c'), 'b.c'); - expect(builder.basename('a/'), ''); + expect(builder.basename('a/'), 'a'); expect(builder.basename('a/.'), '.'); expect(builder.basename(r'a\b/c'), 'c'); expect(builder.basename('/a'), 'a'); - // TODO(nweiz): this should actually return '/' - expect(builder.basename('/'), ''); - expect(builder.basename('a/b/'), ''); + expect(builder.basename('/'), '/'); + expect(builder.basename('a/b/'), 'b'); expect(builder.basename(r'a/b\c'), r'b\c'); - expect(builder.basename('a//'), ''); + expect(builder.basename('a//'), 'a'); + expect(builder.basename('a/b//'), 'b'); + expect(builder.basename('a//b'), 'b'); }); test('basenameWithoutExtension', () { @@ -81,11 +86,16 @@ main() { expect(builder.basenameWithoutExtension('a/b'), 'b'); expect(builder.basenameWithoutExtension('a/b/c'), 'c'); expect(builder.basenameWithoutExtension('a/b.c'), 'b'); - expect(builder.basenameWithoutExtension('a/'), ''); + expect(builder.basenameWithoutExtension('a/'), 'a'); expect(builder.basenameWithoutExtension('a/.'), '.'); expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(builder.basenameWithoutExtension('a//'), 'a'); + expect(builder.basenameWithoutExtension('a/b//'), 'b'); + expect(builder.basenameWithoutExtension('a//b'), 'b'); + expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); + expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); }); test('isAbsolute', () { @@ -358,5 +368,7 @@ main() { expect(builder.withoutExtension(r'a.b\c'), r'a'); expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(builder.withoutExtension('a/b.c/'), 'a/b/'); + expect(builder.withoutExtension('a/b.c//'), 'a/b//'); }); } diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/path_windows_test.dart index a94d126b..744b7ab5 100644 --- a/pkgs/path/test/path_windows_test.dart +++ b/pkgs/path/test/path_windows_test.dart @@ -54,15 +54,19 @@ main() { expect(builder.dirname(r'a\b'), 'a'); expect(builder.dirname(r'a\b\c'), r'a\b'); expect(builder.dirname(r'a\b.c'), 'a'); - expect(builder.dirname(r'a\'), 'a'); - expect(builder.dirname('a/'), 'a'); + expect(builder.dirname(r'a\'), '.'); + expect(builder.dirname('a/'), '.'); expect(builder.dirname(r'a\.'), 'a'); expect(builder.dirname(r'a\b/c'), r'a\b'); expect(builder.dirname(r'C:\a'), r'C:\'); - expect(builder.dirname('C:\\'), r'C:\'); - expect(builder.dirname(r'a\b\'), r'a\b'); + expect(builder.dirname(r'C:\\\a'), r'C:\'); + expect(builder.dirname(r'C:\'), r'C:\'); + expect(builder.dirname(r'C:\\\'), r'C:\'); + expect(builder.dirname(r'a\b\'), r'a'); expect(builder.dirname(r'a/b\c'), 'a/b'); - expect(builder.dirname(r'a\\'), r'a\'); + expect(builder.dirname(r'a\\'), r'.'); + expect(builder.dirname(r'a\b\\'), 'a'); + expect(builder.dirname(r'a\\b'), 'a'); }); test('basename', () { @@ -71,16 +75,17 @@ main() { expect(builder.basename(r'a\b'), 'b'); expect(builder.basename(r'a\b\c'), 'c'); expect(builder.basename(r'a\b.c'), 'b.c'); - expect(builder.basename(r'a\'), ''); - expect(builder.basename(r'a/'), ''); + expect(builder.basename(r'a\'), 'a'); + expect(builder.basename(r'a/'), 'a'); expect(builder.basename(r'a\.'), '.'); expect(builder.basename(r'a\b/c'), r'c'); expect(builder.basename(r'C:\a'), 'a'); - // TODO(nweiz): this should actually return 'C:\' - expect(builder.basename(r'C:\'), ''); - expect(builder.basename(r'a\b\'), ''); + expect(builder.basename(r'C:\'), r'C:\'); + expect(builder.basename(r'a\b\'), 'b'); expect(builder.basename(r'a/b\c'), 'c'); - expect(builder.basename(r'a\\'), ''); + expect(builder.basename(r'a\\'), 'a'); + expect(builder.basename(r'a\b\\'), 'b'); + expect(builder.basename(r'a\\b'), 'b'); }); test('basenameWithoutExtension', () { @@ -89,11 +94,16 @@ main() { expect(builder.basenameWithoutExtension(r'a\b'), 'b'); expect(builder.basenameWithoutExtension(r'a\b\c'), 'c'); expect(builder.basenameWithoutExtension(r'a\b.c'), 'b'); - expect(builder.basenameWithoutExtension(r'a\'), ''); + expect(builder.basenameWithoutExtension(r'a\'), 'a'); expect(builder.basenameWithoutExtension(r'a\.'), '.'); expect(builder.basenameWithoutExtension(r'a\b/c'), r'c'); expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); + expect(builder.basenameWithoutExtension(r'a\\'), 'a'); + expect(builder.basenameWithoutExtension(r'a\b\\'), 'b'); + expect(builder.basenameWithoutExtension(r'a\\b'), 'b'); + expect(builder.basenameWithoutExtension(r'a\b.c\'), 'b'); + expect(builder.basenameWithoutExtension(r'a\b.c\\'), 'b'); }); test('isAbsolute', () { @@ -392,5 +402,6 @@ main() { expect(builder.withoutExtension(r'a\b/c'), r'a\b/c'); expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c'); expect(builder.withoutExtension(r'a.b/c'), r'a.b/c'); + expect(builder.withoutExtension(r'a\b.c\'), r'a\b\'); }); } From 04f1f7a47661e5b1ddd9782cd70a1c38eb985c46 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Thu, 3 Jan 2013 23:25:33 +0000 Subject: [PATCH 004/183] Update pkg/path to use "package:" imports. Review URL: https://codereview.chromium.org//11734010 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16619 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/test/path_posix_test.dart | 5 ++--- pkgs/path/test/path_test.dart | 5 ++--- pkgs/path/test/path_windows_test.dart | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pkgs/path/test/path_posix_test.dart b/pkgs/path/test/path_posix_test.dart index b917e93a..e1c39421 100644 --- a/pkgs/path/test/path_posix_test.dart +++ b/pkgs/path/test/path_posix_test.dart @@ -6,9 +6,8 @@ library path_test; import 'dart:io' as io; -// TODO(rnystrom): Use "package:" path when #7491 is fixed. -import '../../unittest/lib/unittest.dart'; -import '../lib/path.dart' as path; +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; main() { var builder = new path.Builder(style: path.Style.posix, root: '/root/path'); diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index bd099ef2..eea906ab 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -6,9 +6,8 @@ library all_test; import 'dart:io' as io; -// TODO(rnystrom): Use "package:" path when #7491 is fixed. -import '../../unittest/lib/unittest.dart'; -import '../lib/path.dart' as path; +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; main() { group('path.Style', () { diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/path_windows_test.dart index 744b7ab5..c3ee6fb3 100644 --- a/pkgs/path/test/path_windows_test.dart +++ b/pkgs/path/test/path_windows_test.dart @@ -6,9 +6,8 @@ library path_test; import 'dart:io' as io; -// TODO(rnystrom): Use "package:" path when #7491 is fixed. -import '../../unittest/lib/unittest.dart'; -import '../lib/path.dart' as path; +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; main() { var builder = new path.Builder(style: path.Style.windows, From e1acb230854311cae9f3941ada0b52e336a83769 Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Mon, 7 Jan 2013 11:23:16 +0000 Subject: [PATCH 005/183] Big merge from experimental to bleeding edge. Review URL: https://codereview.chromium.org//11783009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16687 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 99eee7be..cc882f47 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -356,7 +356,7 @@ class Builder { List split(String path) { var parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.filter((part) => part != ''); + parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); if (parsed.root != null) parsed.parts.insertRange(0, 1, parsed.root); return parsed.parts; } From 9502bc52af3f165f9f18fe8f0f56c926d1415e89 Mon Sep 17 00:00:00 2001 From: "efortuna@google.com" Date: Sat, 12 Jan 2013 23:43:44 +0000 Subject: [PATCH 006/183] Fix minor path handling issue in path package. BUG= Review URL: https://codereview.chromium.org//11876012 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17006 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 16 ++++++++++++---- pkgs/path/test/path_windows_test.dart | 12 ++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index cc882f47..f27d5587 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -438,10 +438,14 @@ class Builder { // If the root prefixes don't match (for example, different drive letters // on Windows), then there is no relative path, so just return the absolute - // one. - // TODO(rnystrom): Drive letters are case-insentive on Windows. Should - // handle "C:\" and "c:\" being the same root. - if (fromParsed.root != pathParsed.root) return pathParsed.toString(); + // one. In Windows, drive letters are case-insenstive and we allow + // calculation of relative paths, even if a path has not been normalized. + if (fromParsed.root != pathParsed.root && + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + return pathParsed.toString(); + } // Strip off their common prefix. while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && @@ -644,6 +648,10 @@ class _ParsedPath { parts = newParts; separators = newSeparators; + // Normalize the Windows root if needed. + if (root != null && style == Style.windows) { + root = root.replaceAll('/', '\\'); + } removeTrailingSeparators(); } diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/path_windows_test.dart index c3ee6fb3..12e370b8 100644 --- a/pkgs/path/test/path_windows_test.dart +++ b/pkgs/path/test/path_windows_test.dart @@ -216,7 +216,7 @@ main() { expect(builder.normalize('.'), '.'); expect(builder.normalize('..'), '..'); expect(builder.normalize('a'), 'a'); - expect(builder.normalize('C:/'), r'C:/'); + expect(builder.normalize('C:/'), r'C:\'); expect(builder.normalize(r'C:\'), r'C:\'); expect(builder.normalize(r'\\'), r'\\'); }); @@ -250,7 +250,7 @@ main() { expect(builder.normalize(r'\\..\..\..'), r'\\'); expect(builder.normalize(r'\\..\../..\a'), r'\\a'); expect(builder.normalize(r'c:\..'), r'c:\'); - expect(builder.normalize(r'A:/..\..\..'), r'A:/'); + expect(builder.normalize(r'A:/..\..\..'), r'A:\'); expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); expect(builder.normalize(r'a\..'), '.'); expect(builder.normalize(r'a\b\..'), 'a'); @@ -282,6 +282,10 @@ main() { expect(builder.relative(r'C:\root\path\a'), 'a'); expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(builder.relative(r'C:/'), r'..\..'); + expect(builder.relative(r'C:/root'), '..'); + expect(builder.relative(r'c:\'), r'..\..'); + expect(builder.relative(r'c:\root'), '..'); }); test('given absolute path outside of root', () { @@ -289,6 +293,10 @@ main() { expect(builder.relative(r'C:\root\path\a'), 'a'); expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(builder.relative(r'C:/a/b'), r'..\..\a\b'); + expect(builder.relative(r'C:/root/path/a'), 'a'); + expect(builder.relative(r'c:\a\b'), r'..\..\a\b'); + expect(builder.relative(r'c:\root\path\a'), 'a'); }); test('given absolute path on different drive', () { From c40e191696b78bc2a770725fd1138e5a0d858e05 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 22 Jan 2013 23:52:37 +0000 Subject: [PATCH 007/183] Clean up code that locates SDK and SDK version. This is prep work for SDK constraints. It moves the SDK version-related code out of SdkSource (which will go away at some point) into its own library. I also removed the need for the DART_SDK env variable. It will still be used if set (which the tests need), but if not set, Pub will locate the SDK. Clean up a few static warnings. Review URL: https://codereview.chromium.org//11871028 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17433 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index f27d5587..cf59a651 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -163,6 +163,29 @@ String relative(String path, {String from}) => /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) => _builder.withoutExtension(path); +/// Validates that there are no non-null arguments following a null one and +/// throws an appropriate [ArgumentError] on failure. +_validateArgList(String method, List args) { + for (var i = 1; i < args.length; i++) { + // Ignore nulls hanging off the end. + if (args[i] == null || args[i - 1] != null) continue; + + var numArgs; + for (numArgs = args.length; numArgs >= 1; numArgs--) { + if (args[numArgs - 1] != null) break; + } + + // Show the arguments. + var message = new StringBuffer(); + message.add("$method("); + message.add(args.take(numArgs) + .mappedBy((arg) => arg == null ? "null" : '"$arg"') + .join(", ")); + message.add("): part ${i - 1} was null, but part $i was not."); + throw new ArgumentError(message.toString()); + } +} + /// An instantiable class for manipulating paths. Unlike the top-level /// functions, this lets you explicitly select what platform the paths will use. class Builder { @@ -302,12 +325,7 @@ class Builder { var needsSeparator = false; var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; - for (var i = 1; i < parts.length; i++) { - if (parts[i] != null && parts[i - 1] == null) { - throw new ArgumentError("join(): part ${i - 1} was null, but part $i " - "was not."); - } - } + _validateArgList("join", parts); for (var part in parts) { if (part == null) continue; @@ -380,12 +398,6 @@ class Builder { /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' String resolve(String part1, [String part2, String part3, String part4, String part5, String part6, String part7]) { - if (!?part2) return join(root, part1); - if (!?part3) return join(root, part1, part2); - if (!?part4) return join(root, part1, part2, part3); - if (!?part5) return join(root, part1, part2, part3, part4); - if (!?part6) return join(root, part1, part2, part3, part4, part5); - if (!?part7) return join(root, part1, part2, part3, part4, part5, part6); return join(root, part1, part2, part3, part4, part5, part6, part7); } From af4a2845d359f94bc09cd1ced753d8307e5e0bbe Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 23 Jan 2013 18:56:25 +0000 Subject: [PATCH 008/183] Add a README for path. Review URL: https://codereview.chromium.org//12047035 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17487 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 312 ++++++++++++++++++++++++++++++++++++++++ pkgs/path/lib/path.dart | 4 +- 2 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 pkgs/path/README.md diff --git a/pkgs/path/README.md b/pkgs/path/README.md new file mode 100644 index 00000000..66f40b1f --- /dev/null +++ b/pkgs/path/README.md @@ -0,0 +1,312 @@ +A comprehensive, cross-platform path manipulation library for Dart. + +The pathos library provides common operations for manipulating file paths: +joining, splitting, normalizing, etc. + +We've tried very hard to make this library do the "right" thing on whatever +platform you run it on. When you use the top-level functions, it will assume +the host OS's path style and work with that. If you want to specifically work +with paths of a specific style, you can construct a `path.Builder` for that +style. + +## Using + +The path library was designed to be imported with a prefix, though you don't +have to if you don't want to: + + import 'package:pathos/path.dart' as path; + +## Top-level functions + +The most common way to use the library is through the top-level functions. +These manipulate path strings based on your current working directory and the +path style (POSIX or Windows) of the host operating system. + +### String get current + +Gets the path to the current working directory. + +### String get separator + +Gets the path separator for the current platform. On Mac and Linux, this +is `/`. On Windows, it's `\`. + +### String absolute(String path) + +Converts [path] to an absolute path by resolving it relative to the current +working directory. If [path] is already an absolute path, just returns it. + + path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt + +### String basename(String path) + +Gets the part of [path] after the last separator. + + path.basename('path/to/foo.dart'); // -> 'foo.dart' + path.basename('path/to'); // -> 'to' + +Trailing separators are ignored. + + builder.basename('path/to/'); // -> 'to' + +### String basenameWithoutExtension(String path) + +Gets the part of [path] after the last separator, and without any trailing +file extension. + + path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + +Trailing separators are ignored. + + builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' + +### String dirname(String path) + +Gets the part of [path] before the last separator. + + path.dirname('path/to/foo.dart'); // -> 'path/to' + path.dirname('path/to'); // -> 'to' + +Trailing separators are ignored. + + builder.dirname('path/to/'); // -> 'path' + +### String extension(String path) + +Gets the file extension of [path]: the portion of [basename] from the last +`.` to the end (including the `.` itself). + + path.extension('path/to/foo.dart'); // -> '.dart' + path.extension('path/to/foo'); // -> '' + path.extension('path.to/foo'); // -> '' + path.extension('path/to/foo.dart.js'); // -> '.js' + +If the file name starts with a `.`, then that is not considered the +extension: + + path.extension('~/.bashrc'); // -> '' + path.extension('~/.notes.txt'); // -> '.txt' + +### String rootPrefix(String path) + +Returns the root of [path], if it's absolute, or the empty string if it's +relative. + + // Unix + path.rootPrefix('path/to/foo'); // -> '' + path.rootPrefix('/path/to/foo'); // -> '/' + + // Windows + path.rootPrefix(r'path\to\foo'); // -> '' + path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + +### bool isAbsolute(String path) + +Returns `true` if [path] is an absolute path and `false` if it is a +relative path. On POSIX systems, absolute paths start with a `/` (forward +slash). On Windows, an absolute path starts with `\\`, or a drive letter +followed by `:/` or `:\`. + +### bool isRelative(String path) + +Returns `true` if [path] is a relative path and `false` if it is absolute. +On POSIX systems, absolute paths start with a `/` (forward slash). On +Windows, an absolute path starts with `\\`, or a drive letter followed by +`:/` or `:\`. + +### String join(String part1, [String part2, String part3, ...]) + +Joins the given path parts into a single path using the current platform's +[separator]. Example: + + path.join('path', 'to', 'foo'); // -> 'path/to/foo' + +If any part ends in a path separator, then a redundant separator will not +be added: + + path.join('path/', 'to', 'foo'); // -> 'path/to/foo + +If a part is an absolute path, then anything before that will be ignored: + + path.join('path', '/to', 'foo'); // -> '/to/foo' + +### List split(String path) + +Splits [path] into its components using the current platform's [separator]. + + path.split('path/to/foo'); // -> ['path', 'to', 'foo'] + +The path will *not* be normalized before splitting. + + path.split('path/../foo'); // -> ['path', '..', 'foo'] + +If [path] is absolute, the root directory will be the first element in the +array. Example: + + // Unix + path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + + // Windows + path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + +### String normalize(String path) + +Normalizes [path], simplifying it by handling `..`, and `.`, and +removing redundant path separators whenever possible. + + path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' +String normalize(String path) => _builder.normalize(path); + +### String relative(String path, {String from}) + +Attempts to convert [path] to an equivalent relative path from the current +directory. + + // Given current directory is /root/path: + path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + path.relative('/root/other.dart'); // -> '../other.dart' + +If the [from] argument is passed, [path] is made relative to that instead. + + path.relative('/root/path/a/b.dart', + from: '/root/path'); // -> 'a/b.dart' + path.relative('/root/other.dart', + from: '/root/path'); // -> '../other.dart' + +Since there is no relative path from one drive letter to another on Windows, +this will return an absolute path in that case. + + path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' + +### String withoutExtension(String path) + +Removes a trailing extension from the last part of [path]. + + withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + +## The path.Builder class + +In addition to the functions, path exposes a `path.Builder` class. This lets +you configure the root directory and path style that paths are built using +explicitly instead of assuming the current working directory and host OS's path +style. + +You won't often use this, but it can be useful if you do a lot of path +manipulation relative to some root directory. + + var builder = new path.Builder(root: '/other/root'); + builder.relative('/other/root/foo.txt'); // -> 'foo.txt' + +It exposes the same methods and getters as the top-level functions, with the +addition of: + +### new Builder({Style style, String root}) + +Creates a new path builder for the given style and root directory. + +If [style] is omitted, it uses the host operating system's path style. If +[root] is omitted, it defaults to the current working directory. If [root] +is relative, it is considered relative to the current working directory. + +### Style style + +The style of path that this builder works with. + +### String root + +The root directory that relative paths will be relative to. + +### String get separator + +Gets the path separator for the builder's [style]. On Mac and Linux, +this is `/`. On Windows, it's `\`. + +### String rootPrefix(String path) + +Returns the root of [path], if it's absolute, or an empty string if it's +relative. + + // Unix + builder.rootPrefix('path/to/foo'); // -> '' + builder.rootPrefix('/path/to/foo'); // -> '/' + + // Windows + builder.rootPrefix(r'path\to\foo'); // -> '' + builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + +### String resolve(String part1, [String part2, String part3, ...]) + +Creates a new path by appending the given path parts to the [root]. +Equivalent to [join()] with [root] as the first argument. Example: + + var builder = new Builder(root: 'root'); + builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' + +## The path.Style class + +The path library can work with two different "flavors" of path: POSIX and +Windows. The differences between these are encapsulated by the `path.Style` +enum class. There are two instances of it: + +### path.Style.posix + +POSIX-style paths use "/" (forward slash) as separators. Absolute paths +start with "/". Used by UNIX, Linux, Mac OS X, and others. + +### path.Style.windows + +Windows paths use "\" (backslash) as separators. Absolute paths start with +a drive letter followed by a colon (example, "C:") or two backslashes +("\\") for UNC paths. + +## FAQ + +### Where can I use this? + +Currently, Dart has no way of encapsulating configuration-specific code. +Ideally, this library would be able to import dart:io when that's available or +dart:html when that is. That would let it seamlessly work on both. + +Until then, this only works on the standalone VM. It's API is not coupled to +dart:io, but it uses it internally to determine the current working directory. + +### Why doesn't this make paths first-class objects? + +When you have path *objects*, then every API that takes a path has to decide if +it accepts strings, path objects, or both. + + * Accepting strings is the most convenient, but then it seems weird to have + these path objects that aren't actually accepted by anything that needs a + path. Once you've created a path, you have to always call `.toString()` on + it before you can do anything useful with it. + + * Requiring objects forces users to wrap path strings in these objects, which + is tedious. It also means coupling that API to whatever library defines this + path class. If there are multiple "path" libraries that each define their + own path types, then any library that works with paths has to pick which one + it uses. + + * Taking both means you can't type your API. That defeats the purpose of + having a path type: why have a type if your APIs can't annotate that they + use it? + +Given that, we've decided this library should simply treat paths as strings. + +### How cross-platform is this? + +We believe this library handles most of the corner cases of Windows paths +(POSIX paths are generally pretty straightforward): + + * It understands that *both* "/" and "\" are valid path separators, not just + "\". + + * It can accurately tell if a path is absolute based on drive-letters or UNC + prefix. + + * It understands that "/foo" is not an absolute path on Windows. + + * It knows that "C:\foo\one.txt" and "c:/foo\two.txt" are two files in the + same directory. + +If you find a problem, surprise or something that's unclear, please don't +hesitate to [file a bug](http://dartbug.com/new) and let us know. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index cf59a651..854d4da3 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -31,7 +31,7 @@ String absolute(String path) => join(current, path); /// /// Trailing separators are ignored. /// -/// builder.dirname('path/to/'); // -> 'to' +/// builder.basename('path/to/'); // -> 'to' String basename(String path) => _builder.basename(path); /// Gets the part of [path] after the last separator, and without any trailing @@ -41,7 +41,7 @@ String basename(String path) => _builder.basename(path); /// /// Trailing separators are ignored. /// -/// builder.dirname('path/to/foo.dart/'); // -> 'foo' +/// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => _builder.basenameWithoutExtension(path); From 79565b5b803764d2ca0f1546e00a072a6f0a811d Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 31 Jan 2013 12:37:13 +0000 Subject: [PATCH 009/183] Rename mappedBy to map. Retain a deprecated mappedBy for now. Change return type of mappedBy, skip and take on List to Iterable. BUG= http://dartbug.com/8063 BUG= http://dartbug.com/8064 BUG= http://dartbug.com/6739 BUG= http://dartbug.com/7982 Review URL: https://codereview.chromium.org//12086062 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17899 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 854d4da3..3bd23323 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -179,7 +179,7 @@ _validateArgList(String method, List args) { var message = new StringBuffer(); message.add("$method("); message.add(args.take(numArgs) - .mappedBy((arg) => arg == null ? "null" : '"$arg"') + .map((arg) => arg == null ? "null" : '"$arg"') .join(", ")); message.add("): part ${i - 1} was null, but part $i was not."); throw new ArgumentError(message.toString()); From 605d2b58ed9d23ba2934f8f0d0e855ed3f23f32f Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Thu, 31 Jan 2013 14:02:27 +0000 Subject: [PATCH 010/183] Revert "Rename mappedBy to map." This reverts commit 17899. Review URL: https://codereview.chromium.org//12087103 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17907 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 3bd23323..854d4da3 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -179,7 +179,7 @@ _validateArgList(String method, List args) { var message = new StringBuffer(); message.add("$method("); message.add(args.take(numArgs) - .map((arg) => arg == null ? "null" : '"$arg"') + .mappedBy((arg) => arg == null ? "null" : '"$arg"') .join(", ")); message.add("): part ${i - 1} was null, but part $i was not."); throw new ArgumentError(message.toString()); From 57c5ea1cb51474789a3ded7378dc77de8d2e6561 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 31 Jan 2013 15:12:56 +0000 Subject: [PATCH 011/183] Reapply "Rename mappedBy to map." This reverts commit r17907. TBR. Review URL: https://codereview.chromium.org//12090093 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@17918 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 854d4da3..3bd23323 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -179,7 +179,7 @@ _validateArgList(String method, List args) { var message = new StringBuffer(); message.add("$method("); message.add(args.take(numArgs) - .mappedBy((arg) => arg == null ? "null" : '"$arg"') + .map((arg) => arg == null ? "null" : '"$arg"') .join(", ")); message.add("): part ${i - 1} was null, but part $i was not."); throw new ArgumentError(message.toString()); From ff1a947c960d303f5439f78ffe07bc7f46dd4190 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 21 Feb 2013 22:48:30 +0000 Subject: [PATCH 012/183] Add path.joinAll. Review URL: https://codereview.chromium.org//12314047 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@18860 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 42 +++++++++++++++++++++++---- pkgs/path/test/path_posix_test.dart | 19 ++++++++++++ pkgs/path/test/path_windows_test.dart | 20 +++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 3bd23323..9e540b2d 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -112,6 +112,23 @@ String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) => _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); +/// Joins the given path parts into a single path using the current platform's +/// [separator]. Example: +/// +/// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' +/// +/// If any part ends in a path separator, then a redundant separator will not +/// be added: +/// +/// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo +/// +/// If a part is an absolute path, then anything before that will be ignored: +/// +/// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' +/// +/// For a fixed number of parts, [join] is usually terser. +String joinAll(Iterable parts) => _builder.joinAll(parts); + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. /// @@ -321,15 +338,30 @@ class Builder { /// String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) { - var buffer = new StringBuffer(); - var needsSeparator = false; - var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; _validateArgList("join", parts); + return joinAll(parts.where((part) => part != null)); + } - for (var part in parts) { - if (part == null) continue; + /// Joins the given path parts into a single path. Example: + /// + /// builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo' + /// + /// For a fixed number of parts, [join] is usually terser. + String joinAll(Iterable parts) { + var buffer = new StringBuffer(); + var needsSeparator = false; + for (var part in parts) { if (this.isAbsolute(part)) { // An absolute path discards everything before it. buffer.clear(); diff --git a/pkgs/path/test/path_posix_test.dart b/pkgs/path/test/path_posix_test.dart index e1c39421..763035fe 100644 --- a/pkgs/path/test/path_posix_test.dart +++ b/pkgs/path/test/path_posix_test.dart @@ -161,6 +161,25 @@ main() { }); }); + group('joinAll', () { + test('allows more than eight parts', () { + expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + 'a/b/c/d/e/f/g/h/i'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d'); + expect(builder.joinAll(['a\\', 'b']), r'a\/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + }); + }); + group('split', () { test('simple cases', () { expect(builder.split(''), []); diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/path_windows_test.dart index 12e370b8..8164224d 100644 --- a/pkgs/path/test/path_windows_test.dart +++ b/pkgs/path/test/path_windows_test.dart @@ -180,6 +180,26 @@ main() { }); }); + group('joinAll', () { + test('allows more than eight parts', () { + expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + r'a\b\c\d\e\f\g\h\i'); + }); + + test('does not add separator if a part ends or begins in one', () { + expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); + expect(builder.joinAll(['a/', 'b']), r'a/b'); + expect(builder.joinAll(['a', '/b']), 'a/b'); + expect(builder.joinAll(['a', r'\b']), r'a\b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.joinAll(['a', '/b', '/c', 'd']), r'a/b/c\d'); + expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); + expect(builder.joinAll(['a', r'\\b', r'\\c', 'd']), r'\\c\d'); + }); + }); + group('split', () { test('simple cases', () { expect(builder.split(''), []); From a3ad547ce1c3c05cb34e6537e492cc5ec408203f Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 12 Jul 2013 17:54:48 +0000 Subject: [PATCH 013/183] Rename "pathos" package to "path". R=ajohnsen@google.com Review URL: https://codereview.chromium.org//18356011 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@24964 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 125 +++- pkgs/path/lib/path.dart | 523 ++++++++++++-- pkgs/path/pubspec.yaml | 5 +- pkgs/path/test/dart2js_test.dart | 27 + pkgs/path/test/dartium_test.dart | 29 + pkgs/path/test/io_test.dart | 30 + pkgs/path/test/path_test.dart | 22 - .../{path_posix_test.dart => posix_test.dart} | 38 +- pkgs/path/test/url_test.dart | 653 ++++++++++++++++++ ...th_windows_test.dart => windows_test.dart} | 50 +- 10 files changed, 1390 insertions(+), 112 deletions(-) create mode 100644 pkgs/path/test/dart2js_test.dart create mode 100644 pkgs/path/test/dartium_test.dart create mode 100644 pkgs/path/test/io_test.dart rename pkgs/path/test/{path_posix_test.dart => posix_test.dart} (91%) create mode 100644 pkgs/path/test/url_test.dart rename pkgs/path/test/{path_windows_test.dart => windows_test.dart} (89%) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 66f40b1f..dacacaa8 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -1,12 +1,12 @@ A comprehensive, cross-platform path manipulation library for Dart. -The pathos library provides common operations for manipulating file paths: +The path package provides common operations for manipulating file paths: joining, splitting, normalizing, etc. We've tried very hard to make this library do the "right" thing on whatever -platform you run it on. When you use the top-level functions, it will assume -the host OS's path style and work with that. If you want to specifically work -with paths of a specific style, you can construct a `path.Builder` for that +platform you run it on. When you use the top-level functions, it will assume the +current platform's path style and work with that. If you want to specifically +work with paths of a specific style, you can construct a `path.Builder` for that style. ## Using @@ -14,22 +14,24 @@ style. The path library was designed to be imported with a prefix, though you don't have to if you don't want to: - import 'package:pathos/path.dart' as path; + import 'package:path/path.dart' as path; // TODO(bob): ??? ## Top-level functions The most common way to use the library is through the top-level functions. These manipulate path strings based on your current working directory and the -path style (POSIX or Windows) of the host operating system. +path style (POSIX, Windows, or URLs) of the host platform. ### String get current -Gets the path to the current working directory. +Gets the path to the current working directory. In the browser, this means the +current URL. When using dart2js, this currently returns `.` due to technical +constraints. In the future, it will return the current URL. ### String get separator -Gets the path separator for the current platform. On Mac and Linux, this -is `/`. On Windows, it's `\`. +Gets the path separator for the current platform. On Mac, Linux, and the +browser, this is `/`. On Windows, it's `\`. ### String absolute(String path) @@ -100,12 +102,23 @@ relative. path.rootPrefix(r'path\to\foo'); // -> '' path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + // URL + path.rootPrefix('path/to/foo'); // -> '' + path.rootPrefix('http://dartlang.org/path/to/foo'); + // -> 'http://dartlang.org' + ### bool isAbsolute(String path) -Returns `true` if [path] is an absolute path and `false` if it is a -relative path. On POSIX systems, absolute paths start with a `/` (forward -slash). On Windows, an absolute path starts with `\\`, or a drive letter -followed by `:/` or `:\`. +Returns `true` if [path] is an absolute path and `false` if it is a relative +path. On POSIX systems, absolute paths start with a `/` (forward slash). On +Windows, an absolute path starts with `\\`, or a drive letter followed by `:/` +or `:\`. For URLs, absolute paths either start with a protocol and optional +hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + +URLs that start with `/` are known as "root-relative", since they're relative to +the root of the current URL. Since root-relative paths are still absolute in +every other sense, [isAbsolute] will return true for them. They can be detected +using [isRootRelative]. ### bool isRelative(String path) @@ -114,6 +127,16 @@ On POSIX systems, absolute paths start with a `/` (forward slash). On Windows, an absolute path starts with `\\`, or a drive letter followed by `:/` or `:\`. +### bool isRootRelative(String path) + +Returns `true` if [path] is a root-relative path and `false` if it's not. URLs +that start with `/` are known as "root-relative", since they're relative to the +root of the current URL. Since root-relative paths are still absolute in every +other sense, [isAbsolute] will return true for them. They can be detected using +[isRootRelative]. + +No POSIX and Windows paths are root-relative. + ### String join(String part1, [String part2, String part3, ...]) Joins the given path parts into a single path using the current platform's @@ -149,6 +172,10 @@ array. Example: // Windows path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + // Browser + path.split('http://dartlang.org/path/to/foo'); + // -> ['http://dartlang.org', 'path', 'to', 'foo'] + ### String normalize(String path) Normalizes [path], simplifying it by handling `..`, and `.`, and @@ -176,14 +203,57 @@ If the [from] argument is passed, [path] is made relative to that instead. Since there is no relative path from one drive letter to another on Windows, this will return an absolute path in that case. + // Windows path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' + // URL + path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); + // -> 'http://dartlang.org' + ### String withoutExtension(String path) Removes a trailing extension from the last part of [path]. withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' +### String fromUri(Uri uri) + +Returns the path represented by [uri]. For POSIX and Windows styles, [uri] must +be a `file:` URI. For the URL style, this will just convert [uri] to a string. + + // POSIX + path.fromUri(Uri.parse('file:///path/to/foo')) + // -> '/path/to/foo' + + // Windows + path.fromUri(Uri.parse('file:///C:/path/to/foo')) + // -> r'C:\path\to\foo' + + // URL + path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) + // -> 'http://dartlang.org/path/to/foo' + +### Uri toUri(String path) + +Returns the URI that represents [path]. For POSIX and Windows styles, this will +return a `file:` URI. For the URL style, this will just convert [path] to a +[Uri]. + +This will always convert relative paths to absolute ones before converting +to a URI. + + // POSIX + path.toUri('/path/to/foo') + // -> Uri.parse('file:///path/to/foo') + + // Windows + path.toUri(r'C:\path\to\foo') + // -> Uri.parse('file:///C:/path/to/foo') + + // URL + path.toUri('http://dartlang.org/path/to/foo') + // -> Uri.parse('http://dartlang.org/path/to/foo') + ## The path.Builder class In addition to the functions, path exposes a `path.Builder` class. This lets @@ -234,6 +304,11 @@ relative. builder.rootPrefix(r'path\to\foo'); // -> '' builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + // URL + builder.rootPrefix('path/to/foo'); // -> '' + builder.rootPrefix('http://dartlang.org/path/to/foo'); + // -> 'http://dartlang.org' + ### String resolve(String part1, [String part2, String part3, ...]) Creates a new path by appending the given path parts to the [root]. @@ -244,9 +319,9 @@ Equivalent to [join()] with [root] as the first argument. Example: ## The path.Style class -The path library can work with two different "flavors" of path: POSIX and -Windows. The differences between these are encapsulated by the `path.Style` -enum class. There are two instances of it: +The path library can work with three different "flavors" of path: POSIX, +Windows, and URLs. The differences between these are encapsulated by the +`path.Style` enum class. There are three instances of it: ### path.Style.posix @@ -259,16 +334,22 @@ Windows paths use "\" (backslash) as separators. Absolute paths start with a drive letter followed by a colon (example, "C:") or two backslashes ("\\") for UNC paths. +### path.Style.url + +URLs aren't filesystem paths, but they're supported by Pathos to make it easier +to manipulate URL paths in the browser. + +URLs use "/" (forward slash) as separators. Absolute paths either start with a +protocol and optional hostname (e.g. `http://dartlang.org`, `file://`) or with +"/". + ## FAQ ### Where can I use this? -Currently, Dart has no way of encapsulating configuration-specific code. -Ideally, this library would be able to import dart:io when that's available or -dart:html when that is. That would let it seamlessly work on both. - -Until then, this only works on the standalone VM. It's API is not coupled to -dart:io, but it uses it internally to determine the current working directory. +Pathos runs on the Dart VM and in the browser under both dart2js and Dartium. +Under dart2js, it currently returns "." as the current working directory, while +under Dartium it returns the current URL. ### Why doesn't this make paths first-class objects? diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 9e540b2d..b425124c 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -3,16 +3,77 @@ // BSD-style license that can be found in the LICENSE file. /// A comprehensive, cross-platform path manipulation library. +/// +/// ## Installing ## +/// +/// Use [pub][] to install this package. Add the following to your +/// `pubspec.yaml` file. +/// +/// dependencies: +/// path: any +/// +/// Then run `pub install`. +/// +/// For more information, see the [path package on pub.dartlang.org][pkg]. +/// +/// [pub]: http://pub.dartlang.org +/// [pkg]: http://pub.dartlang.org/packages/path library path; -import 'dart:io' as io; +import 'dart:mirrors'; /// An internal builder for the current OS so we can provide a straight /// functional interface and not require users to create one. final _builder = new Builder(); +/** + * Inserts [length] elements in front of the [list] and fills them with the + * [fillValue]. + */ +void _growListFront(List list, int length, fillValue) => + list.insertAll(0, new List.filled(length, fillValue)); + +/// If we're running in the server-side Dart VM, this will return a +/// [LibraryMirror] that gives access to the `dart:io` library. +/// +/// If `dart:io` is not available, this returns null. +LibraryMirror get _io { + try { + return currentMirrorSystem().libraries[Uri.parse('dart:io')]; + } catch (_) { + return null; + } +} + +// TODO(nweiz): when issue 6490 or 6943 are fixed, make this work under dart2js. +/// If we're running in Dartium, this will return a [LibraryMirror] that gives +/// access to the `dart:html` library. +/// +/// If `dart:html` is not available, this returns null. +LibraryMirror get _html { + try { + return currentMirrorSystem().libraries[Uri.parse('dart:html')]; + } catch (_) { + return null; + } +} + /// Gets the path to the current working directory. -String get current => new io.Directory.current().path; +/// +/// In the browser, this means the current URL. When using dart2js, this +/// currently returns `.` due to technical constraints. In the future, it will +/// return the current URL. +String get current { + if (_io != null) { + return _io.classes[const Symbol('Directory')] + .getField(const Symbol('current')).reflectee.path; + } else if (_html != null) { + return _html.getField(const Symbol('window')) + .reflectee.location.href; + } else { + return '.'; + } +} /// Gets the path separator for the current platform. On Mac and Linux, this /// is `/`. On Windows, it's `\`. @@ -81,12 +142,25 @@ String extension(String path) => _builder.extension(path); /// // Windows /// path.rootPrefix(r'path\to\foo'); // -> '' /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +/// +/// // URL +/// path.rootPrefix('path/to/foo'); // -> '' +/// path.rootPrefix('http://dartlang.org/path/to/foo'); +/// // -> 'http://dartlang.org' String rootPrefix(String path) => _builder.rootPrefix(path); /// Returns `true` if [path] is an absolute path and `false` if it is a -/// relative path. On POSIX systems, absolute paths start with a `/` (forward -/// slash). On Windows, an absolute path starts with `\\`, or a drive letter -/// followed by `:/` or `:\`. +/// relative path. +/// +/// On POSIX systems, absolute paths start with a `/` (forward slash). On +/// Windows, an absolute path starts with `\\`, or a drive letter followed by +/// `:/` or `:\`. For URLs, absolute paths either start with a protocol and +/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. bool isAbsolute(String path) => _builder.isAbsolute(path); /// Returns `true` if [path] is a relative path and `false` if it is absolute. @@ -95,6 +169,16 @@ bool isAbsolute(String path) => _builder.isAbsolute(path); /// `:/` or `:\`. bool isRelative(String path) => _builder.isRelative(path); +/// Returns `true` if [path] is a root-relative path and `false` if it's not. +/// +/// URLs that start with `/` are known as "root-relative", since they're +/// relative to the root of the current URL. Since root-relative paths are still +/// absolute in every other sense, [isAbsolute] will return true for them. They +/// can be detected using [isRootRelative]. +/// +/// No POSIX and Windows paths are root-relative. +bool isRootRelative(String path) => _builder.isRootRelative(path); + /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: /// @@ -146,6 +230,10 @@ String joinAll(Iterable parts) => _builder.joinAll(parts); /// /// // Windows /// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +/// +/// // Browser +/// path.split('http://dartlang.org/path/to/foo'); +/// // -> ['http://dartlang.org', 'path', 'to', 'foo'] List split(String path) => _builder.split(path); /// Normalizes [path], simplifying it by handling `..`, and `.`, and @@ -169,9 +257,15 @@ String normalize(String path) => _builder.normalize(path); /// from: '/root/path'); // -> '../other.dart' /// /// Since there is no relative path from one drive letter to another on Windows, -/// this will return an absolute path in that case. +/// or from one hostname to another for URLs, this will return an absolute path +/// in those cases. /// +/// // Windows /// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' +/// +/// // URL +/// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); +/// // -> 'http://dartlang.org' String relative(String path, {String from}) => _builder.relative(path, from: from); @@ -180,6 +274,45 @@ String relative(String path, {String from}) => /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) => _builder.withoutExtension(path); +/// Returns the path represented by [uri]. +/// +/// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL +/// style, this will just convert [uri] to a string. +/// +/// // POSIX +/// path.fromUri(Uri.parse('file:///path/to/foo')) +/// // -> '/path/to/foo' +/// +/// // Windows +/// path.fromUri(Uri.parse('file:///C:/path/to/foo')) +/// // -> r'C:\path\to\foo' +/// +/// // URL +/// path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) +/// // -> 'http://dartlang.org/path/to/foo' +String fromUri(Uri uri) => _builder.fromUri(uri); + +/// Returns the URI that represents [path]. +/// +/// For POSIX and Windows styles, this will return a `file:` URI. For the URL +/// style, this will just convert [path] to a [Uri]. +/// +/// This will always convert relative paths to absolute ones before converting +/// to a URI. +/// +/// // POSIX +/// path.toUri('/path/to/foo') +/// // -> Uri.parse('file:///path/to/foo') +/// +/// // Windows +/// path.toUri(r'C:\path\to\foo') +/// // -> Uri.parse('file:///C:/path/to/foo') +/// +/// // URL +/// path.toUri('http://dartlang.org/path/to/foo') +/// // -> Uri.parse('http://dartlang.org/path/to/foo') +Uri toUri(String path) => _builder.toUri(path); + /// Validates that there are no non-null arguments following a null one and /// throws an appropriate [ArgumentError] on failure. _validateArgList(String method, List args) { @@ -194,11 +327,11 @@ _validateArgList(String method, List args) { // Show the arguments. var message = new StringBuffer(); - message.add("$method("); - message.add(args.take(numArgs) + message.write("$method("); + message.write(args.take(numArgs) .map((arg) => arg == null ? "null" : '"$arg"') .join(", ")); - message.add("): part ${i - 1} was null, but part $i was not."); + message.write("): part ${i - 1} was null, but part $i was not."); throw new ArgumentError(message.toString()); } } @@ -211,9 +344,16 @@ class Builder { /// If [style] is omitted, it uses the host operating system's path style. If /// [root] is omitted, it defaults to the current working directory. If [root] /// is relative, it is considered relative to the current working directory. + /// + /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults + /// to the current URL. When using dart2js, it currently defaults to `.` due + /// to technical constraints. factory Builder({Style style, String root}) { if (style == null) { - if (io.Platform.operatingSystem == 'windows') { + if (_io == null) { + style = Style.url; + } else if (_io.classes[const Symbol('Platform')] + .getField(const Symbol('operatingSystem')).reflectee == 'windows') { style = Style.windows; } else { style = Style.posix; @@ -245,7 +385,7 @@ class Builder { /// /// Trailing separators are ignored. /// - /// builder.dirname('path/to/'); // -> 'to' + /// builder.basename('path/to/'); // -> 'to' String basename(String path) => _parse(path).basename; /// Gets the part of [path] after the last separator on the builder's @@ -255,7 +395,7 @@ class Builder { /// /// Trailing separators are ignored. /// - /// builder.dirname('path/to/foo.dart/'); // -> 'foo' + /// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => _parse(path).basenameWithoutExtension; @@ -306,22 +446,45 @@ class Builder { /// // Windows /// builder.rootPrefix(r'path\to\foo'); // -> '' /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// + /// // URL + /// builder.rootPrefix('path/to/foo'); // -> '' + /// builder.rootPrefix('http://dartlang.org/path/to/foo'); + /// // -> 'http://dartlang.org' String rootPrefix(String path) { var root = _parse(path).root; return root == null ? '' : root; } /// Returns `true` if [path] is an absolute path and `false` if it is a - /// relative path. On POSIX systems, absolute paths start with a `/` (forward - /// slash). On Windows, an absolute path starts with `\\`, or a drive letter - /// followed by `:/` or `:\`. + /// relative path. + /// + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. bool isAbsolute(String path) => _parse(path).isAbsolute; /// Returns `true` if [path] is a relative path and `false` if it is absolute. /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. - bool isRelative(String path) => !isAbsolute(path); + bool isRelative(String path) => !this.isAbsolute(path); + + /// Returns `true` if [path] is a root-relative path and `false` if it's not. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + /// + /// No POSIX and Windows paths are root-relative. + bool isRootRelative(String path) => _parse(path).isRootRelative; /// Joins the given path parts into a single path. Example: /// @@ -360,26 +523,34 @@ class Builder { String joinAll(Iterable parts) { var buffer = new StringBuffer(); var needsSeparator = false; + var isAbsoluteAndNotRootRelative = false; for (var part in parts) { - if (this.isAbsolute(part)) { + if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + // If the new part is root-relative, it preserves the previous root but + // replaces the path after it. + var oldRoot = this.rootPrefix(buffer.toString()); + buffer.clear(); + buffer.write(oldRoot); + buffer.write(part); + } else if (this.isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !this.isRootRelative(part); // An absolute path discards everything before it. buffer.clear(); - buffer.add(part); + buffer.write(part); } else { if (part.length > 0 && part[0].contains(style.separatorPattern)) { // The part starts with a separator, so we don't need to add one. } else if (needsSeparator) { - buffer.add(separator); + buffer.write(separator); } - buffer.add(part); + buffer.write(part); } // Unless this part ends with a separator, we'll need to add one before // the next part. - needsSeparator = part.length > 0 && - !part[part.length - 1].contains(style.separatorPattern); + needsSeparator = part.contains(style.needsSeparatorPattern); } return buffer.toString(); @@ -406,8 +577,9 @@ class Builder { List split(String path) { var parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); - if (parsed.root != null) parsed.parts.insertRange(0, 1, parsed.root); + parsed.parts = parsed.parts.where((part) => !part.isEmpty) + .toList(); + if (parsed.root != null) parsed.parts.insert(0, parsed.root); return parsed.parts; } @@ -469,7 +641,9 @@ class Builder { // If the given path is relative, resolve it relative to the root of the // builder. - if (this.isRelative(path)) path = this.resolve(path); + if (this.isRelative(path) || this.isRootRelative(path)) { + path = this.resolve(path); + } // If the path is still relative and `from` is absolute, we're unable to // find a path from `from` to `path`. @@ -480,6 +654,10 @@ class Builder { var fromParsed = _parse(from)..normalize(); var pathParsed = _parse(path)..normalize(); + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + return pathParsed.toString(); + } + // If the root prefixes don't match (for example, different drive letters // on Windows), then there is no relative path, so just return the absolute // one. In Windows, drive letters are case-insenstive and we allow @@ -495,16 +673,17 @@ class Builder { while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && fromParsed.parts[0] == pathParsed.parts[0]) { fromParsed.parts.removeAt(0); - fromParsed.separators.removeAt(0); + fromParsed.separators.removeAt(1); pathParsed.parts.removeAt(0); - pathParsed.separators.removeAt(0); + pathParsed.separators.removeAt(1); } // If there are any directories left in the root path, we need to walk up // out of them. - pathParsed.parts.insertRange(0, fromParsed.parts.length, '..'); - pathParsed.separators.insertRange(0, fromParsed.parts.length, - style.separator); + _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); + pathParsed.separators[0] = ''; + pathParsed.separators.insertAll(1, + new List.filled(fromParsed.parts.length, style.separator)); // Corner case: the paths completely collapsed. if (pathParsed.parts.length == 0) return '.'; @@ -532,16 +711,68 @@ class Builder { return parsed.toString(); } + /// Returns the path represented by [uri]. + /// + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL + /// style, this will just convert [uri] to a string. + /// + /// // POSIX + /// builder.fromUri(Uri.parse('file:///path/to/foo')) + /// // -> '/path/to/foo' + /// + /// // Windows + /// builder.fromUri(Uri.parse('file:///C:/path/to/foo')) + /// // -> r'C:\path\to\foo' + /// + /// // URL + /// builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) + /// // -> 'http://dartlang.org/path/to/foo' + String fromUri(Uri uri) => style.pathFromUri(uri); + + /// Returns the URI that represents [path]. + /// + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL + /// style, this will just convert [path] to a [Uri]. + /// + /// // POSIX + /// builder.toUri('/path/to/foo') + /// // -> Uri.parse('file:///path/to/foo') + /// + /// // Windows + /// builder.toUri(r'C:\path\to\foo') + /// // -> Uri.parse('file:///C:/path/to/foo') + /// + /// // URL + /// builder.toUri('http://dartlang.org/path/to/foo') + /// // -> Uri.parse('http://dartlang.org/path/to/foo') + Uri toUri(String path) { + if (isRelative(path)) { + return Uri.parse(path.replaceAll(style.separatorPattern, '/')); + } else { + return style.pathToUri(join(root, path)); + } + } + _ParsedPath _parse(String path) { var before = path; // Remove the root prefix, if any. var root = style.getRoot(path); + var isRootRelative = style.getRelativeRoot(path) != null; if (root != null) path = path.substring(root.length); // Split the parts on path separators. var parts = []; var separators = []; + + var firstSeparator = style.separatorPattern.firstMatch(path); + if (firstSeparator != null && firstSeparator.start == 0) { + separators.add(firstSeparator[0]); + path = path.substring(firstSeparator[0].length); + } else { + separators.add(''); + } + var start = 0; for (var match in style.separatorPattern.allMatches(path)) { parts.add(path.substring(start, match.start)); @@ -555,57 +786,216 @@ class Builder { separators.add(''); } - return new _ParsedPath(style, root, parts, separators); + return new _ParsedPath(style, root, isRootRelative, parts, separators); } } /// An enum type describing a "flavor" of path. -class Style { +abstract class Style { /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths /// start with "/". Used by UNIX, Linux, Mac OS X, and others. - static final posix = new Style._('posix', '/', '/', '/'); + static final posix = new _PosixStyle(); /// Windows paths use "\" (backslash) as separators. Absolute paths start with /// a drive letter followed by a colon (example, "C:") or two backslashes /// ("\\") for UNC paths. // TODO(rnystrom): The UNC root prefix should include the drive name too, not // just the "\\". - static final windows = new Style._('windows', '\\', r'[/\\]', - r'\\\\|[a-zA-Z]:[/\\]'); + static final windows = new _WindowsStyle(); - Style._(this.name, this.separator, String separatorPattern, - String rootPattern) - : separatorPattern = new RegExp(separatorPattern), - _rootPattern = new RegExp('^$rootPattern'); + /// URLs aren't filesystem paths, but they're supported by Pathos to make it + /// easier to manipulate URL paths in the browser. + /// + /// URLs use "/" (forward slash) as separators. Absolute paths either start + /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// `file://`) or with "/". + static final url = new _UrlStyle(); /// The name of this path style. Will be "posix" or "windows". - final String name; + String get name; /// The path separator for this style. On POSIX, this is `/`. On Windows, /// it's `\`. - final String separator; + String get separator; /// The [Pattern] that can be used to match a separator for a path in this - /// style. Windows allows both "/" and "\" as path separators even though - /// "\" is the canonical one. - final Pattern separatorPattern; + /// style. Windows allows both "/" and "\" as path separators even though "\" + /// is the canonical one. + Pattern get separatorPattern; - // TODO(nweiz): make this a Pattern when issue 7080 is fixed. - /// The [RegExp] that can be used to match the root prefix of an absolute + /// The [Pattern] that matches path components that need a separator after + /// them. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + Pattern get needsSeparatorPattern; + + /// The [Pattern] that can be used to match the root prefix of an absolute /// path in this style. - final RegExp _rootPattern; + Pattern get rootPattern; + + /// The [Pattern] that can be used to match the root prefix of a root-relative + /// path in this style. + /// + /// This can be null to indicate that this style doesn't support root-relative + /// paths. + final Pattern relativeRootPattern = null; /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. String getRoot(String path) { - var match = _rootPattern.firstMatch(path); + var match = rootPattern.firstMatch(path); + if (match != null) return match[0]; + return getRelativeRoot(path); + } + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path) { + if (relativeRootPattern == null) return null; + var match = relativeRootPattern.firstMatch(path); if (match == null) return null; return match[0]; } + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents [path]. + /// + /// Pathos will always path an absolute path for [path]. Relative paths are + /// handled automatically by [Builder]. + Uri pathToUri(String path); + String toString() => name; } +/// The style for POSIX paths. +class _PosixStyle extends Style { + _PosixStyle(); + + static final _builder = new Builder(style: Style.posix); + + final name = 'posix'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp(r'[^/]$'); + final rootPattern = new RegExp(r'^/'); + + String pathFromUri(Uri uri) { + if (uri.scheme == '' || uri.scheme == 'file') { + return Uri.decodeComponent(uri.path); + } + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + Uri pathToUri(String path) { + var parsed = _builder._parse(path); + + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "/"), [components] will + // currently be empty. We add two empty components so the URL constructor + // produces "file:///", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } +} + +/// The style for Windows paths. +class _WindowsStyle extends Style { + _WindowsStyle(); + + static final _builder = new Builder(style: Style.windows); + + final name = 'windows'; + final separator = '\\'; + final separatorPattern = new RegExp(r'[/\\]'); + final needsSeparatorPattern = new RegExp(r'[^/\\]$'); + final rootPattern = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])'); + + String pathFromUri(Uri uri) { + if (uri.scheme != '' && uri.scheme != 'file') { + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + var path = uri.path; + if (uri.host == '') { + // Drive-letter paths look like "file:///C:/path/to/file". The + // replaceFirst removes the extra initial slash. + if (path.startsWith('/')) path = path.replaceFirst("/", ""); + } else { + // Network paths look like "file://hostname/path/to/file". + path = '\\\\${uri.host}$path'; + } + return Uri.decodeComponent(path.replaceAll("/", "\\")); + } + + Uri pathToUri(String path) { + var parsed = _builder._parse(path); + if (parsed.root == r'\\') { + // Network paths become "file://hostname/path/to/file". + + var host = parsed.parts.removeAt(0); + + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will + // currently be empty. We add two empty components so the URL + // constructor produces "file://hostname/", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', host: host, pathSegments: parsed.parts); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently + // be empty. We add an empty component so the URL constructor produces + // "file:///C:/", with a trailing slash. We also add an empty component if + // the URL otherwise has a trailing slash. + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + parsed.parts.add(""); + } + + // Get rid of the trailing "\" in "C:\" because the URI constructor will + // add a separator on its own. + parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } + } +} + +/// The style for URL paths. +class _UrlStyle extends Style { + _UrlStyle(); + + final name = 'url'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp( + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = new RegExp(r"^/"); + + String pathFromUri(Uri uri) => uri.toString(); + + Uri pathToUri(String path) => Uri.parse(path); +} + // TODO(rnystrom): Make this public? class _ParsedPath { /// The [Style] that was used to parse this path. @@ -617,12 +1007,20 @@ class _ParsedPath { /// letters. String root; + /// Whether this path is root-relative. + /// + /// See [Builder.isRootRelative]. + bool isRootRelative; + /// The path-separated parts of the path. All but the last will be /// directories. List parts; - /// The path separators following each part. The last one will be an empty - /// string unless the path ends with a trailing separator. + /// The path separators preceding each part. + /// + /// The first one will be an empty string unless the root requires a separator + /// between it and the path. The last one will be an empty string unless the + /// path ends with a trailing separator. List separators; /// The file extension of the last part, or "" if it doesn't have one. @@ -631,7 +1029,8 @@ class _ParsedPath { /// `true` if this is an absolute path. bool get isAbsolute => root != null; - _ParsedPath(this.style, this.root, this.parts, this.separators); + _ParsedPath(this.style, this.root, this.isRootRelative, this.parts, + this.separators); String get basename { var copy = this.clone(); @@ -647,6 +1046,8 @@ class _ParsedPath { return copy._splitExtension()[0]; } + bool get hasTrailingSeparator => !parts.isEmpty && (parts.last == '' || separators.last != ''); + void removeTrailingSeparators() { while (!parts.isEmpty && parts.last == '') { parts.removeLast(); @@ -677,7 +1078,7 @@ class _ParsedPath { // A relative path can back out from the start directory. if (!isAbsolute) { - newParts.insertRange(0, leadingDoubles, '..'); + _growListFront(newParts, leadingDoubles, '..'); } // If we collapsed down to nothing, do ".". @@ -686,8 +1087,12 @@ class _ParsedPath { } // Canonicalize separators. - var newSeparators = []; - newSeparators.insertRange(0, newParts.length, style.separator); + var newSeparators = new List.generate( + newParts.length, (_) => style.separator, growable: true); + newSeparators.insert(0, + isAbsolute && newParts.length > 0 && + root.contains(style.needsSeparatorPattern) ? + style.separator : ''); parts = newParts; separators = newSeparators; @@ -701,11 +1106,12 @@ class _ParsedPath { String toString() { var builder = new StringBuffer(); - if (root != null) builder.add(root); + if (root != null) builder.write(root); for (var i = 0; i < parts.length; i++) { - builder.add(parts[i]); - builder.add(separators[i]); + builder.write(separators[i]); + builder.write(parts[i]); } + builder.write(separators.last); return builder.toString(); } @@ -729,5 +1135,6 @@ class _ParsedPath { } _ParsedPath clone() => new _ParsedPath( - style, root, new List.from(parts), new List.from(separators)); + style, root, isRootRelative, + new List.from(parts), new List.from(separators)); } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 3372302d..60fcec2c 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,10 +1,7 @@ -name: pathos +name: path author: "Dart Team " homepage: http://www.dartlang.org description: > A string-based path manipulation library. All of the path operations you know and love, with solid support on both Windows and POSIX (Linux and Mac OS X) machines. - - Currently only runs on the standalone VM, but will run in a browser as soon as - configuration-specific code is supported by Dart. diff --git a/pkgs/path/test/dart2js_test.dart b/pkgs/path/test/dart2js_test.dart new file mode 100644 index 00000000..997513de --- /dev/null +++ b/pkgs/path/test/dart2js_test.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; + +// In the future, the default root will be window.location.href, but right now +// that's not possible. + +main() { + group('new Builder()', () { + test('uses the current working directory if root is omitted', () { + var builder = new path.Builder(); + expect(builder.root, '.'); + }); + + test('uses URL if style is omitted', () { + var builder = new path.Builder(); + expect(builder.style, path.Style.url); + }); + }); + + test('current', () { + expect(path.current, '.'); + }); +} diff --git a/pkgs/path/test/dartium_test.dart b/pkgs/path/test/dartium_test.dart new file mode 100644 index 00000000..2a830c75 --- /dev/null +++ b/pkgs/path/test/dartium_test.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:html'; + +import 'package:unittest/unittest.dart'; +import 'package:unittest/html_config.dart'; +import 'package:path/path.dart' as path; + +main() { + useHtmlConfiguration(); + + group('new Builder()', () { + test('uses the current working directory if root is omitted', () { + var builder = new path.Builder(); + expect(builder.root, window.location.href); + }); + + test('uses URL if style is omitted', () { + var builder = new path.Builder(); + expect(builder.style, path.Style.url); + }); + }); + + test('current', () { + expect(path.current, window.location.href); + }); +} diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart new file mode 100644 index 00000000..fc29ced6 --- /dev/null +++ b/pkgs/path/test/io_test.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; + +main() { + group('new Builder()', () { + test('uses the current working directory if root is omitted', () { + var builder = new path.Builder(); + expect(builder.root, io.Directory.current.path); + }); + + test('uses the host OS if style is omitted', () { + var builder = new path.Builder(); + if (io.Platform.operatingSystem == 'windows') { + expect(builder.style, path.Style.windows); + } else { + expect(builder.style, path.Style.posix); + } + }); + }); + + test('current', () { + expect(path.current, io.Directory.current.path); + }); +} diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index eea906ab..ef3cf21e 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -2,10 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library all_test; - -import 'dart:io' as io; - import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; @@ -37,23 +33,5 @@ main() { var builder = new path.Builder(style: path.Style.windows); expect(builder.style, path.Style.windows); }); - - test('uses the current working directory if root is omitted', () { - var builder = new path.Builder(); - expect(builder.root, new io.Directory.current().path); - }); - - test('uses the host OS if style is omitted', () { - var builder = new path.Builder(); - if (io.Platform.operatingSystem == 'windows') { - expect(builder.style, path.Style.windows); - } else { - expect(builder.style, path.Style.posix); - } - }); - }); - - test('current', () { - expect(path.current, new io.Directory.current().path); }); } diff --git a/pkgs/path/test/path_posix_test.dart b/pkgs/path/test/posix_test.dart similarity index 91% rename from pkgs/path/test/path_posix_test.dart rename to pkgs/path/test/posix_test.dart index 763035fe..f3232bc8 100644 --- a/pkgs/path/test/path_posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -2,9 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path_test; - -import 'dart:io' as io; +library path.test.posix_test; import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; @@ -300,6 +298,12 @@ main() { expect(builder.relative('../a/b.txt'), '../a/b.txt'); expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); }); + + // Regression + test('from root-only path', () { + expect(builder.relative('/', from: '/'), '.'); + expect(builder.relative('/root/path', from: '/'), 'root/path'); + }); }); group('from relative root', () { @@ -344,6 +348,12 @@ main() { equals('/foo/bar/baz')); expect(r.relative('..', from: 'foo/bar'), equals('../../..')); }); + + test('from a . root', () { + var r = new path.Builder(style: path.Style.posix, root: '.'); + expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); + expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); + }); }); group('resolve', () { @@ -389,4 +399,26 @@ main() { expect(builder.withoutExtension('a/b.c/'), 'a/b/'); expect(builder.withoutExtension('a/b.c//'), 'a/b//'); }); + + test('fromUri', () { + expect(builder.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); + expect(builder.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); + expect(builder.fromUri(Uri.parse('file:///')), '/'); + expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(builder.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); + expect(builder.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); + expect(builder.fromUri(Uri.parse('file:///path/to/foo%23bar')), + '/path/to/foo#bar'); + expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + throwsArgumentError); + }); + + test('toUri', () { + expect(builder.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); + expect(builder.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); + expect(builder.toUri('/'), Uri.parse('file:///')); + expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(builder.toUri('/path/to/foo#bar'), + Uri.parse('file:///path/to/foo%23bar')); + }); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart new file mode 100644 index 00000000..3e03d509 --- /dev/null +++ b/pkgs/path/test/url_test.dart @@ -0,0 +1,653 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:unittest/unittest.dart'; +import 'package:path/path.dart' as path; + +main() { + var builder = new path.Builder(style: path.Style.url, + root: 'http://dartlang.org/root/path'); + + test('separator', () { + expect(builder.separator, '/'); + }); + + test('extension', () { + expect(builder.extension(''), ''); + expect(builder.extension('foo.dart'), '.dart'); + expect(builder.extension('foo.dart.js'), '.js'); + expect(builder.extension('a.b/c'), ''); + expect(builder.extension('a.b/c.d'), '.d'); + expect(builder.extension(r'a.b\c'), r'.b\c'); + }); + + test('rootPrefix', () { + expect(builder.rootPrefix(''), ''); + expect(builder.rootPrefix('a'), ''); + expect(builder.rootPrefix('a/b'), ''); + expect(builder.rootPrefix('http://dartlang.org/a/c'), + 'http://dartlang.org'); + expect(builder.rootPrefix('file:///a/c'), 'file://'); + expect(builder.rootPrefix('/a/c'), '/'); + expect(builder.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); + expect(builder.rootPrefix('file:///'), 'file://'); + expect(builder.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); + expect(builder.rootPrefix('file://'), 'file://'); + expect(builder.rootPrefix('/'), '/'); + }); + + test('dirname', () { + expect(builder.dirname(''), '.'); + expect(builder.dirname('a'), '.'); + expect(builder.dirname('a/b'), 'a'); + expect(builder.dirname('a/b/c'), 'a/b'); + expect(builder.dirname('a/b.c'), 'a'); + expect(builder.dirname('a/'), '.'); + expect(builder.dirname('a/.'), 'a'); + expect(builder.dirname(r'a\b/c'), r'a\b'); + expect(builder.dirname('http://dartlang.org/a'), 'http://dartlang.org'); + expect(builder.dirname('file:///a'), 'file://'); + expect(builder.dirname('/a'), '/'); + expect(builder.dirname('http://dartlang.org///a'), 'http://dartlang.org'); + expect(builder.dirname('file://///a'), 'file://'); + expect(builder.dirname('///a'), '/'); + expect(builder.dirname('http://dartlang.org/'), 'http://dartlang.org'); + expect(builder.dirname('http://dartlang.org'), 'http://dartlang.org'); + expect(builder.dirname('file:///'), 'file://'); + expect(builder.dirname('file://'), 'file://'); + expect(builder.dirname('/'), '/'); + expect(builder.dirname('http://dartlang.org///'), 'http://dartlang.org'); + expect(builder.dirname('file://///'), 'file://'); + expect(builder.dirname('///'), '/'); + expect(builder.dirname('a/b/'), 'a'); + expect(builder.dirname(r'a/b\c'), 'a'); + expect(builder.dirname('a//'), '.'); + expect(builder.dirname('a/b//'), 'a'); + expect(builder.dirname('a//b'), 'a'); + }); + + test('basename', () { + expect(builder.basename(''), ''); + expect(builder.basename('a'), 'a'); + expect(builder.basename('a/b'), 'b'); + expect(builder.basename('a/b/c'), 'c'); + expect(builder.basename('a/b.c'), 'b.c'); + expect(builder.basename('a/'), 'a'); + expect(builder.basename('a/.'), '.'); + expect(builder.basename(r'a\b/c'), 'c'); + expect(builder.basename('http://dartlang.org/a'), 'a'); + expect(builder.basename('file:///a'), 'a'); + expect(builder.basename('/a'), 'a'); + expect(builder.basename('http://dartlang.org/'), 'http://dartlang.org'); + expect(builder.basename('http://dartlang.org'), 'http://dartlang.org'); + expect(builder.basename('file:///'), 'file://'); + expect(builder.basename('file://'), 'file://'); + expect(builder.basename('/'), '/'); + expect(builder.basename('a/b/'), 'b'); + expect(builder.basename(r'a/b\c'), r'b\c'); + expect(builder.basename('a//'), 'a'); + expect(builder.basename('a/b//'), 'b'); + expect(builder.basename('a//b'), 'b'); + }); + + test('basenameWithoutExtension', () { + expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('a'), 'a'); + expect(builder.basenameWithoutExtension('a/b'), 'b'); + expect(builder.basenameWithoutExtension('a/b/c'), 'c'); + expect(builder.basenameWithoutExtension('a/b.c'), 'b'); + expect(builder.basenameWithoutExtension('a/'), 'a'); + expect(builder.basenameWithoutExtension('a/.'), '.'); + expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(builder.basenameWithoutExtension('a//'), 'a'); + expect(builder.basenameWithoutExtension('a/b//'), 'b'); + expect(builder.basenameWithoutExtension('a//b'), 'b'); + expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); + expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); + }); + + test('isAbsolute', () { + expect(builder.isAbsolute(''), false); + expect(builder.isAbsolute('a'), false); + expect(builder.isAbsolute('a/b'), false); + expect(builder.isAbsolute('http://dartlang.org/a'), true); + expect(builder.isAbsolute('file:///a'), true); + expect(builder.isAbsolute('/a'), true); + expect(builder.isAbsolute('http://dartlang.org/a/b'), true); + expect(builder.isAbsolute('file:///a/b'), true); + expect(builder.isAbsolute('/a/b'), true); + expect(builder.isAbsolute('http://dartlang.org/'), true); + expect(builder.isAbsolute('file:///'), true); + expect(builder.isAbsolute('http://dartlang.org'), true); + expect(builder.isAbsolute('file://'), true); + expect(builder.isAbsolute('/'), true); + expect(builder.isAbsolute('~'), false); + expect(builder.isAbsolute('.'), false); + expect(builder.isAbsolute('../a'), false); + expect(builder.isAbsolute('C:/a'), false); + expect(builder.isAbsolute(r'C:\a'), false); + expect(builder.isAbsolute(r'\\a'), false); + }); + + test('isRelative', () { + expect(builder.isRelative(''), true); + expect(builder.isRelative('a'), true); + expect(builder.isRelative('a/b'), true); + expect(builder.isRelative('http://dartlang.org/a'), false); + expect(builder.isRelative('file:///a'), false); + expect(builder.isRelative('/a'), false); + expect(builder.isRelative('http://dartlang.org/a/b'), false); + expect(builder.isRelative('file:///a/b'), false); + expect(builder.isRelative('/a/b'), false); + expect(builder.isRelative('http://dartlang.org/'), false); + expect(builder.isRelative('file:///'), false); + expect(builder.isRelative('http://dartlang.org'), false); + expect(builder.isRelative('file://'), false); + expect(builder.isRelative('/'), false); + expect(builder.isRelative('~'), true); + expect(builder.isRelative('.'), true); + expect(builder.isRelative('../a'), true); + expect(builder.isRelative('C:/a'), true); + expect(builder.isRelative(r'C:\a'), true); + expect(builder.isRelative(r'\\a'), true); + }); + + test('isRootRelative', () { + expect(builder.isRootRelative(''), false); + expect(builder.isRootRelative('a'), false); + expect(builder.isRootRelative('a/b'), false); + expect(builder.isRootRelative('http://dartlang.org/a'), false); + expect(builder.isRootRelative('file:///a'), false); + expect(builder.isRootRelative('/a'), true); + expect(builder.isRootRelative('http://dartlang.org/a/b'), false); + expect(builder.isRootRelative('file:///a/b'), false); + expect(builder.isRootRelative('/a/b'), true); + expect(builder.isRootRelative('http://dartlang.org/'), false); + expect(builder.isRootRelative('file:///'), false); + expect(builder.isRootRelative('http://dartlang.org'), false); + expect(builder.isRootRelative('file://'), false); + expect(builder.isRootRelative('/'), true); + expect(builder.isRootRelative('~'), false); + expect(builder.isRootRelative('.'), false); + expect(builder.isRootRelative('../a'), false); + expect(builder.isRootRelative('C:/a'), false); + expect(builder.isRootRelative(r'C:\a'), false); + expect(builder.isRootRelative(r'\\a'), false); + }); + + group('join', () { + test('allows up to eight parts', () { + expect(builder.join('a'), 'a'); + expect(builder.join('a', 'b'), 'a/b'); + expect(builder.join('a', 'b', 'c'), 'a/b/c'); + expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + 'a/b/c/d/e/f/g/h'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(builder.join('a\\', 'b'), r'a\/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.join('a', 'http://dartlang.org', 'b', 'c'), + 'http://dartlang.org/b/c'); + expect(builder.join('a', 'file://', 'b', 'c'), 'file:///b/c'); + expect(builder.join('a', '/', 'b', 'c'), '/b/c'); + expect(builder.join('a', '/b', 'http://dartlang.org/c', 'd'), + 'http://dartlang.org/c/d'); + expect(builder.join( + 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), + 'http://dartlang.org/c/d'); + expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); + expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + }); + + test('preserves roots before a root-relative path', () { + expect(builder.join('http://dartlang.org', 'a', '/b', 'c'), + 'http://dartlang.org/b/c'); + expect(builder.join('file://', 'a', '/b', 'c'), 'file:///b/c'); + expect(builder.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); + }); + + test('ignores trailing nulls', () { + expect(builder.join('a', null), equals('a')); + expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + }); + + test('disallows intermediate nulls', () { + expect(() => builder.join('a', null, 'b'), throwsArgumentError); + expect(() => builder.join(null, 'a'), throwsArgumentError); + }); + }); + + group('joinAll', () { + test('allows more than eight parts', () { + expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + 'a/b/c/d/e/f/g/h/i'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.joinAll(['a', 'http://dartlang.org', 'b', 'c']), + 'http://dartlang.org/b/c'); + expect(builder.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); + expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(builder.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), + 'http://dartlang.org/c/d'); + expect(builder.joinAll( + ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), + 'http://dartlang.org/c/d'); + expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + }); + + test('preserves roots before a root-relative path', () { + expect(builder.joinAll(['http://dartlang.org', 'a', '/b', 'c']), + 'http://dartlang.org/b/c'); + expect(builder.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); + expect(builder.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); + }); + }); + + group('split', () { + test('simple cases', () { + expect(builder.split(''), []); + expect(builder.split('.'), ['.']); + expect(builder.split('..'), ['..']); + expect(builder.split('foo'), equals(['foo'])); + expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split('foo/../bar/./baz'), + equals(['foo', '..', 'bar', '.', 'baz'])); + expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(builder.split('.'), equals(['.'])); + expect(builder.split(''), equals([])); + expect(builder.split('foo/'), equals(['foo'])); + expect(builder.split('http://dartlang.org//'), + equals(['http://dartlang.org'])); + expect(builder.split('file:////'), equals(['file://'])); + expect(builder.split('//'), equals(['/'])); + }); + + test('includes the root for absolute paths', () { + expect(builder.split('http://dartlang.org/foo/bar/baz'), + equals(['http://dartlang.org', 'foo', 'bar', 'baz'])); + expect(builder.split('file:///foo/bar/baz'), + equals(['file://', 'foo', 'bar', 'baz'])); + expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(builder.split('http://dartlang.org/'), + equals(['http://dartlang.org'])); + expect(builder.split('http://dartlang.org'), + equals(['http://dartlang.org'])); + expect(builder.split('file:///'), equals(['file://'])); + expect(builder.split('file://'), equals(['file://'])); + expect(builder.split('/'), equals(['/'])); + }); + }); + + group('normalize', () { + test('simple cases', () { + expect(builder.normalize(''), ''); + expect(builder.normalize('.'), '.'); + expect(builder.normalize('..'), '..'); + expect(builder.normalize('a'), 'a'); + expect(builder.normalize('http://dartlang.org/'), 'http://dartlang.org'); + expect(builder.normalize('http://dartlang.org'), 'http://dartlang.org'); + expect(builder.normalize('file://'), 'file://'); + expect(builder.normalize('file:///'), 'file://'); + expect(builder.normalize('/'), '/'); + expect(builder.normalize(r'\'), r'\'); + }); + + test('collapses redundant separators', () { + expect(builder.normalize(r'a/b/c'), r'a/b/c'); + expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + }); + + test('does not collapse separators for other platform', () { + expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + }); + + test('eliminates "." parts', () { + expect(builder.normalize('./'), '.'); + expect(builder.normalize('http://dartlang.org/.'), 'http://dartlang.org'); + expect(builder.normalize('file:///.'), 'file://'); + expect(builder.normalize('/.'), '/'); + expect(builder.normalize('http://dartlang.org/./'), + 'http://dartlang.org'); + expect(builder.normalize('file:///./'), 'file://'); + expect(builder.normalize('/./'), '/'); + expect(builder.normalize('./.'), '.'); + expect(builder.normalize('a/./b'), 'a/b'); + expect(builder.normalize('a/.b/c'), 'a/.b/c'); + expect(builder.normalize('a/././b/./c'), 'a/b/c'); + expect(builder.normalize('././a'), 'a'); + expect(builder.normalize('a/./.'), 'a'); + }); + + test('eliminates ".." parts', () { + expect(builder.normalize('..'), '..'); + expect(builder.normalize('../'), '..'); + expect(builder.normalize('../../..'), '../../..'); + expect(builder.normalize('../../../'), '../../..'); + expect(builder.normalize('http://dartlang.org/..'), + 'http://dartlang.org'); + expect(builder.normalize('file:///..'), 'file://'); + expect(builder.normalize('/..'), '/'); + expect(builder.normalize('http://dartlang.org/../../..'), + 'http://dartlang.org'); + expect(builder.normalize('file:///../../..'), 'file://'); + expect(builder.normalize('/../../..'), '/'); + expect(builder.normalize('http://dartlang.org/../../../a'), + 'http://dartlang.org/a'); + expect(builder.normalize('file:///../../../a'), 'file:///a'); + expect(builder.normalize('/../../../a'), '/a'); + expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('a/../b'), 'b'); + expect(builder.normalize('a/./../b'), 'b'); + expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(builder.normalize('a/b/../../../../c'), '../../c'); + }); + + test('does not walk before root on absolute paths', () { + expect(builder.normalize('..'), '..'); + expect(builder.normalize('../'), '..'); + expect(builder.normalize('http://dartlang.org/..'), + 'http://dartlang.org'); + expect(builder.normalize('file:///..'), 'file://'); + expect(builder.normalize('/..'), '/'); + expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('a/../b'), 'b'); + expect(builder.normalize('a/./../b'), 'b'); + expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(builder.normalize('a/b/../../../../c'), '../../c'); + }); + + test('removes trailing separators', () { + expect(builder.normalize('./'), '.'); + expect(builder.normalize('.//'), '.'); + expect(builder.normalize('a/'), 'a'); + expect(builder.normalize('a/b/'), 'a/b'); + expect(builder.normalize('a/b///'), 'a/b'); + }); + }); + + group('relative', () { + group('from absolute root', () { + test('given absolute path in root', () { + expect(builder.relative('http://dartlang.org'), '../..'); + expect(builder.relative('http://dartlang.org/'), '../..'); + expect(builder.relative('/'), '../..'); + expect(builder.relative('http://dartlang.org/root'), '..'); + expect(builder.relative('/root'), '..'); + expect(builder.relative('http://dartlang.org/root/path'), '.'); + expect(builder.relative('/root/path'), '.'); + expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); + expect(builder.relative('/root/path/a'), 'a'); + expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + 'a/b.txt'); + expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(builder.relative('http://dartlang.org/root/a/b.txt'), + '../a/b.txt'); + expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + }); + + test('given absolute path outside of root', () { + expect(builder.relative('http://dartlang.org/a/b'), '../../a/b'); + expect(builder.relative('/a/b'), '../../a/b'); + expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); + expect(builder.relative('/root/path/a'), 'a'); + expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + 'a/b.txt'); + expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + 'a/b.txt'); + expect(builder.relative('http://dartlang.org/root/a/b.txt'), + '../a/b.txt'); + }); + + test('given absolute path with different hostname/protocol', () { + expect(builder.relative(r'http://google.com/a/b'), + r'http://google.com/a/b'); + expect(builder.relative(r'file:///a/b'), + r'file:///a/b'); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(builder.relative(''), '.'); + expect(builder.relative('.'), '.'); + expect(builder.relative('a'), 'a'); + expect(builder.relative('a/b.txt'), 'a/b.txt'); + expect(builder.relative('../a/b.txt'), '../a/b.txt'); + expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + }); + + // Regression + test('from root-only path', () { + expect(builder.relative('http://dartlang.org', + from: 'http://dartlang.org'), + '.'); + expect(builder.relative('http://dartlang.org/root/path', + from: 'http://dartlang.org'), + 'root/path'); + }); + }); + + group('from relative root', () { + var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); + + test('given absolute path', () { + expect(r.relative('http://google.com/'), equals('http://google.com')); + expect(r.relative('http://google.com'), equals('http://google.com')); + expect(r.relative('file:///'), equals('file://')); + expect(r.relative('file://'), equals('file://')); + expect(r.relative('/'), equals('/')); + expect(r.relative('/a/b'), equals('/a/b')); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(r.relative(''), '.'); + expect(r.relative('.'), '.'); + expect(r.relative('..'), '..'); + expect(r.relative('a'), 'a'); + expect(r.relative('a/b.txt'), 'a/b.txt'); + expect(r.relative('../a/b.txt'), '../a/b.txt'); + expect(r.relative('a/./b/../c.txt'), 'a/c.txt'); + }); + }); + + group('from root-relative root', () { + var r = new path.Builder(style: path.Style.url, root: '/foo/bar'); + + test('given absolute path', () { + expect(r.relative('http://google.com/'), equals('http://google.com')); + expect(r.relative('http://google.com'), equals('http://google.com')); + expect(r.relative('file:///'), equals('file://')); + expect(r.relative('file://'), equals('file://')); + expect(r.relative('/'), equals('../..')); + expect(r.relative('/a/b'), equals('../../a/b')); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(r.relative(''), '.'); + expect(r.relative('.'), '.'); + expect(r.relative('..'), '..'); + expect(r.relative('a'), 'a'); + expect(r.relative('a/b.txt'), 'a/b.txt'); + expect(r.relative('../a/b.txt'), '../a/b.txt'); + expect(r.relative('a/./b/../c.txt'), 'a/c.txt'); + }); + }); + + test('from a root with extension', () { + var r = new path.Builder(style: path.Style.url, root: '/dir.ext'); + expect(r.relative('/dir.ext/file'), 'file'); + }); + + test('with a root parameter', () { + expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect( + builder.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + equals('baz')); + expect( + builder.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + equals('baz')); + expect(builder.relative('http://dartlang.org/foo/bar/baz', + from: 'file:///foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect(builder.relative('http://dartlang.org/foo/bar/baz', + from: 'http://dartlang.org/foo/bar'), equals('baz')); + expect( + builder.relative('/foo/bar/baz', from: 'file:///foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect( + builder.relative('file:///foo/bar/baz', from: '/foo/bar'), + equals('file:///foo/bar/baz')); + + expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); + expect(builder.relative('..', from: 'http://dartlang.org/foo/bar'), + equals('../../root')); + expect(builder.relative('..', from: 'file:///foo/bar'), + equals('http://dartlang.org/root')); + expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); + + expect(builder.relative('http://dartlang.org/foo/bar/baz', + from: 'foo/bar'), + equals('../../../../foo/bar/baz')); + expect(builder.relative('file:///foo/bar/baz', from: 'foo/bar'), + equals('file:///foo/bar/baz')); + expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + equals('../../../../foo/bar/baz')); + + expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + }); + + test('with a root parameter and a relative root', () { + var r = new path.Builder(style: path.Style.url, root: 'relative/root'); + expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect( + r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + equals('/foo/bar/baz')); + expect( + r.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('http://dartlang.org/foo/bar/baz', + from: 'file:///foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('http://dartlang.org/foo/bar/baz', + from: 'http://dartlang.org/foo/bar'), equals('baz')); + + expect(r.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('file:///foo/bar/baz', from: 'foo/bar'), + equals('file:///foo/bar/baz')); + expect(r.relative('/foo/bar/baz', from: 'foo/bar'), + equals('/foo/bar/baz')); + + expect(r.relative('..', from: 'foo/bar'), equals('../../..')); + }); + + test('from a . root', () { + var r = new path.Builder(style: path.Style.url, root: '.'); + expect(r.relative('http://dartlang.org/foo/bar/baz'), + equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); + expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); + expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); + }); + }); + + group('resolve', () { + test('allows up to seven parts', () { + expect(builder.resolve('a'), 'http://dartlang.org/root/path/a'); + expect(builder.resolve('a', 'b'), 'http://dartlang.org/root/path/a/b'); + expect(builder.resolve('a', 'b', 'c'), + 'http://dartlang.org/root/path/a/b/c'); + expect(builder.resolve('a', 'b', 'c', 'd'), + 'http://dartlang.org/root/path/a/b/c/d'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + 'http://dartlang.org/root/path/a/b/c/d/e'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + 'http://dartlang.org/root/path/a/b/c/d/e/f'); + expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + 'http://dartlang.org/root/path/a/b/c/d/e/f/g'); + }); + + test('does not add separator if a part ends in one', () { + expect(builder.resolve('a/', 'b', 'c/', 'd'), + 'http://dartlang.org/root/path/a/b/c/d'); + expect(builder.resolve(r'a\', 'b'), + r'http://dartlang.org/root/path/a\/b'); + }); + + test('ignores parts before an absolute path', () { + expect(builder.resolve('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); + expect(builder.resolve('a', '/b', 'file:///c', 'd'), 'file:///c/d'); + expect(builder.resolve('a', r'c:\b', 'c', 'd'), + r'http://dartlang.org/root/path/a/c:\b/c/d'); + expect(builder.resolve('a', r'\\b', 'c', 'd'), + r'http://dartlang.org/root/path/a/\\b/c/d'); + }); + }); + + test('withoutExtension', () { + expect(builder.withoutExtension(''), ''); + expect(builder.withoutExtension('a'), 'a'); + expect(builder.withoutExtension('.a'), '.a'); + expect(builder.withoutExtension('a.b'), 'a'); + expect(builder.withoutExtension('a/b.c'), 'a/b'); + expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(builder.withoutExtension('a/'), 'a/'); + expect(builder.withoutExtension('a/b/'), 'a/b/'); + expect(builder.withoutExtension('a/.'), 'a/.'); + expect(builder.withoutExtension('a/.b'), 'a/.b'); + expect(builder.withoutExtension('a.b/c'), 'a.b/c'); + expect(builder.withoutExtension(r'a.b\c'), r'a'); + expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(builder.withoutExtension('a/b.c/'), 'a/b/'); + expect(builder.withoutExtension('a/b.c//'), 'a/b//'); + }); + + + test('fromUri', () { + expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), + 'http://dartlang.org/path/to/foo'); + expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), + 'http://dartlang.org/path/to/foo/'); + expect(builder.fromUri(Uri.parse('file:///path/to/foo')), + 'file:///path/to/foo'); + expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), + 'http://dartlang.org/path/to/foo%23bar'); + }); + + test('toUri', () { + expect(builder.toUri('http://dartlang.org/path/to/foo'), + Uri.parse('http://dartlang.org/path/to/foo')); + expect(builder.toUri('http://dartlang.org/path/to/foo/'), + Uri.parse('http://dartlang.org/path/to/foo/')); + expect(builder.toUri('file:///path/to/foo'), + Uri.parse('file:///path/to/foo')); + expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(builder.toUri('http://dartlang.org/path/to/foo%23bar'), + Uri.parse('http://dartlang.org/path/to/foo%23bar')); + }); +} diff --git a/pkgs/path/test/path_windows_test.dart b/pkgs/path/test/windows_test.dart similarity index 89% rename from pkgs/path/test/path_windows_test.dart rename to pkgs/path/test/windows_test.dart index 8164224d..7b3cf600 100644 --- a/pkgs/path/test/path_windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -2,9 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path_test; - -import 'dart:io' as io; +library path.test.windows_test; import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; @@ -333,6 +331,12 @@ main() { expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt'); expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); }); + + // Regression + test('from root-only path', () { + expect(builder.relative(r'C:\', from: r'C:\'), '.'); + expect(builder.relative(r'C:\root\path', from: r'C:\'), r'root\path'); + }); }); group('from relative root', () { @@ -385,6 +389,12 @@ main() { expect(builder.relative(r'D:\a\b'), r'D:\a\b'); expect(builder.relative(r'\\a\b'), r'\\a\b'); }); + + test('from a . root', () { + var r = new path.Builder(style: path.Style.windows, root: '.'); + expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); + expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); + }); }); group('resolve', () { @@ -431,4 +441,38 @@ main() { expect(builder.withoutExtension(r'a.b/c'), r'a.b/c'); expect(builder.withoutExtension(r'a\b.c\'), r'a\b\'); }); + + test('fromUri', () { + expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo')), + r'C:\path\to\foo'); + expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo')), + r'\\hostname\path\to\foo'); + expect(builder.fromUri(Uri.parse('file:///C:/')), r'C:\'); + expect(builder.fromUri(Uri.parse('file://hostname/')), r'\\hostname\'); + expect(builder.fromUri(Uri.parse('foo/bar')), r'foo\bar'); + expect(builder.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); + expect(builder.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); + expect(builder.fromUri(Uri.parse('//hostname/path/to/foo')), + r'\\hostname\path\to\foo'); + expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), + r'C:\path\to\foo#bar'); + expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo%23bar')), + r'\\hostname\path\to\foo#bar'); + expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + throwsArgumentError); + }); + + test('toUri', () { + expect(builder.toUri(r'C:\path\to\foo'), + Uri.parse('file:///C:/path/to/foo')); + expect(builder.toUri(r'C:\path\to\foo\'), + Uri.parse('file:///C:/path/to/foo/')); + expect(builder.toUri(r'C:\'), Uri.parse('file:///C:/')); + expect(builder.toUri(r'\\hostname\'), Uri.parse('file://hostname/')); + expect(builder.toUri(r'foo\bar'), Uri.parse('foo/bar')); + expect(builder.toUri(r'C:\path\to\foo#bar'), + Uri.parse('file:///C:/path/to/foo%23bar')); + expect(builder.toUri(r'\\hostname\path\to\foo#bar'), + Uri.parse('file://hostname/path/to/foo%23bar')); + }); } From 79b7f0e21afdd1942ad26461ec03590c8921e5f4 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Mon, 15 Jul 2013 23:28:44 +0000 Subject: [PATCH 014/183] Test empty strings arguments to path.join(). BUG=https://code.google.com/p/dart/issues/detail?id=11819 R=nweiz@google.com, whesse@google.com Review URL: https://codereview.chromium.org//19219002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25033 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- pkgs/path/test/posix_test.dart | 8 ++++++++ pkgs/path/test/url_test.dart | 8 ++++++++ pkgs/path/test/windows_test.dart | 8 ++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index b425124c..25ce0e6f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -525,7 +525,7 @@ class Builder { var needsSeparator = false; var isAbsoluteAndNotRootRelative = false; - for (var part in parts) { + for (var part in parts.where((part) => part != '')) { if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { // If the new part is root-relative, it preserves the previous root but // replaces the path after it. diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index f3232bc8..c26a5ba8 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -153,6 +153,14 @@ main() { expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); }); + test('ignores empty strings', () { + expect(builder.join(''), ''); + expect(builder.join('', ''), ''); + expect(builder.join('', 'a'), 'a'); + expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(builder.join('a', 'b', ''), 'a/b'); + }); + test('disallows intermediate nulls', () { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 3e03d509..d040a598 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -223,6 +223,14 @@ main() { expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); }); + test('ignores empty strings', () { + expect(builder.join(''), ''); + expect(builder.join('', ''), ''); + expect(builder.join('', 'a'), 'a'); + expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(builder.join('a', 'b', ''), 'a/b'); + }); + test('disallows intermediate nulls', () { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 7b3cf600..74094397 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -172,6 +172,14 @@ main() { expect(builder.join('a', 'b', 'c', null, null), equals(r'a\b\c')); }); + test('ignores empty strings', () { + expect(builder.join(''), ''); + expect(builder.join('', ''), ''); + expect(builder.join('', 'a'), 'a'); + expect(builder.join('a', '', 'b', '', '', '', 'c'), r'a\b\c'); + expect(builder.join('a', 'b', ''), r'a\b'); + }); + test('disallows intermediate nulls', () { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); From cf0b8be737a8b296d667f3f3aeb522554f57ecf3 Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Wed, 17 Jul 2013 19:28:45 +0000 Subject: [PATCH 015/183] pkg/path: fixed tiny doc bug path.dirname('path/to') returns 'path', not 'to' R=rnystrom@google.com Review URL: https://codereview.chromium.org//19492003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25120 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 25ce0e6f..1d83c00c 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -109,7 +109,7 @@ String basenameWithoutExtension(String path) => /// Gets the part of [path] before the last separator. /// /// path.dirname('path/to/foo.dart'); // -> 'path/to' -/// path.dirname('path/to'); // -> 'to' +/// path.dirname('path/to'); // -> 'path' /// /// Trailing separators are ignored. /// From d4f65c43cacf38a7f344c68e493db6e60f8ae02c Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Mon, 22 Jul 2013 21:06:37 +0000 Subject: [PATCH 016/183] Use window.location.href in path under dart2js. R=rnystrom@google.com Review URL: https://codereview.chromium.org//19939004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25304 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 20 +++++--------- .../{dartium_test.dart => browser_test.dart} | 0 pkgs/path/test/dart2js_test.dart | 27 ------------------- 3 files changed, 6 insertions(+), 41 deletions(-) rename pkgs/path/test/{dartium_test.dart => browser_test.dart} (100%) delete mode 100644 pkgs/path/test/dart2js_test.dart diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 1d83c00c..3d533e10 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -20,6 +20,9 @@ /// [pkg]: http://pub.dartlang.org/packages/path library path; +@MirrorsUsed(targets: 'dart.dom.html.window, ' + 'dart.io.Directory.current, ' + 'dart.io.Platform.operatingSystem') import 'dart:mirrors'; /// An internal builder for the current OS so we can provide a straight @@ -37,26 +40,15 @@ void _growListFront(List list, int length, fillValue) => /// [LibraryMirror] that gives access to the `dart:io` library. /// /// If `dart:io` is not available, this returns null. -LibraryMirror get _io { - try { - return currentMirrorSystem().libraries[Uri.parse('dart:io')]; - } catch (_) { - return null; - } -} +LibraryMirror get _io => currentMirrorSystem().libraries[Uri.parse('dart:io')]; // TODO(nweiz): when issue 6490 or 6943 are fixed, make this work under dart2js. /// If we're running in Dartium, this will return a [LibraryMirror] that gives /// access to the `dart:html` library. /// /// If `dart:html` is not available, this returns null. -LibraryMirror get _html { - try { - return currentMirrorSystem().libraries[Uri.parse('dart:html')]; - } catch (_) { - return null; - } -} +LibraryMirror get _html => + currentMirrorSystem().libraries[Uri.parse('dart:html')]; /// Gets the path to the current working directory. /// diff --git a/pkgs/path/test/dartium_test.dart b/pkgs/path/test/browser_test.dart similarity index 100% rename from pkgs/path/test/dartium_test.dart rename to pkgs/path/test/browser_test.dart diff --git a/pkgs/path/test/dart2js_test.dart b/pkgs/path/test/dart2js_test.dart deleted file mode 100644 index 997513de..00000000 --- a/pkgs/path/test/dart2js_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:unittest/unittest.dart'; -import 'package:path/path.dart' as path; - -// In the future, the default root will be window.location.href, but right now -// that's not possible. - -main() { - group('new Builder()', () { - test('uses the current working directory if root is omitted', () { - var builder = new path.Builder(); - expect(builder.root, '.'); - }); - - test('uses URL if style is omitted', () { - var builder = new path.Builder(); - expect(builder.style, path.Style.url); - }); - }); - - test('current', () { - expect(path.current, '.'); - }); -} From e1e95f072b90b12b8b11a22658b2a0ebd01b7f38 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 23 Jul 2013 20:09:31 +0000 Subject: [PATCH 017/183] Fix static warnings in pkg/path. BUG=https://code.google.com/p/dart/issues/detail?id=11856 R=nweiz@google.com Review URL: https://codereview.chromium.org//19851005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25361 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 3d533e10..1845e096 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -757,8 +757,8 @@ class Builder { var parts = []; var separators = []; - var firstSeparator = style.separatorPattern.firstMatch(path); - if (firstSeparator != null && firstSeparator.start == 0) { + var firstSeparator = style.separatorPattern.matchAsPrefix(path); + if (firstSeparator != null) { separators.add(firstSeparator[0]); path = path.substring(firstSeparator[0].length); } else { @@ -839,8 +839,9 @@ abstract class Style { /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. String getRoot(String path) { - var match = rootPattern.firstMatch(path); - if (match != null) return match[0]; + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = rootPattern.allMatches(path); + if (matches.isNotEmpty) return matches.first[0]; return getRelativeRoot(path); } @@ -849,9 +850,10 @@ abstract class Style { /// If [path] is relative or absolute and not root-relative, returns `null`. String getRelativeRoot(String path) { if (relativeRootPattern == null) return null; - var match = relativeRootPattern.firstMatch(path); - if (match == null) return null; - return match[0]; + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = relativeRootPattern.allMatches(path); + if (matches.isEmpty) return null; + return matches.first[0]; } /// Returns the path represented by [uri] in this style. From 8e2e11f99694c131fd8adedcadd378c6e02007ac Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 23 Jul 2013 20:37:57 +0000 Subject: [PATCH 018/183] Fix documentation of path.toUri(). R=nweiz@google.com Review URL: https://codereview.chromium.org//20043004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25366 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 1845e096..cc0493ce 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -289,9 +289,6 @@ String fromUri(Uri uri) => _builder.fromUri(uri); /// For POSIX and Windows styles, this will return a `file:` URI. For the URL /// style, this will just convert [path] to a [Uri]. /// -/// This will always convert relative paths to absolute ones before converting -/// to a URI. -/// /// // POSIX /// path.toUri('/path/to/foo') /// // -> Uri.parse('file:///path/to/foo') @@ -303,6 +300,11 @@ String fromUri(Uri uri) => _builder.fromUri(uri); /// // URL /// path.toUri('http://dartlang.org/path/to/foo') /// // -> Uri.parse('http://dartlang.org/path/to/foo') +/// +/// If [path] is relative, a relative URI will be returned. +/// +/// path.toUri('path/to/foo') +/// // -> Uri.parse('path/to/foo') Uri toUri(String path) => _builder.toUri(path); /// Validates that there are no non-null arguments following a null one and From 6d0caed8b5907b73b6dfb0340dc8bc5acceb0c78 Mon Sep 17 00:00:00 2001 From: "whesse@google.com" Date: Wed, 24 Jul 2013 08:45:18 +0000 Subject: [PATCH 019/183] Port dart:io Path tests to package:path. BUG= R=nweiz@google.com, rnystrom@google.com Review URL: https://codereview.chromium.org//19231002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25403 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 13 +++++ pkgs/path/lib/path.dart | 39 +++++++++++--- pkgs/path/test/posix_test.dart | 47 ++++++++++++++++- pkgs/path/test/relative_test.dart | 86 +++++++++++++++++++++++++++++++ pkgs/path/test/url_test.dart | 33 +++++++++++- pkgs/path/test/windows_test.dart | 39 +++++++++++++- 6 files changed, 247 insertions(+), 10 deletions(-) create mode 100644 pkgs/path/test/relative_test.dart diff --git a/pkgs/path/README.md b/pkgs/path/README.md index dacacaa8..4108ce0c 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -73,6 +73,16 @@ Trailing separators are ignored. builder.dirname('path/to/'); // -> 'path' +If an absolute path contains no directories, only a root, then the root +is returned. + + path.dirname('/'); // -> '/' (posix) + path.dirname('c:\'); // -> 'c:\' (windows) + +If a relative path has no directories, then '.' is returned. + path.dirname('foo'); // -> '.' + path.dirname(''); // -> '.' + ### String extension(String path) Gets the file extension of [path]: the portion of [basename] from the last @@ -200,6 +210,9 @@ If the [from] argument is passed, [path] is made relative to that instead. path.relative('/root/other.dart', from: '/root/path'); // -> '../other.dart' +If [path] and/or [from] are relative paths, they are assumed to be relative +to the current directory. + Since there is no relative path from one drive letter to another on Windows, this will return an absolute path in that case. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index cc0493ce..bb88953f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -106,6 +106,17 @@ String basenameWithoutExtension(String path) => /// Trailing separators are ignored. /// /// builder.dirname('path/to/'); // -> 'path' +/// +/// If an absolute path contains no directories, only a root, then the root +/// is returned. +/// +/// path.dirname('/'); // -> '/' (posix) +/// path.dirname('c:\'); // -> 'c:\' (windows) +/// +/// If a relative path has no directories, then '.' is returned. +/// +/// path.dirname('foo'); // -> '.' +/// path.dirname(''); // -> '.' String dirname(String path) => _builder.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last @@ -248,6 +259,9 @@ String normalize(String path) => _builder.normalize(path); /// path.relative('/root/other.dart', /// from: '/root/path'); // -> '../other.dart' /// +/// If [path] and/or [from] are relative paths, they are assumed to be relative +/// to the current directory. +/// /// Since there is no relative path from one drive letter to another on Windows, /// or from one hostname to another for URLs, this will return an absolute path /// in those cases. @@ -582,8 +596,6 @@ class Builder { /// /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) { - if (path == '') return path; - var parsed = _parse(path); parsed.normalize(); return parsed.toString(); @@ -613,6 +625,9 @@ class Builder { /// builder.relative('/root/other.dart', /// from: '/root/path'); // -> '../other.dart' /// + /// If [path] and/or [from] are relative paths, they are assumed to be + /// relative to [root]. + /// /// Since there is no relative path from one drive letter to another on /// Windows, this will return an absolute path in that case. /// @@ -624,8 +639,6 @@ class Builder { /// var builder = new Builder(r'some/relative/path'); /// builder.relative(r'/absolute/path'); // -> '/absolute/path' String relative(String path, {String from}) { - if (path == '') return '.'; - from = from == null ? root : this.join(root, from); // We can't determine the path from a relative path to an absolute path. @@ -672,8 +685,12 @@ class Builder { pathParsed.separators.removeAt(1); } - // If there are any directories left in the root path, we need to walk up - // out of them. + // If there are any directories left in the from path, we need to walk up + // out of them. If a directory left in the from path is '..', it cannot + // be cancelled by adding a '..'. + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + throw new ArgumentError('Unable to find a path to "$path" from "$from".'); + } _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); pathParsed.separators[0] = ''; pathParsed.separators.insertAll(1, @@ -682,6 +699,13 @@ class Builder { // Corner case: the paths completely collapsed. if (pathParsed.parts.length == 0) return '.'; + // Corner case: path was '.' and some '..' directories were added in front. + // Don't add a final '/.' in that case. + if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { + pathParsed.parts.removeLast(); + pathParsed.separators..removeLast()..removeLast()..add(''); + } + // Make it relative. pathParsed.root = ''; pathParsed.removeTrailingSeparators(); @@ -1042,7 +1066,8 @@ class _ParsedPath { return copy._splitExtension()[0]; } - bool get hasTrailingSeparator => !parts.isEmpty && (parts.last == '' || separators.last != ''); + bool get hasTrailingSeparator => + !parts.isEmpty && (parts.last == '' || separators.last != ''); void removeTrailingSeparators() { while (!parts.isEmpty && parts.last == '') { diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index c26a5ba8..3afb3bb8 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -23,6 +23,8 @@ main() { test('extension', () { expect(builder.extension(''), ''); + expect(builder.extension('.'), ''); + expect(builder.extension('..'), ''); expect(builder.extension('foo.dart'), '.dart'); expect(builder.extension('foo.dart.js'), '.js'); expect(builder.extension('a.b/c'), ''); @@ -41,12 +43,16 @@ main() { test('dirname', () { expect(builder.dirname(''), '.'); + expect(builder.dirname('.'), '.'); + expect(builder.dirname('..'), '.'); + expect(builder.dirname('../..'), '..'); expect(builder.dirname('a'), '.'); expect(builder.dirname('a/b'), 'a'); expect(builder.dirname('a/b/c'), 'a/b'); expect(builder.dirname('a/b.c'), 'a'); expect(builder.dirname('a/'), '.'); expect(builder.dirname('a/.'), 'a'); + expect(builder.dirname('a/..'), 'a'); expect(builder.dirname(r'a\b/c'), r'a\b'); expect(builder.dirname('/a'), '/'); expect(builder.dirname('///a'), '/'); @@ -61,12 +67,16 @@ main() { test('basename', () { expect(builder.basename(''), ''); + expect(builder.basename('.'), '.'); + expect(builder.basename('..'), '..'); + expect(builder.basename('.foo'), '.foo'); expect(builder.basename('a'), 'a'); expect(builder.basename('a/b'), 'b'); expect(builder.basename('a/b/c'), 'c'); expect(builder.basename('a/b.c'), 'b.c'); expect(builder.basename('a/'), 'a'); expect(builder.basename('a/.'), '.'); + expect(builder.basename('a/..'), '..'); expect(builder.basename(r'a\b/c'), 'c'); expect(builder.basename('/a'), 'a'); expect(builder.basename('/'), '/'); @@ -79,6 +89,8 @@ main() { test('basenameWithoutExtension', () { expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('.'), '.'); + expect(builder.basenameWithoutExtension('..'), '..'); expect(builder.basenameWithoutExtension('a'), 'a'); expect(builder.basenameWithoutExtension('a/b'), 'b'); expect(builder.basenameWithoutExtension('a/b/c'), 'c'); @@ -93,6 +105,7 @@ main() { expect(builder.basenameWithoutExtension('a//b'), 'b'); expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); + expect(builder.basenameWithoutExtension('a/b c.d e'), 'b c'); }); test('isAbsolute', () { @@ -103,6 +116,8 @@ main() { expect(builder.isAbsolute('/a/b'), true); expect(builder.isAbsolute('~'), false); expect(builder.isAbsolute('.'), false); + expect(builder.isAbsolute('..'), false); + expect(builder.isAbsolute('.foo'), false); expect(builder.isAbsolute('../a'), false); expect(builder.isAbsolute('C:/a'), false); expect(builder.isAbsolute(r'C:\a'), false); @@ -117,6 +132,8 @@ main() { expect(builder.isRelative('/a/b'), false); expect(builder.isRelative('~'), true); expect(builder.isRelative('.'), true); + expect(builder.isRelative('..'), true); + expect(builder.isRelative('.foo'), true); expect(builder.isRelative('../a'), true); expect(builder.isRelative('C:/a'), true); expect(builder.isRelative(r'C:\a'), true); @@ -165,6 +182,14 @@ main() { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); }); + + test('join does not modify internal ., .., or trailing separators', () { + expect(builder.join('a/', 'b/c/'), 'a/b/c/'); + expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + 'a/b/./c/..//d/.././..//e/f//'); + expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + }); }); group('joinAll', () { @@ -212,12 +237,17 @@ main() { group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), ''); + expect(builder.normalize(''), '.'); expect(builder.normalize('.'), '.'); expect(builder.normalize('..'), '..'); expect(builder.normalize('a'), 'a'); expect(builder.normalize('/'), '/'); expect(builder.normalize(r'\'), r'\'); + expect(builder.normalize('C:/'), 'C:'); + expect(builder.normalize(r'C:\'), r'C:\'); + expect(builder.normalize(r'\\'), r'\\'); + expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { @@ -249,24 +279,38 @@ main() { expect(builder.normalize('/..'), '/'); expect(builder.normalize('/../../..'), '/'); expect(builder.normalize('/../../../a'), '/a'); + expect(builder.normalize('c:/..'), '.'); + expect(builder.normalize('A:/../../..'), '../..'); expect(builder.normalize('a/..'), '.'); expect(builder.normalize('a/b/..'), 'a'); expect(builder.normalize('a/../b'), 'b'); expect(builder.normalize('a/./../b'), 'b'); expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); expect(builder.normalize('a/b/../../../../c'), '../../c'); + expect(builder.normalize(r'z/a/b/../../..\../c'), r'z/..\../c'); + expect(builder.normalize(r'a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { expect(builder.normalize('..'), '..'); expect(builder.normalize('../'), '..'); + expect(builder.normalize('http://dartlang.org/..'), 'http:'); + expect(builder.normalize('http://dartlang.org/../../a'), 'a'); + expect(builder.normalize('file:///..'), '.'); + expect(builder.normalize('file:///../../a'), '../a'); expect(builder.normalize('/..'), '/'); expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('../a'), '../a'); + expect(builder.normalize('/../a'), '/a'); + expect(builder.normalize('c:/../a'), 'a'); + expect(builder.normalize('/../a'), '/a'); expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('../a/b/..'), '../a'); expect(builder.normalize('a/../b'), 'b'); expect(builder.normalize('a/./../b'), 'b'); expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); expect(builder.normalize('a/b/../../../../c'), '../../c'); + expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { @@ -274,6 +318,7 @@ main() { expect(builder.normalize('.//'), '.'); expect(builder.normalize('a/'), 'a'); expect(builder.normalize('a/b/'), 'a/b'); + expect(builder.normalize(r'a/b\'), r'a/b\'); expect(builder.normalize('a/b///'), 'a/b'); }); }); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart new file mode 100644 index 00000000..b9cd254b --- /dev/null +++ b/pkgs/path/test/relative_test.dart @@ -0,0 +1,86 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +// +// Test "relative" on all styles of path.Builder, on all platforms. + +import "package:unittest/unittest.dart"; +import "package:path/path.dart" as path; + +void main() { + test("test relative", () { + relativeTest(new path.Builder(style: path.Style.posix, root: '.'), '/'); + relativeTest(new path.Builder(style: path.Style.posix, root: '/'), '/'); + relativeTest(new path.Builder(style: path.Style.windows, root: r'd:\'), + r'c:\'); + relativeTest(new path.Builder(style: path.Style.windows, root: '.'), + r'c:\'); + relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + 'http://myserver/'); + relativeTest(new path.Builder(style: path.Style.url, root: '.'), + 'http://myserver/'); + relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + '/'); + relativeTest(new path.Builder(style: path.Style.url, root: '.'), '/'); + }); +} + +void relativeTest(path.Builder builder, String prefix) { + var isRelative = (builder.root == '.'); + // Cases where the arguments are absolute paths. + expectRelative(result, pathArg, fromArg) { + expect(builder.normalize(result), builder.relative(pathArg, from: fromArg)); + } + + expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); + expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b/'); + expectRelative('.', '${prefix}a', '${prefix}a'); + // Trailing slashes in the inputs have no effect. + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d/'); + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d'); + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y/', '${prefix}a/b/c/d'); + expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c'); + expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c/'); + + // Cases where the arguments are relative paths. + expectRelative('c/d', 'a/b/c/d', 'a/b'); + expectRelative('.', 'a/b/c', 'a/b/c'); + expectRelative('.', 'a/d/../b/c', 'a/b/c/'); + expectRelative('.', '', ''); + expectRelative('.', '.', ''); + expectRelative('.', '', '.'); + expectRelative('.', '.', '.'); + expectRelative('.', '..', '..'); + if (isRelative) expectRelative('..', '..', '.'); + expectRelative('a', 'a', ''); + expectRelative('a', 'a', '.'); + expectRelative('..', '.', 'a'); + expectRelative('.', 'a/b/f/../c', 'a/e/../b/c'); + expectRelative('d', 'a/b/f/../c/d', 'a/e/../b/c'); + expectRelative('..', 'a/b/f/../c', 'a/e/../b/c/e/'); + expectRelative('../..', '', 'a/b/'); + if (isRelative) expectRelative('../../..', '..', 'a/b/'); + expectRelative('../b/c/d', 'b/c/d/', 'a/'); + expectRelative('../a/b/c', 'x/y/a//b/./f/../c', 'x//y/z'); + + // Case where from is an exact substring of path. + expectRelative('a/b', '${prefix}x/y//a/b', '${prefix}x/y/'); + expectRelative('a/b', 'x/y//a/b', 'x/y/'); + expectRelative('../ya/b', '${prefix}x/ya/b', '${prefix}x/y'); + expectRelative('../ya/b', 'x/ya/b', 'x/y'); + expectRelative('../b', 'x/y/../b', 'x/y/.'); + expectRelative('a/b/c', 'x/y/a//b/./f/../c', 'x/y'); + expectRelative('.', '${prefix}x/y//', '${prefix}x/y/'); + expectRelative('.', '${prefix}x/y/', '${prefix}x/y'); + + // Should always throw - no relative path can be constructed. + if (isRelative) { + expect(() => builder.relative('.', from: '..'), throwsArgumentError); + expect(() => builder.relative('a/b', from: '../../d'), + throwsArgumentError); + expect(() => builder.relative('a/b', from: '${prefix}a/b'), + throwsArgumentError); + // An absolute path relative from a relative path returns the absolute path. + expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); + } +} diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index d040a598..1cd256d1 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -89,10 +89,13 @@ main() { expect(builder.basename('a//'), 'a'); expect(builder.basename('a/b//'), 'b'); expect(builder.basename('a//b'), 'b'); + expect(builder.basename('a b/c d.e f'), 'c d.e f'); }); test('basenameWithoutExtension', () { expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('.'), '.'); + expect(builder.basenameWithoutExtension('..'), '..'); expect(builder.basenameWithoutExtension('a'), 'a'); expect(builder.basenameWithoutExtension('a/b'), 'b'); expect(builder.basenameWithoutExtension('a/b/c'), 'c'); @@ -107,6 +110,7 @@ main() { expect(builder.basenameWithoutExtension('a//b'), 'b'); expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); + expect(builder.basenameWithoutExtension('a/b c.d e.f g'), 'b c.d e'); }); test('isAbsolute', () { @@ -235,6 +239,14 @@ main() { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); }); + + test('Join does not modify internal ., .., or trailing separators', () { + expect(builder.join('a/', 'b/c/'), 'a/b/c/'); + expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + 'a/b/./c/..//d/.././..//e/f//'); + expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + }); }); group('joinAll', () { @@ -305,7 +317,7 @@ main() { group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), ''); + expect(builder.normalize(''), '.'); expect(builder.normalize('.'), '.'); expect(builder.normalize('..'), '..'); expect(builder.normalize('a'), 'a'); @@ -315,6 +327,11 @@ main() { expect(builder.normalize('file:///'), 'file://'); expect(builder.normalize('/'), '/'); expect(builder.normalize(r'\'), r'\'); + expect(builder.normalize('C:/'), 'C:'); + expect(builder.normalize(r'C:\'), r'C:\'); + expect(builder.normalize(r'\\'), r'\\'); + expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { @@ -360,12 +377,16 @@ main() { 'http://dartlang.org/a'); expect(builder.normalize('file:///../../../a'), 'file:///a'); expect(builder.normalize('/../../../a'), '/a'); + expect(builder.normalize('c:/..'), '.'); + expect(builder.normalize('A:/../../..'), '../..'); expect(builder.normalize('a/..'), '.'); expect(builder.normalize('a/b/..'), 'a'); expect(builder.normalize('a/../b'), 'b'); expect(builder.normalize('a/./../b'), 'b'); expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); expect(builder.normalize('a/b/../../../../c'), '../../c'); + expect(builder.normalize('z/a/b/../../..\../c'), 'z/..\../c'); + expect(builder.normalize('a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { @@ -373,14 +394,23 @@ main() { expect(builder.normalize('../'), '..'); expect(builder.normalize('http://dartlang.org/..'), 'http://dartlang.org'); + expect(builder.normalize('http://dartlang.org/../a'), + 'http://dartlang.org/a'); expect(builder.normalize('file:///..'), 'file://'); + expect(builder.normalize('file:///../a'), 'file:///a'); expect(builder.normalize('/..'), '/'); expect(builder.normalize('a/..'), '.'); + expect(builder.normalize('../a'), '../a'); + expect(builder.normalize('/../a'), '/a'); + expect(builder.normalize('c:/../a'), 'a'); + expect(builder.normalize('/../a'), '/a'); expect(builder.normalize('a/b/..'), 'a'); + expect(builder.normalize('../a/b/..'), '../a'); expect(builder.normalize('a/../b'), 'b'); expect(builder.normalize('a/./../b'), 'b'); expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); expect(builder.normalize('a/b/../../../../c'), '../../c'); + expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { @@ -388,6 +418,7 @@ main() { expect(builder.normalize('.//'), '.'); expect(builder.normalize('a/'), 'a'); expect(builder.normalize('a/b/'), 'a/b'); + expect(builder.normalize(r'a/b\'), r'a/b\'); expect(builder.normalize('a/b///'), 'a/b'); }); }); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 74094397..1966b7f3 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -25,8 +25,12 @@ main() { test('extension', () { expect(builder.extension(''), ''); + expect(builder.extension('.'), ''); + expect(builder.extension('..'), ''); + expect(builder.extension('a/..'), ''); expect(builder.extension('foo.dart'), '.dart'); expect(builder.extension('foo.dart.js'), '.js'); + expect(builder.extension('foo bar\gule fisk.dart.js'), '.js'); expect(builder.extension(r'a.b\c'), ''); expect(builder.extension('a.b/c.d'), '.d'); expect(builder.extension(r'~\.bashrc'), ''); @@ -64,10 +68,14 @@ main() { expect(builder.dirname(r'a\\'), r'.'); expect(builder.dirname(r'a\b\\'), 'a'); expect(builder.dirname(r'a\\b'), 'a'); + expect(builder.dirname(r'foo bar\gule fisk'), 'foo bar'); }); test('basename', () { expect(builder.basename(r''), ''); + expect(builder.basename(r'.'), '.'); + expect(builder.basename(r'..'), '..'); + expect(builder.basename(r'.hest'), '.hest'); expect(builder.basename(r'a'), 'a'); expect(builder.basename(r'a\b'), 'b'); expect(builder.basename(r'a\b\c'), 'c'); @@ -83,10 +91,15 @@ main() { expect(builder.basename(r'a\\'), 'a'); expect(builder.basename(r'a\b\\'), 'b'); expect(builder.basename(r'a\\b'), 'b'); + expect(builder.basename(r'a\\b'), 'b'); + expect(builder.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); }); test('basenameWithoutExtension', () { expect(builder.basenameWithoutExtension(''), ''); + expect(builder.basenameWithoutExtension('.'), '.'); + expect(builder.basenameWithoutExtension('..'), '..'); + expect(builder.basenameWithoutExtension('.hest'), '.hest'); expect(builder.basenameWithoutExtension('a'), 'a'); expect(builder.basenameWithoutExtension(r'a\b'), 'b'); expect(builder.basenameWithoutExtension(r'a\b\c'), 'c'); @@ -101,10 +114,13 @@ main() { expect(builder.basenameWithoutExtension(r'a\\b'), 'b'); expect(builder.basenameWithoutExtension(r'a\b.c\'), 'b'); expect(builder.basenameWithoutExtension(r'a\b.c\\'), 'b'); + expect(builder.basenameWithoutExtension(r'C:\f h.ma pa.f s'), 'f h.ma pa'); }); test('isAbsolute', () { expect(builder.isAbsolute(''), false); + expect(builder.isAbsolute('.'), false); + expect(builder.isAbsolute('..'), false); expect(builder.isAbsolute('a'), false); expect(builder.isAbsolute(r'a\b'), false); expect(builder.isAbsolute(r'\a'), false); @@ -124,6 +140,8 @@ main() { test('isRelative', () { expect(builder.isRelative(''), true); + expect(builder.isRelative('.'), true); + expect(builder.isRelative('..'), true); expect(builder.isRelative('a'), true); expect(builder.isRelative(r'a\b'), true); expect(builder.isRelative(r'\a'), true); @@ -184,6 +202,14 @@ main() { expect(() => builder.join('a', null, 'b'), throwsArgumentError); expect(() => builder.join(null, 'a'), throwsArgumentError); }); + + test('join does not modify internal ., .., or trailing separators', () { + expect(builder.join('a/', 'b/c/'), 'a/b/c/'); + expect(builder.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), + r'a\b\./c\..\\d\..\.\..\\e\f\\'); + expect(builder.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); + expect(builder.join(r'a', 'b${builder.separator}'), r'a\b\'); + }); }); group('joinAll', () { @@ -238,13 +264,17 @@ main() { group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), ''); + expect(builder.normalize(''), '.'); expect(builder.normalize('.'), '.'); expect(builder.normalize('..'), '..'); expect(builder.normalize('a'), 'a'); + expect(builder.normalize(r'\'), '.'); + expect(builder.normalize('/'), r'.'); expect(builder.normalize('C:/'), r'C:\'); expect(builder.normalize(r'C:\'), r'C:\'); expect(builder.normalize(r'\\'), r'\\'); + expect(builder.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), + 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); }); test('collapses redundant separators', () { @@ -278,12 +308,19 @@ main() { expect(builder.normalize(r'c:\..'), r'c:\'); expect(builder.normalize(r'A:/..\..\..'), r'A:\'); expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); + expect(builder.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); expect(builder.normalize(r'a\..'), '.'); + expect(builder.normalize(r'..\a'), r'..\a'); + expect(builder.normalize(r'c:\..\a'), r'c:\a'); + // A path starting with '\' is not an absolute path on Windows. + expect(builder.normalize(r'\..\a'), r'..\a'); expect(builder.normalize(r'a\b\..'), 'a'); + expect(builder.normalize(r'..\a\b\..'), r'..\a'); expect(builder.normalize(r'a\..\b'), 'b'); expect(builder.normalize(r'a\.\..\b'), 'b'); expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); + expect(builder.normalize(r'a/b/c/../../..d/./.e/f././'), r'a\..d\.e\f.'); }); test('removes trailing separators', () { From fe8934f78f948684f8abbded03de45d6f226cbec Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Thu, 25 Jul 2013 23:27:08 +0000 Subject: [PATCH 020/183] Ignore all trailing separators in path.extension. BUG=https://code.google.com/p/dart/issues/detail?id=10235 R=nweiz@google.com Review URL: https://codereview.chromium.org//20372002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25509 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 22 ++++++++++------------ pkgs/path/test/posix_test.dart | 2 ++ pkgs/path/test/url_test.dart | 2 ++ pkgs/path/test/windows_test.dart | 2 ++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index bb88953f..4b87cde6 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -1043,7 +1043,8 @@ class _ParsedPath { /// path ends with a trailing separator. List separators; - /// The file extension of the last part, or "" if it doesn't have one. + /// The file extension of the last non-empty part, or "" if it doesn't have + /// one. String get extension => _splitExtension()[1]; /// `true` if this is an absolute path. @@ -1059,12 +1060,7 @@ class _ParsedPath { return copy.parts.last; } - String get basenameWithoutExtension { - var copy = this.clone(); - copy.removeTrailingSeparators(); - if (copy.parts.isEmpty) return root == null ? '' : root; - return copy._splitExtension()[0]; - } + String get basenameWithoutExtension => _splitExtension()[0]; bool get hasTrailingSeparator => !parts.isEmpty && (parts.last == '' || separators.last != ''); @@ -1137,13 +1133,15 @@ class _ParsedPath { return builder.toString(); } - /// Splits the last part of the path into 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. + /// Splits the last non-empty part of the path into a `[basename, extension`] + /// pair. + /// + /// 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 _splitExtension() { - if (parts.isEmpty) return ['', '']; + var file = parts.lastWhere((p) => p != '', orElse: () => null); - var file = parts.last; + if (file == null) return ['', '']; if (file == '..') return ['..', '']; var lastDot = file.lastIndexOf('.'); diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 3afb3bb8..20363930 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -31,6 +31,8 @@ main() { expect(builder.extension('a.b/c.d'), '.d'); expect(builder.extension('~/.bashrc'), ''); expect(builder.extension(r'a.b\c'), r'.b\c'); + expect(builder.extension('foo.dart/'), '.dart'); + expect(builder.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 1cd256d1..f1dfcde2 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -20,6 +20,8 @@ main() { expect(builder.extension('a.b/c'), ''); expect(builder.extension('a.b/c.d'), '.d'); expect(builder.extension(r'a.b\c'), r'.b\c'); + expect(builder.extension('foo.dart/'), '.dart'); + expect(builder.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 1966b7f3..4d830edd 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -35,6 +35,8 @@ main() { expect(builder.extension('a.b/c.d'), '.d'); expect(builder.extension(r'~\.bashrc'), ''); expect(builder.extension(r'a.b/c'), r''); + expect(builder.extension(r'foo.dart\'), '.dart'); + expect(builder.extension(r'foo.dart\\'), '.dart'); }); test('rootPrefix', () { From e0f31b9cc9be2eb0b379fe31c61acc896d880cb3 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 26 Jul 2013 00:15:40 +0000 Subject: [PATCH 021/183] Handle special characters in path<->uri conversion. Also cleaned up some docs a bit. R=nweiz@google.com Review URL: https://codereview.chromium.org//20130004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25512 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 2 +- pkgs/path/lib/path.dart | 67 ++++++++++++++++++++++---------- pkgs/path/test/posix_test.dart | 6 +++ pkgs/path/test/url_test.dart | 11 +++++- pkgs/path/test/windows_test.dart | 6 +++ 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 4108ce0c..0c835f4e 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -14,7 +14,7 @@ style. The path library was designed to be imported with a prefix, though you don't have to if you don't want to: - import 'package:path/path.dart' as path; // TODO(bob): ??? + import 'package:path/path.dart' as path; ## Top-level functions diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 4b87cde6..74ea2ef6 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -18,6 +18,32 @@ /// /// [pub]: http://pub.dartlang.org /// [pkg]: http://pub.dartlang.org/packages/path +/// +/// ## Usage ## +/// +/// The path library was designed to be imported with a prefix, though you don't +/// have to if you don't want to: +/// +/// import 'package:path/path.dart' as path; +/// +/// The most common way to use the library is through the top-level functions. +/// These manipulate path strings based on your current working directory and +/// the path style (POSIX, Windows, or URLs) of the host platform. For example: +/// +/// path.join("directory", "file.txt"); +/// +/// This calls the top-level [join] function to join "directory" and "file.txt" +/// using the current platform's directory separator. +/// +/// If you want to work with paths for a specific platform regardless of the +/// underlying platform that the program is running on, you can create a +/// [Builder] and give it an explicit [Style]: +/// +/// var builder = new path.Builder(style: Style.windows); +/// builder.join("directory", "file.txt"); +/// +/// This will join "directory" and "file.txt" using the Windows path separator, +/// even when the program is run on a POSIX machine. library path; @MirrorsUsed(targets: 'dart.dom.html.window, ' @@ -67,8 +93,8 @@ String get current { } } -/// Gets the path separator for the current platform. On Mac and Linux, this -/// is `/`. On Windows, it's `\`. +/// Gets the path separator for the current platform. This is `\` on Windows +/// and `/` on other platforms (including the browser). String get separator => _builder.separator; /// Converts [path] to an absolute path by resolving it relative to the current @@ -765,9 +791,9 @@ class Builder { /// // -> Uri.parse('http://dartlang.org/path/to/foo') Uri toUri(String path) { if (isRelative(path)) { - return Uri.parse(path.replaceAll(style.separatorPattern, '/')); + return style.relativePathToUri(path); } else { - return style.pathToUri(join(root, path)); + return style.absolutePathToUri(join(root, path)); } } @@ -821,8 +847,8 @@ abstract class Style { // just the "\\". static final windows = new _WindowsStyle(); - /// URLs aren't filesystem paths, but they're supported by Pathos to make it - /// easier to manipulate URL paths in the browser. + /// URLs aren't filesystem paths, but they're supported to make it easier to + /// manipulate URL paths in the browser. /// /// URLs use "/" (forward slash) as separators. Absolute paths either start /// with a protocol and optional hostname (e.g. `http://dartlang.org`, @@ -862,6 +888,9 @@ abstract class Style { /// paths. final Pattern relativeRootPattern = null; + /// A [Builder] that uses this style. + Builder get builder => new Builder(style: this); + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. String getRoot(String path) { @@ -885,11 +914,12 @@ abstract class Style { /// Returns the path represented by [uri] in this style. String pathFromUri(Uri uri); - /// Returns the URI that represents [path]. - /// - /// Pathos will always path an absolute path for [path]. Relative paths are - /// handled automatically by [Builder]. - Uri pathToUri(String path); + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: builder.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); String toString() => name; } @@ -898,8 +928,6 @@ abstract class Style { class _PosixStyle extends Style { _PosixStyle(); - static final _builder = new Builder(style: Style.posix); - final name = 'posix'; final separator = '/'; final separatorPattern = new RegExp(r'/'); @@ -913,8 +941,8 @@ class _PosixStyle extends Style { throw new ArgumentError("Uri $uri must have scheme 'file:'."); } - Uri pathToUri(String path) { - var parsed = _builder._parse(path); + Uri absolutePathToUri(String path) { + var parsed = builder._parse(path); if (parsed.parts.isEmpty) { // If the path is a bare root (e.g. "/"), [components] will @@ -935,8 +963,6 @@ class _PosixStyle extends Style { class _WindowsStyle extends Style { _WindowsStyle(); - static final _builder = new Builder(style: Style.windows); - final name = 'windows'; final separator = '\\'; final separatorPattern = new RegExp(r'[/\\]'); @@ -960,8 +986,8 @@ class _WindowsStyle extends Style { return Uri.decodeComponent(path.replaceAll("/", "\\")); } - Uri pathToUri(String path) { - var parsed = _builder._parse(path); + Uri absolutePathToUri(String path) { + var parsed = builder._parse(path); if (parsed.root == r'\\') { // Network paths become "file://hostname/path/to/file". @@ -1013,7 +1039,8 @@ class _UrlStyle extends Style { String pathFromUri(Uri uri) => uri.toString(); - Uri pathToUri(String path) => Uri.parse(path); + Uri relativePathToUri(String path) => Uri.parse(path); + Uri absolutePathToUri(String path) => Uri.parse(path); } // TODO(rnystrom): Make this public? diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 20363930..1156379a 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -464,6 +464,8 @@ main() { expect(builder.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); expect(builder.fromUri(Uri.parse('file:///path/to/foo%23bar')), '/path/to/foo#bar'); + expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_{_}_`_^_ _"_%_'); expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); @@ -475,5 +477,9 @@ main() { expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); expect(builder.toUri('/path/to/foo#bar'), Uri.parse('file:///path/to/foo%23bar')); + expect(builder.toUri(r'/_{_}_`_^_ _"_%_'), + Uri.parse('file:///_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index f1dfcde2..d43e88c4 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -667,7 +667,6 @@ main() { expect(builder.withoutExtension('a/b.c//'), 'a/b//'); }); - test('fromUri', () { expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), 'http://dartlang.org/path/to/foo'); @@ -678,6 +677,10 @@ main() { expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), 'http://dartlang.org/path/to/foo%23bar'); + // Since the resulting "path" is also a URL, special characters should + // remain percent-encoded in the result. + expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_%7B_%7D_%60_%5E_%20_%22_%25_'); }); test('toUri', () { @@ -690,5 +693,11 @@ main() { expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); expect(builder.toUri('http://dartlang.org/path/to/foo%23bar'), Uri.parse('http://dartlang.org/path/to/foo%23bar')); + // Since the input path is also a URI, special characters should already + // be percent encoded there too. + expect(builder.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), + Uri.parse('http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(builder.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), + Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 4d830edd..787adfc3 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -505,6 +505,8 @@ main() { r'C:\path\to\foo#bar'); expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo%23bar')), r'\\hostname\path\to\foo#bar'); + expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_{_}_`_^_ _"_%_'); expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); @@ -521,5 +523,9 @@ main() { Uri.parse('file:///C:/path/to/foo%23bar')); expect(builder.toUri(r'\\hostname\path\to\foo#bar'), Uri.parse('file://hostname/path/to/foo%23bar')); + expect(builder.toUri(r'C:\_{_}_`_^_ _"_%_'), + Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } From b1e5373e50282280999a0e044d8ee42037854cf4 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 26 Jul 2013 00:18:52 +0000 Subject: [PATCH 022/183] Remove API documentation from path README. BUG=https://code.google.com/p/dart/issues/detail?id=8072 R=nweiz@google.com Review URL: https://codereview.chromium.org//20364002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25513 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 363 +++------------------------------------- pkgs/path/lib/path.dart | 1 - pkgs/path/pubspec.yaml | 1 + 3 files changed, 23 insertions(+), 342 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 0c835f4e..eff60de4 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -1,13 +1,13 @@ A comprehensive, cross-platform path manipulation library for Dart. -The path package provides common operations for manipulating file paths: +The path package provides common operations for manipulating paths: joining, splitting, normalizing, etc. We've tried very hard to make this library do the "right" thing on whatever -platform you run it on. When you use the top-level functions, it will assume the -current platform's path style and work with that. If you want to specifically -work with paths of a specific style, you can construct a `path.Builder` for that -style. +platform you run it on, including in the browser. When you use the top-level +functions, it will assume the current platform's path style and work with +that. If you want to explicitly work with paths of a specific style, you can +construct a `path.Builder` for that style. ## Using @@ -16,345 +16,24 @@ have to if you don't want to: import 'package:path/path.dart' as path; -## Top-level functions - The most common way to use the library is through the top-level functions. -These manipulate path strings based on your current working directory and the -path style (POSIX, Windows, or URLs) of the host platform. - -### String get current - -Gets the path to the current working directory. In the browser, this means the -current URL. When using dart2js, this currently returns `.` due to technical -constraints. In the future, it will return the current URL. - -### String get separator - -Gets the path separator for the current platform. On Mac, Linux, and the -browser, this is `/`. On Windows, it's `\`. - -### String absolute(String path) - -Converts [path] to an absolute path by resolving it relative to the current -working directory. If [path] is already an absolute path, just returns it. - - path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt - -### String basename(String path) - -Gets the part of [path] after the last separator. - - path.basename('path/to/foo.dart'); // -> 'foo.dart' - path.basename('path/to'); // -> 'to' - -Trailing separators are ignored. - - builder.basename('path/to/'); // -> 'to' - -### String basenameWithoutExtension(String path) - -Gets the part of [path] after the last separator, and without any trailing -file extension. - - path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' - -Trailing separators are ignored. - - builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' - -### String dirname(String path) - -Gets the part of [path] before the last separator. - - path.dirname('path/to/foo.dart'); // -> 'path/to' - path.dirname('path/to'); // -> 'to' - -Trailing separators are ignored. - - builder.dirname('path/to/'); // -> 'path' - -If an absolute path contains no directories, only a root, then the root -is returned. - - path.dirname('/'); // -> '/' (posix) - path.dirname('c:\'); // -> 'c:\' (windows) - -If a relative path has no directories, then '.' is returned. - path.dirname('foo'); // -> '.' - path.dirname(''); // -> '.' - -### String extension(String path) - -Gets the file extension of [path]: the portion of [basename] from the last -`.` to the end (including the `.` itself). - - path.extension('path/to/foo.dart'); // -> '.dart' - path.extension('path/to/foo'); // -> '' - path.extension('path.to/foo'); // -> '' - path.extension('path/to/foo.dart.js'); // -> '.js' - -If the file name starts with a `.`, then that is not considered the -extension: - - path.extension('~/.bashrc'); // -> '' - path.extension('~/.notes.txt'); // -> '.txt' - -### String rootPrefix(String path) - -Returns the root of [path], if it's absolute, or the empty string if it's -relative. - - // Unix - path.rootPrefix('path/to/foo'); // -> '' - path.rootPrefix('/path/to/foo'); // -> '/' - - // Windows - path.rootPrefix(r'path\to\foo'); // -> '' - path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' - - // URL - path.rootPrefix('path/to/foo'); // -> '' - path.rootPrefix('http://dartlang.org/path/to/foo'); - // -> 'http://dartlang.org' - -### bool isAbsolute(String path) - -Returns `true` if [path] is an absolute path and `false` if it is a relative -path. On POSIX systems, absolute paths start with a `/` (forward slash). On -Windows, an absolute path starts with `\\`, or a drive letter followed by `:/` -or `:\`. For URLs, absolute paths either start with a protocol and optional -hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. - -URLs that start with `/` are known as "root-relative", since they're relative to -the root of the current URL. Since root-relative paths are still absolute in -every other sense, [isAbsolute] will return true for them. They can be detected -using [isRootRelative]. - -### bool isRelative(String path) - -Returns `true` if [path] is a relative path and `false` if it is absolute. -On POSIX systems, absolute paths start with a `/` (forward slash). On -Windows, an absolute path starts with `\\`, or a drive letter followed by -`:/` or `:\`. - -### bool isRootRelative(String path) - -Returns `true` if [path] is a root-relative path and `false` if it's not. URLs -that start with `/` are known as "root-relative", since they're relative to the -root of the current URL. Since root-relative paths are still absolute in every -other sense, [isAbsolute] will return true for them. They can be detected using -[isRootRelative]. - -No POSIX and Windows paths are root-relative. - -### String join(String part1, [String part2, String part3, ...]) - -Joins the given path parts into a single path using the current platform's -[separator]. Example: - - path.join('path', 'to', 'foo'); // -> 'path/to/foo' - -If any part ends in a path separator, then a redundant separator will not -be added: - - path.join('path/', 'to', 'foo'); // -> 'path/to/foo - -If a part is an absolute path, then anything before that will be ignored: - - path.join('path', '/to', 'foo'); // -> '/to/foo' - -### List split(String path) - -Splits [path] into its components using the current platform's [separator]. - - path.split('path/to/foo'); // -> ['path', 'to', 'foo'] - -The path will *not* be normalized before splitting. - - path.split('path/../foo'); // -> ['path', '..', 'foo'] - -If [path] is absolute, the root directory will be the first element in the -array. Example: +These manipulate path strings based on your current working directory and +the path style (POSIX, Windows, or URLs) of the host platform. For example: - // Unix - path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + path.join("directory", "file.txt"); - // Windows - path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +This calls the top-level [join] function to join "directory" and +"file.txt" using the current platform's directory separator. - // Browser - path.split('http://dartlang.org/path/to/foo'); - // -> ['http://dartlang.org', 'path', 'to', 'foo'] +If you want to work with paths for a specific platform regardless of the +underlying platform that the program is running on, you can create a +[Builder] and give it an explicit [Style]: -### String normalize(String path) + var builder = new path.Builder(style: Style.windows); + builder.join("directory", "file.txt"); -Normalizes [path], simplifying it by handling `..`, and `.`, and -removing redundant path separators whenever possible. - - path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' -String normalize(String path) => _builder.normalize(path); - -### String relative(String path, {String from}) - -Attempts to convert [path] to an equivalent relative path from the current -directory. - - // Given current directory is /root/path: - path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' - path.relative('/root/other.dart'); // -> '../other.dart' - -If the [from] argument is passed, [path] is made relative to that instead. - - path.relative('/root/path/a/b.dart', - from: '/root/path'); // -> 'a/b.dart' - path.relative('/root/other.dart', - from: '/root/path'); // -> '../other.dart' - -If [path] and/or [from] are relative paths, they are assumed to be relative -to the current directory. - -Since there is no relative path from one drive letter to another on Windows, -this will return an absolute path in that case. - - // Windows - path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' - - // URL - path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); - // -> 'http://dartlang.org' - -### String withoutExtension(String path) - -Removes a trailing extension from the last part of [path]. - - withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' - -### String fromUri(Uri uri) - -Returns the path represented by [uri]. For POSIX and Windows styles, [uri] must -be a `file:` URI. For the URL style, this will just convert [uri] to a string. - - // POSIX - path.fromUri(Uri.parse('file:///path/to/foo')) - // -> '/path/to/foo' - - // Windows - path.fromUri(Uri.parse('file:///C:/path/to/foo')) - // -> r'C:\path\to\foo' - - // URL - path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) - // -> 'http://dartlang.org/path/to/foo' - -### Uri toUri(String path) - -Returns the URI that represents [path]. For POSIX and Windows styles, this will -return a `file:` URI. For the URL style, this will just convert [path] to a -[Uri]. - -This will always convert relative paths to absolute ones before converting -to a URI. - - // POSIX - path.toUri('/path/to/foo') - // -> Uri.parse('file:///path/to/foo') - - // Windows - path.toUri(r'C:\path\to\foo') - // -> Uri.parse('file:///C:/path/to/foo') - - // URL - path.toUri('http://dartlang.org/path/to/foo') - // -> Uri.parse('http://dartlang.org/path/to/foo') - -## The path.Builder class - -In addition to the functions, path exposes a `path.Builder` class. This lets -you configure the root directory and path style that paths are built using -explicitly instead of assuming the current working directory and host OS's path -style. - -You won't often use this, but it can be useful if you do a lot of path -manipulation relative to some root directory. - - var builder = new path.Builder(root: '/other/root'); - builder.relative('/other/root/foo.txt'); // -> 'foo.txt' - -It exposes the same methods and getters as the top-level functions, with the -addition of: - -### new Builder({Style style, String root}) - -Creates a new path builder for the given style and root directory. - -If [style] is omitted, it uses the host operating system's path style. If -[root] is omitted, it defaults to the current working directory. If [root] -is relative, it is considered relative to the current working directory. - -### Style style - -The style of path that this builder works with. - -### String root - -The root directory that relative paths will be relative to. - -### String get separator - -Gets the path separator for the builder's [style]. On Mac and Linux, -this is `/`. On Windows, it's `\`. - -### String rootPrefix(String path) - -Returns the root of [path], if it's absolute, or an empty string if it's -relative. - - // Unix - builder.rootPrefix('path/to/foo'); // -> '' - builder.rootPrefix('/path/to/foo'); // -> '/' - - // Windows - builder.rootPrefix(r'path\to\foo'); // -> '' - builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' - - // URL - builder.rootPrefix('path/to/foo'); // -> '' - builder.rootPrefix('http://dartlang.org/path/to/foo'); - // -> 'http://dartlang.org' - -### String resolve(String part1, [String part2, String part3, ...]) - -Creates a new path by appending the given path parts to the [root]. -Equivalent to [join()] with [root] as the first argument. Example: - - var builder = new Builder(root: 'root'); - builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' - -## The path.Style class - -The path library can work with three different "flavors" of path: POSIX, -Windows, and URLs. The differences between these are encapsulated by the -`path.Style` enum class. There are three instances of it: - -### path.Style.posix - -POSIX-style paths use "/" (forward slash) as separators. Absolute paths -start with "/". Used by UNIX, Linux, Mac OS X, and others. - -### path.Style.windows - -Windows paths use "\" (backslash) as separators. Absolute paths start with -a drive letter followed by a colon (example, "C:") or two backslashes -("\\") for UNC paths. - -### path.Style.url - -URLs aren't filesystem paths, but they're supported by Pathos to make it easier -to manipulate URL paths in the browser. - -URLs use "/" (forward slash) as separators. Absolute paths either start with a -protocol and optional hostname (e.g. `http://dartlang.org`, `file://`) or with -"/". +This will join "directory" and "file.txt" using the Windows path separator, +even when the program is run on a POSIX machine. ## FAQ @@ -382,7 +61,7 @@ it accepts strings, path objects, or both. * Taking both means you can't type your API. That defeats the purpose of having a path type: why have a type if your APIs can't annotate that they - use it? + expect it? Given that, we've decided this library should simply treat paths as strings. @@ -402,5 +81,7 @@ We believe this library handles most of the corner cases of Windows paths * It knows that "C:\foo\one.txt" and "c:/foo\two.txt" are two files in the same directory. -If you find a problem, surprise or something that's unclear, please don't -hesitate to [file a bug](http://dartbug.com/new) and let us know. +### What is a "path" in the browser? + +If you use this package in a browser, then it considers the "platform" to be +the browser itself and uses URL strings to represent "browser paths". diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 74ea2ef6..95bc90d5 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -943,7 +943,6 @@ class _PosixStyle extends Style { Uri absolutePathToUri(String path) { var parsed = builder._parse(path); - if (parsed.parts.isEmpty) { // If the path is a bare root (e.g. "/"), [components] will // currently be empty. We add two empty components so the URL constructor diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 60fcec2c..35047ac2 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,6 +1,7 @@ name: path author: "Dart Team " homepage: http://www.dartlang.org +documentation: http://api.dartlang.org/docs/pkg/path description: > A string-based path manipulation library. All of the path operations you know and love, with solid support on both Windows and POSIX (Linux and Mac OS X) From 0b09e71a761a03b3ade4908e6221b76fa821aa5d Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 30 Jul 2013 00:44:56 +0000 Subject: [PATCH 023/183] First stab at a dev server in pub using barback. R=nweiz@google.com Review URL: https://codereview.chromium.org//20204003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25600 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 50 +++++++++++++++++++++++--------- pkgs/path/test/browser_test.dart | 17 ++++++++--- pkgs/path/test/io_test.dart | 23 ++++++++++----- pkgs/path/test/path_test.dart | 15 ++++++++++ 4 files changed, 80 insertions(+), 25 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 95bc90d5..eb126d53 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -55,10 +55,17 @@ import 'dart:mirrors'; /// functional interface and not require users to create one. final _builder = new Builder(); -/** - * Inserts [length] elements in front of the [list] and fills them with the - * [fillValue]. - */ +/// A default builder for manipulating POSIX paths. +final posix = new Builder(style: Style.posix); + +/// A default builder for manipulating Windows paths. +final windows = new Builder(style: Style.windows); + +/// A default builder for manipulating URLs. +final url = new Builder(style: Style.url); + +/// Inserts [length] elements in front of the [list] and fills them with the +/// [fillValue]. void _growListFront(List list, int length, fillValue) => list.insertAll(0, new List.filled(length, fillValue)); @@ -376,25 +383,22 @@ class Builder { /// Creates a new path builder for the given style and root directory. /// /// If [style] is omitted, it uses the host operating system's path style. If - /// [root] is omitted, it defaults to the current working directory. If [root] - /// is relative, it is considered relative to the current working directory. + /// only [root] is omitted, it defaults ".". If *both* [style] and [root] are + /// omitted, [root] defaults to the current working directory. /// /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults /// to the current URL. When using dart2js, it currently defaults to `.` due /// to technical constraints. factory Builder({Style style, String root}) { - if (style == null) { - if (_io == null) { - style = Style.url; - } else if (_io.classes[const Symbol('Platform')] - .getField(const Symbol('operatingSystem')).reflectee == 'windows') { - style = Style.windows; + if (root == null) { + if (style == null) { + root = current; } else { - style = Style.posix; + root = "."; } } - if (root == null) root = current; + if (style == null) style = Style.platform; return new Builder._(style, root); } @@ -855,6 +859,24 @@ abstract class Style { /// `file://`) or with "/". static final url = new _UrlStyle(); + /// The style of the host platform. + /// + /// When running on the command line, this will be [windows] or [posix] based + /// on the host operating system. On a browser, this will be [url]. + static final platform = _getPlatformStyle(); + + /// Gets the type of the host platform. + static Style _getPlatformStyle() { + if (_io == null) return Style.url; + + if (_io.classes[const Symbol('Platform')] + .getField(const Symbol('operatingSystem')).reflectee == 'windows') { + return Style.windows; + } + + return Style.posix; + } + /// The name of this path style. Will be "posix" or "windows". String get name; diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 2a830c75..3e885007 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -12,17 +12,26 @@ main() { useHtmlConfiguration(); group('new Builder()', () { - test('uses the current working directory if root is omitted', () { + test('uses the current directory if root and style are omitted', () { var builder = new path.Builder(); - expect(builder.root, window.location.href); + expect(builder.root, io.Directory.current.path); }); - test('uses URL if style is omitted', () { + test('uses "." if root is omitted', () { + var builder = new path.Builder(style: path.Style.platform); + expect(builder.root, "."); + }); + + test('uses the host platform if style is omitted', () { var builder = new path.Builder(); - expect(builder.style, path.Style.url); + expect(builder.style, path.Style.platform); }); }); + test('Style.platform is url', () { + expect(path.Style.platform, path.Style.url); + }); + test('current', () { expect(path.current, window.location.href); }); diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index fc29ced6..66512495 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -9,21 +9,30 @@ import 'package:path/path.dart' as path; main() { group('new Builder()', () { - test('uses the current working directory if root is omitted', () { + test('uses the current directory if root and style are omitted', () { var builder = new path.Builder(); expect(builder.root, io.Directory.current.path); }); - test('uses the host OS if style is omitted', () { + test('uses "." if root is omitted', () { + var builder = new path.Builder(style: path.Style.platform); + expect(builder.root, "."); + }); + + test('uses the host platform if style is omitted', () { var builder = new path.Builder(); - if (io.Platform.operatingSystem == 'windows') { - expect(builder.style, path.Style.windows); - } else { - expect(builder.style, path.Style.posix); - } + expect(builder.style, path.Style.platform); }); }); + test('Style.platform returns the host platform style', () { + if (io.Platform.operatingSystem == 'windows') { + expect(path.Style.platform, path.Style.windows); + } else { + expect(path.Style.platform, path.Style.posix); + } + }); + test('current', () { expect(path.current, io.Directory.current.path); }); diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index ef3cf21e..d6c2f349 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -34,4 +34,19 @@ main() { expect(builder.style, path.Style.windows); }); }); + + test('posix is a default Builder for the POSIX style', () { + expect(path.posix.style, path.Style.posix); + expect(path.posix.root, "."); + }); + + test('windows is a default Builder for the Windows style', () { + expect(path.windows.style, path.Style.windows); + expect(path.windows.root, "."); + }); + + test('url is a default Builder for the URL style', () { + expect(path.url.style, path.Style.url); + expect(path.url.root, "."); + }); } From 491f06fa8d6600a87dd434969e29fbe15e6eda68 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 30 Jul 2013 18:32:40 +0000 Subject: [PATCH 024/183] Fix path browser test. BUG=https://code.google.com/p/dart/issues/detail?id=12121 R=nweiz@google.com Review URL: https://codereview.chromium.org//21164005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25628 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/test/browser_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 3e885007..07256469 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -12,9 +12,9 @@ main() { useHtmlConfiguration(); group('new Builder()', () { - test('uses the current directory if root and style are omitted', () { + test('uses the window location if root and style are omitted', () { var builder = new path.Builder(); - expect(builder.root, io.Directory.current.path); + expect(builder.root, window.location.href); }); test('uses "." if root is omitted', () { From fb288fca58d034e33fa68e7c6749a35caacb059e Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Tue, 6 Aug 2013 20:42:04 +0000 Subject: [PATCH 025/183] pkg: analysis aided cleanup Removed a lot of warnings and hints when opening many pkg projects in the editor R=gram@google.com Review URL: https://codereview.chromium.org//22284003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25831 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 35047ac2..a3661220 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,8 +1,10 @@ name: path -author: "Dart Team " -homepage: http://www.dartlang.org -documentation: http://api.dartlang.org/docs/pkg/path +author: Dart Team description: > A string-based path manipulation library. All of the path operations you know and love, with solid support on both Windows and POSIX (Linux and Mac OS X) machines. +homepage: http://www.dartlang.org +documentation: http://api.dartlang.org/docs/pkg/path +dev_dependencies: + unittest: any From e878e6eb28e4d0a8d9af7e7fece97c317e08ca5c Mon Sep 17 00:00:00 2001 From: "jmesserly@google.com" Date: Thu, 10 Oct 2013 22:55:17 +0000 Subject: [PATCH 026/183] use symbol literals instead of const ctor R=rnystrom@google.com, sigmund@google.com Review URL: https://codereview.chromium.org//26734004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@28485 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index eb126d53..dafb0c2b 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -90,11 +90,9 @@ LibraryMirror get _html => /// return the current URL. String get current { if (_io != null) { - return _io.classes[const Symbol('Directory')] - .getField(const Symbol('current')).reflectee.path; + return _io.classes[#Directory].getField(#current).reflectee.path; } else if (_html != null) { - return _html.getField(const Symbol('window')) - .reflectee.location.href; + return _html.getField(#window).reflectee.location.href; } else { return '.'; } @@ -869,8 +867,8 @@ abstract class Style { static Style _getPlatformStyle() { if (_io == null) return Style.url; - if (_io.classes[const Symbol('Platform')] - .getField(const Symbol('operatingSystem')).reflectee == 'windows') { + if (_io.classes[#Platform].getField(#operatingSystem) + .reflectee == 'windows') { return Style.windows; } From 2766386b13335c7cc69345f6ca0c26669fc40bf0 Mon Sep 17 00:00:00 2001 From: "ahe@google.com" Date: Wed, 30 Oct 2013 14:58:58 +0000 Subject: [PATCH 027/183] Mirrors overhaul. R=johnniwinther@google.com Review URL: https://codereview.chromium.org//23455028 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@29551 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index dafb0c2b..da4c935f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -90,7 +90,8 @@ LibraryMirror get _html => /// return the current URL. String get current { if (_io != null) { - return _io.classes[#Directory].getField(#current).reflectee.path; + return (_io.declarations[#Directory] as ClassMirror) + .getField(#current).reflectee.path; } else if (_html != null) { return _html.getField(#window).reflectee.location.href; } else { @@ -867,7 +868,7 @@ abstract class Style { static Style _getPlatformStyle() { if (_io == null) return Style.url; - if (_io.classes[#Platform].getField(#operatingSystem) + if ((_io.declarations[#Platform] as ClassMirror).getField(#operatingSystem) .reflectee == 'windows') { return Style.windows; } From 76c44448e992442cb497dbcfbe8fd6f7631cb69a Mon Sep 17 00:00:00 2001 From: "jmesserly@google.com" Date: Wed, 6 Nov 2013 03:27:58 +0000 Subject: [PATCH 028/183] add versions and constraints for packages and samples - all packages at 0.9.0, except "analyzer" which had a version already - dependencies at ">=0.9.0 <0.10.0" except analyzer is ">=0.10.0 <0.11.0" - sdk constraint ">=1.0.0 <2.0.0" R=sigmund@google.com Review URL: https://codereview.chromium.org//59763006 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@29957 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index a3661220..0dcc61d6 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,4 +1,5 @@ name: path +version: 0.9.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -7,4 +8,6 @@ description: > homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: - unittest: any + unittest: ">=0.9.0 <0.10.0" +environment: + sdk: ">=1.0.0 <2.0.0" From 4de90fbf7f99bdbfa12b6f4d0df6c66a90cd5162 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Wed, 6 Nov 2013 09:09:18 +0000 Subject: [PATCH 029/183] Revert "add versions and constraints for packages and samples" This is currently blocking us from testing samples. BUG= R=kasperl@google.com Review URL: https://codereview.chromium.org//59513007 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@29960 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 0dcc61d6..a3661220 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,4 @@ name: path -version: 0.9.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -8,6 +7,4 @@ description: > homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: - unittest: ">=0.9.0 <0.10.0" -environment: - sdk: ">=1.0.0 <2.0.0" + unittest: any From ba06b94278a50097980d4ff50ef76452044e88fa Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Wed, 6 Nov 2013 18:28:22 +0000 Subject: [PATCH 030/183] Re-land r29957 (add versions and constraints for packages and samples), with SDK constraints bumped from 1.0.0 to 0.8.10+6 . R=ricow@google.com, sigmund@google.com Review URL: https://codereview.chromium.org//62473002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@29986 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index a3661220..aa905cff 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,4 +1,5 @@ name: path +version: 0.9.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -7,4 +8,6 @@ description: > homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: - unittest: any + unittest: ">=0.9.0 <0.10.0" +environment: + sdk: ">=0.8.10+6 <2.0.0" From ab4bc47078ccb630ab08acb0603d0fa379509eba Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Thu, 7 Nov 2013 08:13:26 +0000 Subject: [PATCH 031/183] Don't use mirrors in pkg/path. BUG= R=kasperl@google.com Review URL: https://codereview.chromium.org//60953003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30038 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 50 ++++++++++---------------------- pkgs/path/test/browser_test.dart | 6 ++-- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index da4c935f..e91310ac 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -46,11 +46,6 @@ /// even when the program is run on a POSIX machine. library path; -@MirrorsUsed(targets: 'dart.dom.html.window, ' - 'dart.io.Directory.current, ' - 'dart.io.Platform.operatingSystem') -import 'dart:mirrors'; - /// An internal builder for the current OS so we can provide a straight /// functional interface and not require users to create one. final _builder = new Builder(); @@ -69,33 +64,20 @@ final url = new Builder(style: Style.url); void _growListFront(List list, int length, fillValue) => list.insertAll(0, new List.filled(length, fillValue)); -/// If we're running in the server-side Dart VM, this will return a -/// [LibraryMirror] that gives access to the `dart:io` library. -/// -/// If `dart:io` is not available, this returns null. -LibraryMirror get _io => currentMirrorSystem().libraries[Uri.parse('dart:io')]; - -// TODO(nweiz): when issue 6490 or 6943 are fixed, make this work under dart2js. -/// If we're running in Dartium, this will return a [LibraryMirror] that gives -/// access to the `dart:html` library. -/// -/// If `dart:html` is not available, this returns null. -LibraryMirror get _html => - currentMirrorSystem().libraries[Uri.parse('dart:html')]; /// Gets the path to the current working directory. /// -/// In the browser, this means the current URL. When using dart2js, this -/// currently returns `.` due to technical constraints. In the future, it will -/// return the current URL. +/// In the browser, this means the current URL, without the last file segment. String get current { - if (_io != null) { - return (_io.declarations[#Directory] as ClassMirror) - .getField(#current).reflectee.path; - } else if (_html != null) { - return _html.getField(#window).reflectee.location.href; + var uri = Uri.base; + if (Style.platform == Style.url) { + return uri.resolve('.').toString(); } else { - return '.'; + var path = uri.toFilePath(); + // Remove trailing '/' or '\'. + int lastIndex = path.length - 1; + assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); + return path.substring(0, lastIndex); } } @@ -866,13 +848,13 @@ abstract class Style { /// Gets the type of the host platform. static Style _getPlatformStyle() { - if (_io == null) return Style.url; - - if ((_io.declarations[#Platform] as ClassMirror).getField(#operatingSystem) - .reflectee == 'windows') { - return Style.windows; - } - + // If we're running a Dart file in the browser from a `file:` URI, + // [Uri.base] will point to a file. If we're running on the standalone, + // it will point to a directory. We can use that fact to determine which + // style to use. + if (Uri.base.scheme != 'file') return Style.url; + if (!Uri.base.path.endsWith('/')) return Style.url; + if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; return Style.posix; } diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 07256469..a6e8a680 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -14,7 +14,8 @@ main() { group('new Builder()', () { test('uses the window location if root and style are omitted', () { var builder = new path.Builder(); - expect(builder.root, window.location.href); + expect(builder.root, + Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { @@ -33,6 +34,7 @@ main() { }); test('current', () { - expect(path.current, window.location.href); + expect(path.current, + Uri.parse(window.location.href).resolve('.').toString()); }); } From 8531c339b4214a00bb7cd071939378608cd60be0 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 13 Nov 2013 23:00:30 +0000 Subject: [PATCH 032/183] Have pkg/path track the current working directory if it changes. R=rnystrom@google.com BUG=11100 Review URL: https://codereview.chromium.org//58903005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30256 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 20 ++++++++++++++++---- pkgs/path/test/io_test.dart | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index e91310ac..7b955e6e 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -46,10 +46,6 @@ /// even when the program is run on a POSIX machine. library path; -/// An internal builder for the current OS so we can provide a straight -/// functional interface and not require users to create one. -final _builder = new Builder(); - /// A default builder for manipulating POSIX paths. final posix = new Builder(style: Style.posix); @@ -64,6 +60,22 @@ final url = new Builder(style: Style.url); void _growListFront(List list, int length, fillValue) => list.insertAll(0, new List.filled(length, fillValue)); +/// The result of [Uri.base] last time the current working directory was +/// calculated. +/// +/// This is used to invalidate [_cachedBuilder] when the working directory has +/// changed since the last time a function was called. +Uri _lastBaseUri; + +/// An internal builder for the current OS so we can provide a straight +/// functional interface and not require users to create one. +Builder get _builder { + if (_cachedBuilder != null && Uri.base == _lastBaseUri) return _cachedBuilder; + _lastBaseUri = Uri.base; + _cachedBuilder = new Builder(); + return _cachedBuilder; +} +Builder _cachedBuilder; /// Gets the path to the current working directory. /// diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 66512495..1b1bea99 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -36,4 +36,18 @@ main() { test('current', () { expect(path.current, io.Directory.current.path); }); + + test('registers changes to the working directory', () { + expect(path.absolute('foo/bar'), + equals(path.join(io.Directory.current.path, 'foo/bar'))); + + var sandbox = io.Directory.systemTemp.createTempSync('path_test_').path; + try { + io.Directory.current = sandbox; + + expect(path.absolute('foo/bar'), equals(path.join(sandbox, 'foo/bar'))); + } finally { + new io.Directory(sandbox).deleteSync(recursive: true); + } + }); } From f362cd8eeda990754260409401ed1e033bfdca7d Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 13 Nov 2013 23:00:50 +0000 Subject: [PATCH 033/183] Properly support UNC paths in pkg/path. This treats the entire "\\server\share" component as the root. R=rnystrom@google.com BUG=7323 Review URL: https://codereview.chromium.org//59133009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30257 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 21 +++++----- pkgs/path/test/windows_test.dart | 66 ++++++++++++++++---------------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 7b955e6e..3c5f51eb 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -981,7 +981,7 @@ class _WindowsStyle extends Style { final separator = '\\'; final separatorPattern = new RegExp(r'[/\\]'); final needsSeparatorPattern = new RegExp(r'[^/\\]$'); - final rootPattern = new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])'); + final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); String pathFromUri(Uri uri) { if (uri.scheme != '' && uri.scheme != 'file') { @@ -1002,23 +1002,22 @@ class _WindowsStyle extends Style { Uri absolutePathToUri(String path) { var parsed = builder._parse(path); - if (parsed.root == r'\\') { - // Network paths become "file://hostname/path/to/file". + if (parsed.root.startsWith(r'\\')) { + // Network paths become "file://server/share/path/to/file". - var host = parsed.parts.removeAt(0); + // The root is of the form "\\server\share". We want "server" to be the + // URI host, and "share" to be the first element of the path. + var rootParts = parsed.root.split('\\').where((part) => part != ''); + parsed.parts.insert(0, rootParts.last); - if (parsed.parts.isEmpty) { - // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will - // currently be empty. We add two empty components so the URL - // constructor produces "file://hostname/", with a trailing slash. - parsed.parts.addAll(["", ""]); - } else if (parsed.hasTrailingSeparator) { + if (parsed.hasTrailingSeparator) { // If the path has a trailing slash, add a single empty component so the // URI has a trailing slash as well. parsed.parts.add(""); } - return new Uri(scheme: 'file', host: host, pathSegments: parsed.parts); + return new Uri(scheme: 'file', host: rootParts.first, + pathSegments: parsed.parts); } else { // Drive-letter paths become "file:///C:/path/to/file". diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 787adfc3..5d4fd04d 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -46,9 +46,7 @@ main() { expect(builder.rootPrefix(r'C:\a\c'), r'C:\'); expect(builder.rootPrefix('C:\\'), r'C:\'); expect(builder.rootPrefix('C:/'), 'C:/'); - - // TODO(nweiz): enable this once issue 7323 is fixed. - // expect(builder.rootPrefix(r'\\server\a\b'), r'\\server\'); + expect(builder.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); }); test('dirname', () { @@ -71,6 +69,8 @@ main() { expect(builder.dirname(r'a\b\\'), 'a'); expect(builder.dirname(r'a\\b'), 'a'); expect(builder.dirname(r'foo bar\gule fisk'), 'foo bar'); + expect(builder.dirname(r'\\server\share'), r'\\server\share'); + expect(builder.dirname(r'\\server\share\dir'), r'\\server\share'); }); test('basename', () { @@ -95,6 +95,8 @@ main() { expect(builder.basename(r'a\\b'), 'b'); expect(builder.basename(r'a\\b'), 'b'); expect(builder.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); + expect(builder.basename(r'\\server\share'), r'\\server\share'); + expect(builder.basename(r'\\server\share\dir'), r'dir'); }); test('basenameWithoutExtension', () { @@ -136,8 +138,8 @@ main() { expect(builder.isAbsolute(r'B:\'), true); expect(builder.isAbsolute(r'c:\a'), true); expect(builder.isAbsolute(r'C:\a'), true); - expect(builder.isAbsolute(r'\\a'), true); - expect(builder.isAbsolute(r'\\'), true); + expect(builder.isAbsolute(r'\\server\share'), true); + expect(builder.isAbsolute(r'\\server\share\path'), true); }); test('isRelative', () { @@ -157,8 +159,8 @@ main() { expect(builder.isRelative(r'B:\'), false); expect(builder.isRelative(r'c:\a'), false); expect(builder.isRelative(r'C:\a'), false); - expect(builder.isRelative(r'\\a'), false); - expect(builder.isRelative(r'\\'), false); + expect(builder.isRelative(r'\\server\share'), false); + expect(builder.isRelative(r'\\server\share\path'), false); }); group('join', () { @@ -184,7 +186,7 @@ main() { test('ignores parts before an absolute path', () { expect(builder.join('a', '/b', '/c', 'd'), r'a/b/c\d'); expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.join('a', r'\\b', r'\\c', 'd'), r'\\c\d'); + expect(builder.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); }); test('ignores trailing nulls', () { @@ -230,7 +232,7 @@ main() { test('ignores parts before an absolute path', () { expect(builder.joinAll(['a', '/b', '/c', 'd']), r'a/b/c\d'); expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); - expect(builder.joinAll(['a', r'\\b', r'\\c', 'd']), r'\\c\d'); + expect(builder.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); }); }); @@ -257,10 +259,9 @@ main() { equals([r'C:\', 'foo', 'bar', 'baz'])); expect(builder.split(r'C:\\'), equals([r'C:\'])); - // TODO(nweiz): enable these once issue 7323 is fixed. - // expect(builder.split(r'\\server\foo\bar\baz'), - // equals([r'\\server\', 'foo', 'bar', 'baz'])); - // expect(builder.split(r'\\server\'), equals([r'\\server\'])); + expect(builder.split(r'\\server\share\foo\bar\baz'), + equals([r'\\server\share', 'foo', 'bar', 'baz'])); + expect(builder.split(r'\\server\share'), equals([r'\\server\share'])); }); }); @@ -274,7 +275,7 @@ main() { expect(builder.normalize('/'), r'.'); expect(builder.normalize('C:/'), r'C:\'); expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\'), r'\\'); + expect(builder.normalize(r'\\server\share'), r'\\server\share'); expect(builder.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); }); @@ -288,8 +289,7 @@ main() { expect(builder.normalize(r'.\'), '.'); expect(builder.normalize(r'c:\.'), r'c:\'); expect(builder.normalize(r'B:\.\'), r'B:\'); - expect(builder.normalize(r'\\.'), r'\\'); - expect(builder.normalize(r'\\.\'), r'\\'); + expect(builder.normalize(r'\\server\share\.'), r'\\server\share'); expect(builder.normalize(r'.\.'), '.'); expect(builder.normalize(r'a\.\b'), r'a\b'); expect(builder.normalize(r'a\.b\c'), r'a\.b\c'); @@ -303,10 +303,9 @@ main() { expect(builder.normalize(r'..\'), '..'); expect(builder.normalize(r'..\..\..'), r'..\..\..'); expect(builder.normalize(r'../..\..\'), r'..\..\..'); - // TODO(rnystrom): Is this how Python handles absolute paths on Windows? - expect(builder.normalize(r'\\..'), r'\\'); - expect(builder.normalize(r'\\..\..\..'), r'\\'); - expect(builder.normalize(r'\\..\../..\a'), r'\\a'); + expect(builder.normalize(r'\\server\share\..'), r'\\server\share'); + expect(builder.normalize(r'\\server\share\..\../..\a'), + r'\\server\share\a'); expect(builder.normalize(r'c:\..'), r'c:\'); expect(builder.normalize(r'A:/..\..\..'), r'A:\'); expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); @@ -434,7 +433,7 @@ main() { test('given absolute with different root prefix', () { expect(builder.relative(r'D:\a\b'), r'D:\a\b'); - expect(builder.relative(r'\\a\b'), r'\\a\b'); + expect(builder.relative(r'\\server\share\a\b'), r'\\server\share\a\b'); }); test('from a . root', () { @@ -466,7 +465,7 @@ main() { test('ignores parts before an absolute path', () { expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\root\path\a/b/c\d'); expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.resolve('a', r'\\b', r'\\c', 'd'), r'\\c\d'); + expect(builder.resolve('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); }); }); @@ -492,19 +491,20 @@ main() { test('fromUri', () { expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo')), - r'\\hostname\path\to\foo'); + expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo')), + r'\\server\share\path\to\foo'); expect(builder.fromUri(Uri.parse('file:///C:/')), r'C:\'); - expect(builder.fromUri(Uri.parse('file://hostname/')), r'\\hostname\'); + expect(builder.fromUri(Uri.parse('file://server/share')), + r'\\server\share'); expect(builder.fromUri(Uri.parse('foo/bar')), r'foo\bar'); expect(builder.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); expect(builder.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('//hostname/path/to/foo')), - r'\\hostname\path\to\foo'); + expect(builder.fromUri(Uri.parse('//server/share/path/to/foo')), + r'\\server\share\path\to\foo'); expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), r'C:\path\to\foo#bar'); - expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo%23bar')), - r'\\hostname\path\to\foo#bar'); + expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), + r'\\server\share\path\to\foo#bar'); expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), @@ -517,12 +517,14 @@ main() { expect(builder.toUri(r'C:\path\to\foo\'), Uri.parse('file:///C:/path/to/foo/')); expect(builder.toUri(r'C:\'), Uri.parse('file:///C:/')); - expect(builder.toUri(r'\\hostname\'), Uri.parse('file://hostname/')); + expect(builder.toUri(r'\\server\share'), Uri.parse('file://server/share')); + expect(builder.toUri(r'\\server\share\'), + Uri.parse('file://server/share/')); expect(builder.toUri(r'foo\bar'), Uri.parse('foo/bar')); expect(builder.toUri(r'C:\path\to\foo#bar'), Uri.parse('file:///C:/path/to/foo%23bar')); - expect(builder.toUri(r'\\hostname\path\to\foo#bar'), - Uri.parse('file://hostname/path/to/foo%23bar')); + expect(builder.toUri(r'\\server\share\path\to\foo#bar'), + Uri.parse('file://server/share/path/to/foo%23bar')); expect(builder.toUri(r'C:\_{_}_`_^_ _"_%_'), Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_')); expect(builder.toUri(r'_{_}_`_^_ _"_%_'), From 36d789984d63360dcc85f18db4ae1f37b2070559 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 14 Nov 2013 00:38:43 +0000 Subject: [PATCH 034/183] Don't rely on a sandbox directory for pkg/path PWD tests. R=rnystrom@google.com Review URL: https://codereview.chromium.org//68893017 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30261 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/test/io_test.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 1b1bea99..2d4ac0c5 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -38,16 +38,15 @@ main() { }); test('registers changes to the working directory', () { - expect(path.absolute('foo/bar'), - equals(path.join(io.Directory.current.path, 'foo/bar'))); - - var sandbox = io.Directory.systemTemp.createTempSync('path_test_').path; + var dir = io.Directory.current.path; try { - io.Directory.current = sandbox; + expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); - expect(path.absolute('foo/bar'), equals(path.join(sandbox, 'foo/bar'))); + io.Directory.current = path.dirname(dir); + expect(path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.join(dir, '../foo/bar')))); } finally { - new io.Directory(sandbox).deleteSync(recursive: true); + io.Directory.current = dir; } }); } From c44d4f60058e9f22d051e463ba21766b0088c594 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 14 Nov 2013 22:50:41 +0000 Subject: [PATCH 035/183] Support root-relative paths on Windows. On Windows, a leading slash in a path means that that path should be relative to the current drive. So for example, `join(r"C:\foo", r"\bar")` should return "C:\bar". R=rnystrom@google.com BUG=14918 Review URL: https://codereview.chromium.org//68503011 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30295 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 13 +++- pkgs/path/test/windows_test.dart | 111 ++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 3c5f51eb..5d490978 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -560,10 +560,13 @@ class Builder { if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { // If the new part is root-relative, it preserves the previous root but // replaces the path after it. - var oldRoot = this.rootPrefix(buffer.toString()); + var parsed = _parse(part); + parsed.root = this.rootPrefix(buffer.toString()); + if (parsed.root.contains(style.needsSeparatorPattern)) { + parsed.separators[0] = style.separator; + } buffer.clear(); - buffer.write(oldRoot); - buffer.write(part); + buffer.write(parsed); } else if (this.isAbsolute(part)) { isAbsoluteAndNotRootRelative = !this.isRootRelative(part); // An absolute path discards everything before it. @@ -983,6 +986,10 @@ class _WindowsStyle extends Style { final needsSeparatorPattern = new RegExp(r'[^/\\]$'); final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + // Matches a back or forward slash that's not followed by another back or + // forward slash. + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + String pathFromUri(Uri uri) { if (uri.scheme != '' && uri.scheme != 'file') { throw new ArgumentError("Uri $uri must have scheme 'file:'."); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 5d4fd04d..8c75474a 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -47,6 +47,10 @@ main() { expect(builder.rootPrefix('C:\\'), r'C:\'); expect(builder.rootPrefix('C:/'), 'C:/'); expect(builder.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); + expect(builder.rootPrefix(r'\a\b'), r'\'); + expect(builder.rootPrefix(r'/a/b'), r'/'); + expect(builder.rootPrefix(r'\'), r'\'); + expect(builder.rootPrefix(r'/'), r'/'); }); test('dirname', () { @@ -71,6 +75,10 @@ main() { expect(builder.dirname(r'foo bar\gule fisk'), 'foo bar'); expect(builder.dirname(r'\\server\share'), r'\\server\share'); expect(builder.dirname(r'\\server\share\dir'), r'\\server\share'); + expect(builder.dirname(r'\a'), r'\'); + expect(builder.dirname(r'/a'), r'/'); + expect(builder.dirname(r'\'), r'\'); + expect(builder.dirname(r'/'), r'/'); }); test('basename', () { @@ -97,6 +105,10 @@ main() { expect(builder.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); expect(builder.basename(r'\\server\share'), r'\\server\share'); expect(builder.basename(r'\\server\share\dir'), r'dir'); + expect(builder.basename(r'\a'), r'a'); + expect(builder.basename(r'/a'), r'a'); + expect(builder.basename(r'\'), r'\'); + expect(builder.basename(r'/'), r'/'); }); test('basenameWithoutExtension', () { @@ -127,8 +139,10 @@ main() { expect(builder.isAbsolute('..'), false); expect(builder.isAbsolute('a'), false); expect(builder.isAbsolute(r'a\b'), false); - expect(builder.isAbsolute(r'\a'), false); - expect(builder.isAbsolute(r'\a\b'), false); + expect(builder.isAbsolute(r'\a\b'), true); + expect(builder.isAbsolute(r'\'), true); + expect(builder.isAbsolute(r'/a/b'), true); + expect(builder.isAbsolute(r'/'), true); expect(builder.isAbsolute('~'), false); expect(builder.isAbsolute('.'), false); expect(builder.isAbsolute(r'..\a'), false); @@ -148,8 +162,10 @@ main() { expect(builder.isRelative('..'), true); expect(builder.isRelative('a'), true); expect(builder.isRelative(r'a\b'), true); - expect(builder.isRelative(r'\a'), true); - expect(builder.isRelative(r'\a\b'), true); + expect(builder.isRelative(r'\a\b'), false); + expect(builder.isRelative(r'\'), false); + expect(builder.isRelative(r'/a/b'), false); + expect(builder.isRelative(r'/'), false); expect(builder.isRelative('~'), true); expect(builder.isRelative('.'), true); expect(builder.isRelative(r'..\a'), true); @@ -163,6 +179,29 @@ main() { expect(builder.isRelative(r'\\server\share\path'), false); }); + test('isRootRelative', () { + expect(builder.isRootRelative(''), false); + expect(builder.isRootRelative('.'), false); + expect(builder.isRootRelative('..'), false); + expect(builder.isRootRelative('a'), false); + expect(builder.isRootRelative(r'a\b'), false); + expect(builder.isRootRelative(r'\a\b'), true); + expect(builder.isRootRelative(r'\'), true); + expect(builder.isRootRelative(r'/a/b'), true); + expect(builder.isRootRelative(r'/'), true); + expect(builder.isRootRelative('~'), false); + expect(builder.isRootRelative('.'), false); + expect(builder.isRootRelative(r'..\a'), false); + expect(builder.isRootRelative(r'a:/a\b'), false); + expect(builder.isRootRelative(r'D:/a/b'), false); + expect(builder.isRootRelative(r'c:\'), false); + expect(builder.isRootRelative(r'B:\'), false); + expect(builder.isRootRelative(r'c:\a'), false); + expect(builder.isRootRelative(r'C:\a'), false); + expect(builder.isRootRelative(r'\\server\share'), false); + expect(builder.isRootRelative(r'\\server\share\path'), false); + }); + group('join', () { test('allows up to eight parts', () { expect(builder.join('a'), 'a'); @@ -179,14 +218,15 @@ main() { test('does not add separator if a part ends or begins in one', () { expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); expect(builder.join('a/', 'b'), r'a/b'); - expect(builder.join('a', '/b'), 'a/b'); - expect(builder.join('a', r'\b'), r'a\b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', '/b', '/c', 'd'), r'a/b/c\d'); + expect(builder.join('a', r'\b', r'\c', 'd'), r'\c\d'); + expect(builder.join('a', '/b', '/c', 'd'), r'/c\d'); expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); expect(builder.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); + expect(builder.join('a', r'c:\b', r'\c', 'd'), r'c:\c\d'); + expect(builder.join('a', r'\\b\c\d', r'\e', 'f'), r'\\b\c\e\f'); }); test('ignores trailing nulls', () { @@ -225,14 +265,15 @@ main() { test('does not add separator if a part ends or begins in one', () { expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); expect(builder.joinAll(['a/', 'b']), r'a/b'); - expect(builder.joinAll(['a', '/b']), 'a/b'); - expect(builder.joinAll(['a', r'\b']), r'a\b'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', '/b', '/c', 'd']), r'a/b/c\d'); + expect(builder.joinAll(['a', r'\b', r'\c', 'd']), r'\c\d'); + expect(builder.joinAll(['a', '/b', '/c', 'd']), r'/c\d'); expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); expect(builder.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); + expect(builder.joinAll(['a', r'c:\b', r'\c', 'd']), r'c:\c\d'); + expect(builder.joinAll(['a', r'\\b\c\d', r'\e', 'f']), r'\\b\c\e\f'); }); }); @@ -262,6 +303,10 @@ main() { expect(builder.split(r'\\server\share\foo\bar\baz'), equals([r'\\server\share', 'foo', 'bar', 'baz'])); expect(builder.split(r'\\server\share'), equals([r'\\server\share'])); + + expect(builder.split(r'\foo\bar\baz'), + equals([r'\', 'foo', 'bar', 'baz'])); + expect(builder.split(r'\'), equals([r'\'])); }); }); @@ -271,8 +316,10 @@ main() { expect(builder.normalize('.'), '.'); expect(builder.normalize('..'), '..'); expect(builder.normalize('a'), 'a'); - expect(builder.normalize(r'\'), '.'); - expect(builder.normalize('/'), r'.'); + expect(builder.normalize('/a/b'), r'\a\b'); + expect(builder.normalize(r'\'), r'\'); + expect(builder.normalize(r'\a\b'), r'\a\b'); + expect(builder.normalize('/'), r'\'); expect(builder.normalize('C:/'), r'C:\'); expect(builder.normalize(r'C:\'), r'C:\'); expect(builder.normalize(r'\\server\share'), r'\\server\share'); @@ -296,6 +343,8 @@ main() { expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c'); expect(builder.normalize(r'.\./a'), 'a'); expect(builder.normalize(r'a/.\.'), 'a'); + expect(builder.normalize(r'\.'), r'\'); + expect(builder.normalize('/.'), r'\'); }); test('eliminates ".." parts', () { @@ -313,8 +362,7 @@ main() { expect(builder.normalize(r'a\..'), '.'); expect(builder.normalize(r'..\a'), r'..\a'); expect(builder.normalize(r'c:\..\a'), r'c:\a'); - // A path starting with '\' is not an absolute path on Windows. - expect(builder.normalize(r'\..\a'), r'..\a'); + expect(builder.normalize(r'\..\a'), r'\a'); expect(builder.normalize(r'a\b\..'), 'a'); expect(builder.normalize(r'..\a\b\..'), r'..\a'); expect(builder.normalize(r'a\..\b'), 'b'); @@ -342,8 +390,11 @@ main() { test('given absolute path in root', () { expect(builder.relative(r'C:\'), r'..\..'); expect(builder.relative(r'C:\root'), '..'); + expect(builder.relative(r'\root'), '..'); expect(builder.relative(r'C:\root\path'), '.'); + expect(builder.relative(r'\root\path'), '.'); expect(builder.relative(r'C:\root\path\a'), 'a'); + expect(builder.relative(r'\root\path\a'), 'a'); expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); expect(builder.relative(r'C:/'), r'..\..'); @@ -354,6 +405,7 @@ main() { test('given absolute path outside of root', () { expect(builder.relative(r'C:\a\b'), r'..\..\a\b'); + expect(builder.relative(r'\a\b'), r'..\..\a\b'); expect(builder.relative(r'C:\root\path\a'), 'a'); expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); @@ -391,6 +443,33 @@ main() { test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); expect(r.relative(r'C:\a\b'), equals(r'C:\a\b')); + expect(r.relative(r'\'), equals(r'\')); + expect(r.relative(r'\a\b'), equals(r'\a\b')); + }); + + test('given relative path', () { + // The path is considered relative to the root, so it basically just + // normalizes. + expect(r.relative(''), '.'); + expect(r.relative('.'), '.'); + expect(r.relative('..'), '..'); + expect(r.relative('a'), 'a'); + expect(r.relative(r'a\b.txt'), r'a\b.txt'); + expect(r.relative(r'..\a/b.txt'), r'..\a\b.txt'); + expect(r.relative(r'a\./b\../c.txt'), r'a\c.txt'); + }); + }); + + group('from root-relative root', () { + var r = new path.Builder(style: path.Style.windows, root: r'\foo\bar'); + + test('given absolute path', () { + expect(r.relative(r'C:\'), equals(r'C:\')); + expect(r.relative(r'C:\a\b'), equals(r'C:\a\b')); + expect(r.relative(r'\'), equals(r'..\..')); + expect(r.relative(r'\a\b'), equals(r'..\..\a\b')); + expect(r.relative('/'), equals(r'..\..')); + expect(r.relative('/a/b'), equals(r'..\..\a\b')); }); test('given relative path', () { @@ -440,6 +519,7 @@ main() { var r = new path.Builder(style: path.Style.windows, root: '.'); expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); + expect(r.relative(r'\foo\bar\baz'), equals(r'\foo\bar\baz')); }); }); @@ -463,7 +543,8 @@ main() { }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\root\path\a/b/c\d'); + expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\c\d'); + expect(builder.resolve('a', r'\b', r'\c', 'd'), r'C:\c\d'); expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); expect(builder.resolve('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); }); From 946bc2e07779b1e6e4ac2c8d5b5fe4cf52002a72 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Fri, 15 Nov 2013 22:05:14 +0000 Subject: [PATCH 036/183] Add isWithin to pkg/path. R=rnystrom@google.com BUG=14980 Review URL: https://codereview.chromium.org//59483008 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30317 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 47 +++++++++++++++++++++++++++++-- pkgs/path/test/posix_test.dart | 25 +++++++++++++++- pkgs/path/test/relative_test.dart | 8 ++++-- pkgs/path/test/url_test.dart | 33 ++++++++++++++++++++++ pkgs/path/test/utils.dart | 11 ++++++++ pkgs/path/test/windows_test.dart | 29 ++++++++++++++++++- 6 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 pkgs/path/test/utils.dart diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 5d490978..5f469edd 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -301,6 +301,13 @@ String normalize(String path) => _builder.normalize(path); String relative(String path, {String from}) => _builder.relative(path, from: from); +/// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. +/// +/// path.isWithin('/root/path', '/root/path/a'); // -> true +/// path.isWithin('/root/path', '/root/other'); // -> false +/// path.isWithin('/root/path', '/root/path') // -> false +bool isWithin(String parent, String child) => _builder.isWithin(parent, child); + /// Removes a trailing extension from the last part of [path]. /// /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' @@ -664,6 +671,11 @@ class Builder { /// /// var builder = new Builder(r'some/relative/path'); /// builder.relative(r'/absolute/path'); // -> '/absolute/path' + /// + /// If [root] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is + /// "/", no path can be determined. In this case, a [PathException] will be + /// thrown. String relative(String path, {String from}) { from = from == null ? root : this.join(root, from); @@ -681,7 +693,7 @@ class Builder { // If the path is still relative and `from` is absolute, we're unable to // find a path from `from` to `path`. if (this.isRelative(path) && this.isAbsolute(from)) { - throw new ArgumentError('Unable to find a path to "$path" from "$from".'); + throw new PathException('Unable to find a path to "$path" from "$from".'); } var fromParsed = _parse(from)..normalize(); @@ -715,7 +727,7 @@ class Builder { // out of them. If a directory left in the from path is '..', it cannot // be cancelled by adding a '..'. if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { - throw new ArgumentError('Unable to find a path to "$path" from "$from".'); + throw new PathException('Unable to find a path to "$path" from "$from".'); } _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); pathParsed.separators[0] = ''; @@ -739,6 +751,27 @@ class Builder { return pathParsed.toString(); } + /// Returns `true` if [child] is a path beneath `parent`, and `false` + /// otherwise. + /// + /// path.isWithin('/root/path', '/root/path/a'); // -> true + /// path.isWithin('/root/path', '/root/other'); // -> false + /// path.isWithin('/root/path', '/root/path') // -> false + bool isWithin(String parent, String child) { + var relative; + try { + relative = this.relative(child, from: parent); + } on PathException catch (_) { + // If no relative path from [parent] to [child] is found, [child] + // definitely isn't a child of [parent]. + return false; + } + + var parts = this.split(relative); + return this.isRelative(relative) && parts.first != '..' && + parts.first != '.'; + } + /// Removes a trailing extension from the last part of [path]. /// /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' @@ -1204,3 +1237,13 @@ class _ParsedPath { style, root, isRootRelative, new List.from(parts), new List.from(separators)); } + +/// An exception class that's thrown when a path operation is unable to be +/// computed accurately. +class PathException implements Exception { + String message; + + PathException(this.message); + + String toString() => "PathException: $message"; +} diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 1156379a..b693fcf4 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -7,6 +7,8 @@ library path.test.posix_test; import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; +import 'utils.dart'; + main() { var builder = new path.Builder(style: path.Style.posix, root: '/root/path'); @@ -398,7 +400,7 @@ main() { test('with a root parameter and a relative root', () { var r = new path.Builder(style: path.Style.posix, root: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(() => r.relative('..', from: '/foo/bar'), throwsArgumentError); + expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); expect(r.relative('/foo/bar/baz', from: 'foo/bar'), equals('/foo/bar/baz')); expect(r.relative('..', from: 'foo/bar'), equals('../../..')); @@ -411,6 +413,27 @@ main() { }); }); + group('isWithin', () { + test('simple cases', () { + expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(builder.isWithin('/', '/foo/bar'), isTrue); + expect(builder.isWithin('baz', '/root/path/baz/bang'), isTrue); + expect(builder.isWithin('baz', '/root/path/bang/baz'), isFalse); + }); + + test('from a relative root', () { + var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); + expect(builder.isWithin('.', 'a/b/c'), isTrue); + expect(builder.isWithin('.', '../a/b/c'), isFalse); + expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(builder.isWithin('/', '/baz/bang'), isTrue); + expect(builder.isWithin('.', '/baz/bang'), isFalse); + }); + }); + group('resolve', () { test('allows up to seven parts', () { expect(builder.resolve('a'), '/root/path/a'); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index b9cd254b..e9762f47 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -7,6 +7,8 @@ import "package:unittest/unittest.dart"; import "package:path/path.dart" as path; +import "utils.dart"; + void main() { test("test relative", () { relativeTest(new path.Builder(style: path.Style.posix, root: '.'), '/'); @@ -75,11 +77,11 @@ void relativeTest(path.Builder builder, String prefix) { // Should always throw - no relative path can be constructed. if (isRelative) { - expect(() => builder.relative('.', from: '..'), throwsArgumentError); + expect(() => builder.relative('.', from: '..'), throwsPathException); expect(() => builder.relative('a/b', from: '../../d'), - throwsArgumentError); + throwsPathException); expect(() => builder.relative('a/b', from: '${prefix}a/b'), - throwsArgumentError); + throwsPathException); // An absolute path relative from a relative path returns the absolute path. expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index d43e88c4..9fb6f079 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -615,6 +615,39 @@ main() { }); }); + group('isWithin', () { + test('simple cases', () { + expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(builder.isWithin( + 'http://dartlang.org', 'http://dartlang.org/foo/bar'), + isTrue); + expect(builder.isWithin( + 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), + isFalse); + expect(builder.isWithin('http://dartlang.org', '/foo/bar'), isTrue); + expect(builder.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); + expect(builder.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); + expect(builder.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), + isTrue); + expect(builder.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), + isFalse); + }); + + test('from a relative root', () { + var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); + expect(builder.isWithin('.', 'a/b/c'), isTrue); + expect(builder.isWithin('.', '../a/b/c'), isFalse); + expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(builder.isWithin( + 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), + isTrue); + expect(builder.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); + }); + }); + group('resolve', () { test('allows up to seven parts', () { expect(builder.resolve('a'), 'http://dartlang.org/root/path/a'); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart new file mode 100644 index 00000000..1730798f --- /dev/null +++ b/pkgs/path/test/utils.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.test.utils; + +import "package:unittest/unittest.dart"; +import "package:path/path.dart" as path; + +/// A matcher for a closure that throws a [path.PathException]. +final throwsPathException = throwsA(new isInstanceOf()); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 8c75474a..f1a2395e 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -7,6 +7,8 @@ library path.test.windows_test; import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; +import 'utils.dart'; + main() { var builder = new path.Builder(style: path.Style.windows, root: r'C:\root\path'); @@ -504,7 +506,7 @@ main() { test('with a root parameter and a relative root', () { var r = new path.Builder(style: path.Style.windows, root: r'relative\root'); expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); - expect(() => r.relative('..', from: r'C:\foo\bar'), throwsArgumentError); + expect(() => r.relative('..', from: r'C:\foo\bar'), throwsPathException); expect(r.relative(r'C:\foo\bar\baz', from: r'foo\bar'), equals(r'C:\foo\bar\baz')); expect(r.relative('..', from: r'foo\bar'), equals(r'..\..\..')); @@ -523,6 +525,31 @@ main() { }); }); + group('isWithin', () { + test('simple cases', () { + expect(builder.isWithin(r'foo\bar', r'foo\bar'), isFalse); + expect(builder.isWithin(r'foo\bar', r'foo\bar\baz'), isTrue); + expect(builder.isWithin(r'foo\bar', r'foo\baz'), isFalse); + expect(builder.isWithin(r'foo\bar', r'..\path\foo\bar\baz'), isTrue); + expect(builder.isWithin(r'C:\', r'C:\foo\bar'), isTrue); + expect(builder.isWithin(r'C:\', r'D:\foo\bar'), isFalse); + expect(builder.isWithin(r'C:\', r'\foo\bar'), isTrue); + expect(builder.isWithin(r'C:\foo', r'\foo\bar'), isTrue); + expect(builder.isWithin(r'C:\foo', r'\bar\baz'), isFalse); + expect(builder.isWithin(r'baz', r'C:\root\path\baz\bang'), isTrue); + expect(builder.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); + }); + + test('from a relative root', () { + var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); + expect(builder.isWithin('.', r'a\b\c'), isTrue); + expect(builder.isWithin('.', r'..\a\b\c'), isFalse); + expect(builder.isWithin('.', r'..\..\a\foo\b\c'), isFalse); + expect(builder.isWithin(r'C:\', r'C:\baz\bang'), isTrue); + expect(builder.isWithin('.', r'C:\baz\bang'), isFalse); + }); + }); + group('resolve', () { test('allows up to seven parts', () { expect(builder.resolve('a'), r'C:\root\path\a'); From 59f3c6aaa7c8ff076349adc8790582407d17a15a Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 19 Nov 2013 20:48:45 +0000 Subject: [PATCH 037/183] Refactor pkg/path. This CL does several things: * Splits lib/path.dart into multiple libraries. * Renames "Builder" to "Context". * Makes the Context API match the top-level functions ("root" was renamed to "current" and "resolve" was renamed to "absolute"). * The top-level "absolute" function now takes multiple parts, to match [Context.absolute]. R=rnystrom@google.com BUG=14981 Review URL: https://codereview.chromium.org//62753005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30423 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/README.md | 8 +- pkgs/path/lib/path.dart | 988 ++------------------------ pkgs/path/lib/src/context.dart | 492 +++++++++++++ pkgs/path/lib/src/parsed_path.dart | 186 +++++ pkgs/path/lib/src/path_exception.dart | 15 + pkgs/path/lib/src/style.dart | 118 +++ pkgs/path/lib/src/style/posix.dart | 42 ++ pkgs/path/lib/src/style/url.dart | 25 + pkgs/path/lib/src/style/windows.dart | 74 ++ pkgs/path/test/browser_test.dart | 14 +- pkgs/path/test/io_test.dart | 14 +- pkgs/path/test/path_test.dart | 24 +- pkgs/path/test/posix_test.dart | 608 ++++++++-------- pkgs/path/test/relative_test.dart | 30 +- pkgs/path/test/url_test.dart | 812 ++++++++++----------- pkgs/path/test/windows_test.dart | 770 ++++++++++---------- 16 files changed, 2136 insertions(+), 2084 deletions(-) create mode 100644 pkgs/path/lib/src/context.dart create mode 100644 pkgs/path/lib/src/parsed_path.dart create mode 100644 pkgs/path/lib/src/path_exception.dart create mode 100644 pkgs/path/lib/src/style.dart create mode 100644 pkgs/path/lib/src/style/posix.dart create mode 100644 pkgs/path/lib/src/style/url.dart create mode 100644 pkgs/path/lib/src/style/windows.dart diff --git a/pkgs/path/README.md b/pkgs/path/README.md index eff60de4..2e3eec1b 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -7,7 +7,7 @@ We've tried very hard to make this library do the "right" thing on whatever platform you run it on, including in the browser. When you use the top-level functions, it will assume the current platform's path style and work with that. If you want to explicitly work with paths of a specific style, you can -construct a `path.Builder` for that style. +construct a `path.Context` for that style. ## Using @@ -27,10 +27,10 @@ This calls the top-level [join] function to join "directory" and If you want to work with paths for a specific platform regardless of the underlying platform that the program is running on, you can create a -[Builder] and give it an explicit [Style]: +[Context] and give it an explicit [Style]: - var builder = new path.Builder(style: Style.windows); - builder.join("directory", "file.txt"); + var context = new path.Context(style: Style.windows); + context.join("directory", "file.txt"); This will join "directory" and "file.txt" using the Windows path separator, even when the program is run on a POSIX machine. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 5f469edd..6f522c5b 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -37,45 +37,47 @@ /// /// If you want to work with paths for a specific platform regardless of the /// underlying platform that the program is running on, you can create a -/// [Builder] and give it an explicit [Style]: +/// [Context] and give it an explicit [Style]: /// -/// var builder = new path.Builder(style: Style.windows); -/// builder.join("directory", "file.txt"); +/// var context = new path.Context(style: Style.windows); +/// context.join("directory", "file.txt"); /// /// This will join "directory" and "file.txt" using the Windows path separator, /// even when the program is run on a POSIX machine. library path; -/// A default builder for manipulating POSIX paths. -final posix = new Builder(style: Style.posix); +import 'src/context.dart'; +import 'src/style.dart'; -/// A default builder for manipulating Windows paths. -final windows = new Builder(style: Style.windows); +export 'src/context.dart'; +export 'src/path_exception.dart'; +export 'src/style.dart'; -/// A default builder for manipulating URLs. -final url = new Builder(style: Style.url); +/// A default context for manipulating POSIX paths. +final posix = new Context(style: Style.posix); -/// Inserts [length] elements in front of the [list] and fills them with the -/// [fillValue]. -void _growListFront(List list, int length, fillValue) => - list.insertAll(0, new List.filled(length, fillValue)); +/// A default context for manipulating Windows paths. +final windows = new Context(style: Style.windows); + +/// A default context for manipulating URLs. +final url = new Context(style: Style.url); /// The result of [Uri.base] last time the current working directory was /// calculated. /// -/// This is used to invalidate [_cachedBuilder] when the working directory has +/// This is used to invalidate [_cachedContext] when the working directory has /// changed since the last time a function was called. Uri _lastBaseUri; -/// An internal builder for the current OS so we can provide a straight +/// An internal context for the current OS so we can provide a straight /// functional interface and not require users to create one. -Builder get _builder { - if (_cachedBuilder != null && Uri.base == _lastBaseUri) return _cachedBuilder; +Context get _context { + if (_cachedContext != null && Uri.base == _lastBaseUri) return _cachedContext; _lastBaseUri = Uri.base; - _cachedBuilder = new Builder(); - return _cachedBuilder; + _cachedContext = new Context(); + return _cachedContext; } -Builder _cachedBuilder; +Context _cachedContext; /// Gets the path to the current working directory. /// @@ -95,13 +97,15 @@ String get current { /// Gets the path separator for the current platform. This is `\` on Windows /// and `/` on other platforms (including the browser). -String get separator => _builder.separator; +String get separator => _context.separator; -/// Converts [path] to an absolute path by resolving it relative to the current -/// working directory. If [path] is already an absolute path, just returns it. +/// Creates a new path by appending the given path parts to [current]. +/// Equivalent to [join()] with [current] as the first argument. Example: /// -/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt -String absolute(String path) => join(current, path); +/// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) => + _context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// @@ -110,8 +114,8 @@ String absolute(String path) => join(current, path); /// /// Trailing separators are ignored. /// -/// builder.basename('path/to/'); // -> 'to' -String basename(String path) => _builder.basename(path); +/// path.basename('path/to/'); // -> 'to' +String basename(String path) => _context.basename(path); /// Gets the part of [path] after the last separator, and without any trailing /// file extension. @@ -120,9 +124,9 @@ String basename(String path) => _builder.basename(path); /// /// Trailing separators are ignored. /// -/// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' +/// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => - _builder.basenameWithoutExtension(path); + _context.basenameWithoutExtension(path); /// Gets the part of [path] before the last separator. /// @@ -131,7 +135,7 @@ String basenameWithoutExtension(String path) => /// /// Trailing separators are ignored. /// -/// builder.dirname('path/to/'); // -> 'path' +/// path.dirname('path/to/'); // -> 'path' /// /// If an absolute path contains no directories, only a root, then the root /// is returned. @@ -143,7 +147,7 @@ String basenameWithoutExtension(String path) => /// /// path.dirname('foo'); // -> '.' /// path.dirname(''); // -> '.' -String dirname(String path) => _builder.dirname(path); +String dirname(String path) => _context.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last /// `.` to the end (including the `.` itself). @@ -158,7 +162,7 @@ String dirname(String path) => _builder.dirname(path); /// /// path.extension('~/.bashrc'); // -> '' /// path.extension('~/.notes.txt'); // -> '.txt' -String extension(String path) => _builder.extension(path); +String extension(String path) => _context.extension(path); // 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 @@ -176,7 +180,7 @@ String extension(String path) => _builder.extension(path); /// path.rootPrefix('path/to/foo'); // -> '' /// path.rootPrefix('http://dartlang.org/path/to/foo'); /// // -> 'http://dartlang.org' -String rootPrefix(String path) => _builder.rootPrefix(path); +String rootPrefix(String path) => _context.rootPrefix(path); /// Returns `true` if [path] is an absolute path and `false` if it is a /// relative path. @@ -190,13 +194,13 @@ String rootPrefix(String path) => _builder.rootPrefix(path); /// relative to the root of the current URL. Since root-relative paths are still /// absolute in every other sense, [isAbsolute] will return true for them. They /// can be detected using [isRootRelative]. -bool isAbsolute(String path) => _builder.isAbsolute(path); +bool isAbsolute(String path) => _context.isAbsolute(path); /// Returns `true` if [path] is a relative path and `false` if it is absolute. /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. -bool isRelative(String path) => _builder.isRelative(path); +bool isRelative(String path) => _context.isRelative(path); /// Returns `true` if [path] is a root-relative path and `false` if it's not. /// @@ -206,7 +210,7 @@ bool isRelative(String path) => _builder.isRelative(path); /// can be detected using [isRootRelative]. /// /// No POSIX and Windows paths are root-relative. -bool isRootRelative(String path) => _builder.isRootRelative(path); +bool isRootRelative(String path) => _context.isRootRelative(path); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -223,7 +227,7 @@ bool isRootRelative(String path) => _builder.isRootRelative(path); /// path.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) => - _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); + _context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -240,7 +244,7 @@ String join(String part1, [String part2, String part3, String part4, /// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' /// /// For a fixed number of parts, [join] is usually terser. -String joinAll(Iterable parts) => _builder.joinAll(parts); +String joinAll(Iterable parts) => _context.joinAll(parts); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. @@ -263,13 +267,13 @@ String joinAll(Iterable parts) => _builder.joinAll(parts); /// // Browser /// path.split('http://dartlang.org/path/to/foo'); /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] -List split(String path) => _builder.split(path); +List split(String path) => _context.split(path); /// Normalizes [path], simplifying it by handling `..`, and `.`, and /// removing redundant path separators whenever possible. /// /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' -String normalize(String path) => _builder.normalize(path); +String normalize(String path) => _context.normalize(path); /// Attempts to convert [path] to an equivalent relative path from the current /// directory. @@ -299,19 +303,19 @@ String normalize(String path) => _builder.normalize(path); /// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); /// // -> 'http://dartlang.org' String relative(String path, {String from}) => - _builder.relative(path, from: from); + _context.relative(path, from: from); /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. /// /// path.isWithin('/root/path', '/root/path/a'); // -> true /// path.isWithin('/root/path', '/root/other'); // -> false /// path.isWithin('/root/path', '/root/path') // -> false -bool isWithin(String parent, String child) => _builder.isWithin(parent, child); +bool isWithin(String parent, String child) => _context.isWithin(parent, child); /// Removes a trailing extension from the last part of [path]. /// /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' -String withoutExtension(String path) => _builder.withoutExtension(path); +String withoutExtension(String path) => _context.withoutExtension(path); /// Returns the path represented by [uri]. /// @@ -329,7 +333,7 @@ String withoutExtension(String path) => _builder.withoutExtension(path); /// // URL /// path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) /// // -> 'http://dartlang.org/path/to/foo' -String fromUri(Uri uri) => _builder.fromUri(uri); +String fromUri(Uri uri) => _context.fromUri(uri); /// Returns the URI that represents [path]. /// @@ -352,898 +356,4 @@ String fromUri(Uri uri) => _builder.fromUri(uri); /// /// path.toUri('path/to/foo') /// // -> Uri.parse('path/to/foo') -Uri toUri(String path) => _builder.toUri(path); - -/// Validates that there are no non-null arguments following a null one and -/// throws an appropriate [ArgumentError] on failure. -_validateArgList(String method, List args) { - for (var i = 1; i < args.length; i++) { - // Ignore nulls hanging off the end. - if (args[i] == null || args[i - 1] != null) continue; - - var numArgs; - for (numArgs = args.length; numArgs >= 1; numArgs--) { - if (args[numArgs - 1] != null) break; - } - - // Show the arguments. - var message = new StringBuffer(); - message.write("$method("); - message.write(args.take(numArgs) - .map((arg) => arg == null ? "null" : '"$arg"') - .join(", ")); - message.write("): part ${i - 1} was null, but part $i was not."); - throw new ArgumentError(message.toString()); - } -} - -/// An instantiable class for manipulating paths. Unlike the top-level -/// functions, this lets you explicitly select what platform the paths will use. -class Builder { - /// Creates a new path builder for the given style and root directory. - /// - /// If [style] is omitted, it uses the host operating system's path style. If - /// only [root] is omitted, it defaults ".". If *both* [style] and [root] are - /// omitted, [root] defaults to the current working directory. - /// - /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults - /// to the current URL. When using dart2js, it currently defaults to `.` due - /// to technical constraints. - factory Builder({Style style, String root}) { - if (root == null) { - if (style == null) { - root = current; - } else { - root = "."; - } - } - - if (style == null) style = Style.platform; - - return new Builder._(style, root); - } - - Builder._(this.style, this.root); - - /// The style of path that this builder works with. - final Style style; - - /// The root directory that relative paths will be relative to. - final String root; - - /// Gets the path separator for the builder's [style]. On Mac and Linux, - /// this is `/`. On Windows, it's `\`. - String get separator => style.separator; - - /// Gets the part of [path] after the last separator on the builder's - /// platform. - /// - /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' - /// builder.basename('path/to'); // -> 'to' - /// - /// Trailing separators are ignored. - /// - /// builder.basename('path/to/'); // -> 'to' - String basename(String path) => _parse(path).basename; - - /// Gets the part of [path] after the last separator on the builder's - /// platform, and without any trailing file extension. - /// - /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' - /// - /// Trailing separators are ignored. - /// - /// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' - String basenameWithoutExtension(String path) => - _parse(path).basenameWithoutExtension; - - /// Gets the part of [path] before the last separator. - /// - /// builder.dirname('path/to/foo.dart'); // -> 'path/to' - /// builder.dirname('path/to'); // -> 'path' - /// - /// Trailing separators are ignored. - /// - /// builder.dirname('path/to/'); // -> 'path' - String dirname(String path) { - var parsed = _parse(path); - parsed.removeTrailingSeparators(); - if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; - if (parsed.parts.length == 1) { - return parsed.root == null ? '.' : parsed.root; - } - parsed.parts.removeLast(); - parsed.separators.removeLast(); - parsed.removeTrailingSeparators(); - return parsed.toString(); - } - - /// Gets the file extension of [path]: the portion of [basename] from the last - /// `.` to the end (including the `.` itself). - /// - /// builder.extension('path/to/foo.dart'); // -> '.dart' - /// builder.extension('path/to/foo'); // -> '' - /// builder.extension('path.to/foo'); // -> '' - /// builder.extension('path/to/foo.dart.js'); // -> '.js' - /// - /// If the file name starts with a `.`, then it is not considered an - /// extension: - /// - /// builder.extension('~/.bashrc'); // -> '' - /// builder.extension('~/.notes.txt'); // -> '.txt' - String extension(String path) => _parse(path).extension; - - // 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 - /// relative. - /// - /// // Unix - /// builder.rootPrefix('path/to/foo'); // -> '' - /// builder.rootPrefix('/path/to/foo'); // -> '/' - /// - /// // Windows - /// builder.rootPrefix(r'path\to\foo'); // -> '' - /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' - /// - /// // URL - /// builder.rootPrefix('path/to/foo'); // -> '' - /// builder.rootPrefix('http://dartlang.org/path/to/foo'); - /// // -> 'http://dartlang.org' - String rootPrefix(String path) { - var root = _parse(path).root; - return root == null ? '' : root; - } - - /// Returns `true` if [path] is an absolute path and `false` if it is a - /// relative path. - /// - /// On POSIX systems, absolute paths start with a `/` (forward slash). On - /// Windows, an absolute path starts with `\\`, or a drive letter followed by - /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and - /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. - /// - /// URLs that start with `/` are known as "root-relative", since they're - /// relative to the root of the current URL. Since root-relative paths are - /// still absolute in every other sense, [isAbsolute] will return true for - /// them. They can be detected using [isRootRelative]. - bool isAbsolute(String path) => _parse(path).isAbsolute; - - /// Returns `true` if [path] is a relative path and `false` if it is absolute. - /// On POSIX systems, absolute paths start with a `/` (forward slash). On - /// Windows, an absolute path starts with `\\`, or a drive letter followed by - /// `:/` or `:\`. - bool isRelative(String path) => !this.isAbsolute(path); - - /// Returns `true` if [path] is a root-relative path and `false` if it's not. - /// - /// URLs that start with `/` are known as "root-relative", since they're - /// relative to the root of the current URL. Since root-relative paths are - /// still absolute in every other sense, [isAbsolute] will return true for - /// them. They can be detected using [isRootRelative]. - /// - /// No POSIX and Windows paths are root-relative. - bool isRootRelative(String path) => _parse(path).isRootRelative; - - /// Joins the given path parts into a single path. Example: - /// - /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' - /// - /// If any part ends in a path separator, then a redundant separator will not - /// be added: - /// - /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo - /// - /// If a part is an absolute path, then anything before that will be ignored: - /// - /// builder.join('path', '/to', 'foo'); // -> '/to/foo' - /// - String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) { - var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; - _validateArgList("join", parts); - return joinAll(parts.where((part) => part != null)); - } - - /// Joins the given path parts into a single path. Example: - /// - /// builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' - /// - /// If any part ends in a path separator, then a redundant separator will not - /// be added: - /// - /// builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo - /// - /// If a part is an absolute path, then anything before that will be ignored: - /// - /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo' - /// - /// For a fixed number of parts, [join] is usually terser. - String joinAll(Iterable parts) { - var buffer = new StringBuffer(); - var needsSeparator = false; - var isAbsoluteAndNotRootRelative = false; - - for (var part in parts.where((part) => part != '')) { - if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { - // If the new part is root-relative, it preserves the previous root but - // replaces the path after it. - var parsed = _parse(part); - parsed.root = this.rootPrefix(buffer.toString()); - if (parsed.root.contains(style.needsSeparatorPattern)) { - parsed.separators[0] = style.separator; - } - buffer.clear(); - buffer.write(parsed); - } else if (this.isAbsolute(part)) { - isAbsoluteAndNotRootRelative = !this.isRootRelative(part); - // An absolute path discards everything before it. - buffer.clear(); - buffer.write(part); - } else { - if (part.length > 0 && part[0].contains(style.separatorPattern)) { - // The part starts with a separator, so we don't need to add one. - } else if (needsSeparator) { - buffer.write(separator); - } - - buffer.write(part); - } - - // Unless this part ends with a separator, we'll need to add one before - // the next part. - needsSeparator = part.contains(style.needsSeparatorPattern); - } - - return buffer.toString(); - } - - // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. - /// Splits [path] into its components using the current platform's - /// [separator]. Example: - /// - /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo'] - /// - /// The path will *not* be normalized before splitting. - /// - /// builder.split('path/../foo'); // -> ['path', '..', 'foo'] - /// - /// If [path] is absolute, the root directory will be the first element in the - /// array. Example: - /// - /// // Unix - /// builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] - /// - /// // Windows - /// builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] - List split(String path) { - var parsed = _parse(path); - // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.where((part) => !part.isEmpty) - .toList(); - if (parsed.root != null) parsed.parts.insert(0, parsed.root); - return parsed.parts; - } - - /// Normalizes [path], simplifying it by handling `..`, and `.`, and - /// removing redundant path separators whenever possible. - /// - /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt' - String normalize(String path) { - var parsed = _parse(path); - parsed.normalize(); - return parsed.toString(); - } - - /// Creates a new path by appending the given path parts to the [root]. - /// Equivalent to [join()] with [root] as the first argument. Example: - /// - /// var builder = new Builder(root: 'root'); - /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' - String resolve(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) { - return join(root, part1, part2, part3, part4, part5, part6, part7); - } - - /// Attempts to convert [path] to an equivalent relative path relative to - /// [root]. - /// - /// var builder = new Builder(root: '/root/path'); - /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart' - /// builder.relative('/root/other.dart'); // -> '../other.dart' - /// - /// If the [from] argument is passed, [path] is made relative to that instead. - /// - /// builder.relative('/root/path/a/b.dart', - /// from: '/root/path'); // -> 'a/b.dart' - /// builder.relative('/root/other.dart', - /// from: '/root/path'); // -> '../other.dart' - /// - /// If [path] and/or [from] are relative paths, they are assumed to be - /// relative to [root]. - /// - /// Since there is no relative path from one drive letter to another on - /// Windows, this will return an absolute path in that case. - /// - /// builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' - /// - /// This will also return an absolute path if an absolute [path] is passed to - /// a builder with a relative [root]. - /// - /// var builder = new Builder(r'some/relative/path'); - /// builder.relative(r'/absolute/path'); // -> '/absolute/path' - /// - /// If [root] is relative, it may be impossible to determine a path from - /// [from] to [path]. For example, if [root] and [path] are "." and [from] is - /// "/", no path can be determined. In this case, a [PathException] will be - /// thrown. - String relative(String path, {String from}) { - from = from == null ? root : this.join(root, from); - - // We can't determine the path from a relative path to an absolute path. - if (this.isRelative(from) && this.isAbsolute(path)) { - return this.normalize(path); - } - - // If the given path is relative, resolve it relative to the root of the - // builder. - if (this.isRelative(path) || this.isRootRelative(path)) { - path = this.resolve(path); - } - - // If the path is still relative and `from` is absolute, we're unable to - // find a path from `from` to `path`. - if (this.isRelative(path) && this.isAbsolute(from)) { - throw new PathException('Unable to find a path to "$path" from "$from".'); - } - - var fromParsed = _parse(from)..normalize(); - var pathParsed = _parse(path)..normalize(); - - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { - return pathParsed.toString(); - } - - // If the root prefixes don't match (for example, different drive letters - // on Windows), then there is no relative path, so just return the absolute - // one. In Windows, drive letters are case-insenstive and we allow - // calculation of relative paths, even if a path has not been normalized. - if (fromParsed.root != pathParsed.root && - ((fromParsed.root == null || pathParsed.root == null) || - fromParsed.root.toLowerCase().replaceAll('/', '\\') != - pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { - return pathParsed.toString(); - } - - // Strip off their common prefix. - while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && - fromParsed.parts[0] == pathParsed.parts[0]) { - fromParsed.parts.removeAt(0); - fromParsed.separators.removeAt(1); - pathParsed.parts.removeAt(0); - pathParsed.separators.removeAt(1); - } - - // If there are any directories left in the from path, we need to walk up - // out of them. If a directory left in the from path is '..', it cannot - // be cancelled by adding a '..'. - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { - throw new PathException('Unable to find a path to "$path" from "$from".'); - } - _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); - pathParsed.separators[0] = ''; - pathParsed.separators.insertAll(1, - new List.filled(fromParsed.parts.length, style.separator)); - - // Corner case: the paths completely collapsed. - if (pathParsed.parts.length == 0) return '.'; - - // Corner case: path was '.' and some '..' directories were added in front. - // Don't add a final '/.' in that case. - if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { - pathParsed.parts.removeLast(); - pathParsed.separators..removeLast()..removeLast()..add(''); - } - - // Make it relative. - pathParsed.root = ''; - pathParsed.removeTrailingSeparators(); - - return pathParsed.toString(); - } - - /// Returns `true` if [child] is a path beneath `parent`, and `false` - /// otherwise. - /// - /// path.isWithin('/root/path', '/root/path/a'); // -> true - /// path.isWithin('/root/path', '/root/other'); // -> false - /// path.isWithin('/root/path', '/root/path') // -> false - bool isWithin(String parent, String child) { - var relative; - try { - relative = this.relative(child, from: parent); - } on PathException catch (_) { - // If no relative path from [parent] to [child] is found, [child] - // definitely isn't a child of [parent]. - return false; - } - - var parts = this.split(relative); - return this.isRelative(relative) && parts.first != '..' && - parts.first != '.'; - } - - /// Removes a trailing extension from the last part of [path]. - /// - /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' - String withoutExtension(String path) { - var parsed = _parse(path); - - for (var i = parsed.parts.length - 1; i >= 0; i--) { - if (!parsed.parts[i].isEmpty) { - parsed.parts[i] = parsed.basenameWithoutExtension; - break; - } - } - - return parsed.toString(); - } - - /// Returns the path represented by [uri]. - /// - /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL - /// style, this will just convert [uri] to a string. - /// - /// // POSIX - /// builder.fromUri(Uri.parse('file:///path/to/foo')) - /// // -> '/path/to/foo' - /// - /// // Windows - /// builder.fromUri(Uri.parse('file:///C:/path/to/foo')) - /// // -> r'C:\path\to\foo' - /// - /// // URL - /// builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) - /// // -> 'http://dartlang.org/path/to/foo' - String fromUri(Uri uri) => style.pathFromUri(uri); - - /// Returns the URI that represents [path]. - /// - /// For POSIX and Windows styles, this will return a `file:` URI. For the URL - /// style, this will just convert [path] to a [Uri]. - /// - /// // POSIX - /// builder.toUri('/path/to/foo') - /// // -> Uri.parse('file:///path/to/foo') - /// - /// // Windows - /// builder.toUri(r'C:\path\to\foo') - /// // -> Uri.parse('file:///C:/path/to/foo') - /// - /// // URL - /// builder.toUri('http://dartlang.org/path/to/foo') - /// // -> Uri.parse('http://dartlang.org/path/to/foo') - Uri toUri(String path) { - if (isRelative(path)) { - return style.relativePathToUri(path); - } else { - return style.absolutePathToUri(join(root, path)); - } - } - - _ParsedPath _parse(String path) { - var before = path; - - // Remove the root prefix, if any. - var root = style.getRoot(path); - var isRootRelative = style.getRelativeRoot(path) != null; - if (root != null) path = path.substring(root.length); - - // Split the parts on path separators. - var parts = []; - var separators = []; - - var firstSeparator = style.separatorPattern.matchAsPrefix(path); - if (firstSeparator != null) { - separators.add(firstSeparator[0]); - path = path.substring(firstSeparator[0].length); - } else { - separators.add(''); - } - - var start = 0; - for (var match in style.separatorPattern.allMatches(path)) { - parts.add(path.substring(start, match.start)); - separators.add(match[0]); - start = match.end; - } - - // Add the final part, if any. - if (start < path.length) { - parts.add(path.substring(start)); - separators.add(''); - } - - return new _ParsedPath(style, root, isRootRelative, parts, separators); - } -} - -/// An enum type describing a "flavor" of path. -abstract class Style { - /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths - /// start with "/". Used by UNIX, Linux, Mac OS X, and others. - static final posix = new _PosixStyle(); - - /// Windows paths use "\" (backslash) as separators. Absolute paths start with - /// a drive letter followed by a colon (example, "C:") or two backslashes - /// ("\\") for UNC paths. - // TODO(rnystrom): The UNC root prefix should include the drive name too, not - // just the "\\". - static final windows = new _WindowsStyle(); - - /// URLs aren't filesystem paths, but they're supported to make it easier to - /// manipulate URL paths in the browser. - /// - /// URLs use "/" (forward slash) as separators. Absolute paths either start - /// with a protocol and optional hostname (e.g. `http://dartlang.org`, - /// `file://`) or with "/". - static final url = new _UrlStyle(); - - /// The style of the host platform. - /// - /// When running on the command line, this will be [windows] or [posix] based - /// on the host operating system. On a browser, this will be [url]. - static final platform = _getPlatformStyle(); - - /// Gets the type of the host platform. - static Style _getPlatformStyle() { - // If we're running a Dart file in the browser from a `file:` URI, - // [Uri.base] will point to a file. If we're running on the standalone, - // it will point to a directory. We can use that fact to determine which - // style to use. - if (Uri.base.scheme != 'file') return Style.url; - if (!Uri.base.path.endsWith('/')) return Style.url; - if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; - return Style.posix; - } - - /// The name of this path style. Will be "posix" or "windows". - String get name; - - /// The path separator for this style. On POSIX, this is `/`. On Windows, - /// it's `\`. - String get separator; - - /// The [Pattern] that can be used to match a separator for a path in this - /// style. Windows allows both "/" and "\" as path separators even though "\" - /// is the canonical one. - Pattern get separatorPattern; - - /// The [Pattern] that matches path components that need a separator after - /// them. - /// - /// Windows and POSIX styles just need separators when the previous component - /// doesn't already end in a separator, but the URL always needs to place a - /// separator between the root and the first component, even if the root - /// already ends in a separator character. For example, to join "file://" and - /// "usr", an additional "/" is needed (making "file:///usr"). - Pattern get needsSeparatorPattern; - - /// The [Pattern] that can be used to match the root prefix of an absolute - /// path in this style. - Pattern get rootPattern; - - /// The [Pattern] that can be used to match the root prefix of a root-relative - /// path in this style. - /// - /// This can be null to indicate that this style doesn't support root-relative - /// paths. - final Pattern relativeRootPattern = null; - - /// A [Builder] that uses this style. - Builder get builder => new Builder(style: this); - - /// Gets the root prefix of [path] if path is absolute. If [path] is relative, - /// returns `null`. - String getRoot(String path) { - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = rootPattern.allMatches(path); - if (matches.isNotEmpty) return matches.first[0]; - return getRelativeRoot(path); - } - - /// Gets the root prefix of [path] if it's root-relative. - /// - /// If [path] is relative or absolute and not root-relative, returns `null`. - String getRelativeRoot(String path) { - if (relativeRootPattern == null) return null; - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = relativeRootPattern.allMatches(path); - if (matches.isEmpty) return null; - return matches.first[0]; - } - - /// Returns the path represented by [uri] in this style. - String pathFromUri(Uri uri); - - /// Returns the URI that represents the relative path made of [parts]. - Uri relativePathToUri(String path) => - new Uri(pathSegments: builder.split(path)); - - /// Returns the URI that represents [path], which is assumed to be absolute. - Uri absolutePathToUri(String path); - - String toString() => name; -} - -/// The style for POSIX paths. -class _PosixStyle extends Style { - _PosixStyle(); - - final name = 'posix'; - final separator = '/'; - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp(r'[^/]$'); - final rootPattern = new RegExp(r'^/'); - - String pathFromUri(Uri uri) { - if (uri.scheme == '' || uri.scheme == 'file') { - return Uri.decodeComponent(uri.path); - } - throw new ArgumentError("Uri $uri must have scheme 'file:'."); - } - - Uri absolutePathToUri(String path) { - var parsed = builder._parse(path); - if (parsed.parts.isEmpty) { - // If the path is a bare root (e.g. "/"), [components] will - // currently be empty. We add two empty components so the URL constructor - // produces "file:///", with a trailing slash. - parsed.parts.addAll(["", ""]); - } else if (parsed.hasTrailingSeparator) { - // If the path has a trailing slash, add a single empty component so the - // URI has a trailing slash as well. - parsed.parts.add(""); - } - - return new Uri(scheme: 'file', pathSegments: parsed.parts); - } -} - -/// The style for Windows paths. -class _WindowsStyle extends Style { - _WindowsStyle(); - - final name = 'windows'; - final separator = '\\'; - final separatorPattern = new RegExp(r'[/\\]'); - final needsSeparatorPattern = new RegExp(r'[^/\\]$'); - final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); - - // Matches a back or forward slash that's not followed by another back or - // forward slash. - final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); - - String pathFromUri(Uri uri) { - if (uri.scheme != '' && uri.scheme != 'file') { - throw new ArgumentError("Uri $uri must have scheme 'file:'."); - } - - var path = uri.path; - if (uri.host == '') { - // Drive-letter paths look like "file:///C:/path/to/file". The - // replaceFirst removes the extra initial slash. - if (path.startsWith('/')) path = path.replaceFirst("/", ""); - } else { - // Network paths look like "file://hostname/path/to/file". - path = '\\\\${uri.host}$path'; - } - return Uri.decodeComponent(path.replaceAll("/", "\\")); - } - - Uri absolutePathToUri(String path) { - var parsed = builder._parse(path); - if (parsed.root.startsWith(r'\\')) { - // Network paths become "file://server/share/path/to/file". - - // The root is of the form "\\server\share". We want "server" to be the - // URI host, and "share" to be the first element of the path. - var rootParts = parsed.root.split('\\').where((part) => part != ''); - parsed.parts.insert(0, rootParts.last); - - if (parsed.hasTrailingSeparator) { - // If the path has a trailing slash, add a single empty component so the - // URI has a trailing slash as well. - parsed.parts.add(""); - } - - return new Uri(scheme: 'file', host: rootParts.first, - pathSegments: parsed.parts); - } else { - // Drive-letter paths become "file:///C:/path/to/file". - - // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently - // be empty. We add an empty component so the URL constructor produces - // "file:///C:/", with a trailing slash. We also add an empty component if - // the URL otherwise has a trailing slash. - if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { - parsed.parts.add(""); - } - - // Get rid of the trailing "\" in "C:\" because the URI constructor will - // add a separator on its own. - parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); - - return new Uri(scheme: 'file', pathSegments: parsed.parts); - } - } -} - -/// The style for URL paths. -class _UrlStyle extends Style { - _UrlStyle(); - - final name = 'url'; - final separator = '/'; - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp( - r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); - final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); - final relativeRootPattern = new RegExp(r"^/"); - - String pathFromUri(Uri uri) => uri.toString(); - - Uri relativePathToUri(String path) => Uri.parse(path); - Uri absolutePathToUri(String path) => Uri.parse(path); -} - -// TODO(rnystrom): Make this public? -class _ParsedPath { - /// The [Style] that was used to parse this path. - Style style; - - /// The absolute root portion of the path, or `null` if the path is relative. - /// On POSIX systems, this will be `null` or "/". On Windows, it can be - /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive - /// letters. - String root; - - /// Whether this path is root-relative. - /// - /// See [Builder.isRootRelative]. - bool isRootRelative; - - /// The path-separated parts of the path. All but the last will be - /// directories. - List parts; - - /// The path separators preceding each part. - /// - /// The first one will be an empty string unless the root requires a separator - /// between it and the path. The last one will be an empty string unless the - /// path ends with a trailing separator. - List separators; - - /// The file extension of the last non-empty part, or "" if it doesn't have - /// one. - String get extension => _splitExtension()[1]; - - /// `true` if this is an absolute path. - bool get isAbsolute => root != null; - - _ParsedPath(this.style, this.root, this.isRootRelative, this.parts, - this.separators); - - String get basename { - var copy = this.clone(); - copy.removeTrailingSeparators(); - if (copy.parts.isEmpty) return root == null ? '' : root; - return copy.parts.last; - } - - String get basenameWithoutExtension => _splitExtension()[0]; - - bool get hasTrailingSeparator => - !parts.isEmpty && (parts.last == '' || separators.last != ''); - - void removeTrailingSeparators() { - while (!parts.isEmpty && parts.last == '') { - parts.removeLast(); - separators.removeLast(); - } - if (separators.length > 0) separators[separators.length - 1] = ''; - } - - void normalize() { - // Handle '.', '..', and empty parts. - var leadingDoubles = 0; - var newParts = []; - for (var part in parts) { - if (part == '.' || part == '') { - // Do nothing. Ignore it. - } else if (part == '..') { - // Pop the last part off. - if (newParts.length > 0) { - newParts.removeLast(); - } else { - // Backed out past the beginning, so preserve the "..". - leadingDoubles++; - } - } else { - newParts.add(part); - } - } - - // A relative path can back out from the start directory. - if (!isAbsolute) { - _growListFront(newParts, leadingDoubles, '..'); - } - - // If we collapsed down to nothing, do ".". - if (newParts.length == 0 && !isAbsolute) { - newParts.add('.'); - } - - // Canonicalize separators. - var newSeparators = new List.generate( - newParts.length, (_) => style.separator, growable: true); - newSeparators.insert(0, - isAbsolute && newParts.length > 0 && - root.contains(style.needsSeparatorPattern) ? - style.separator : ''); - - parts = newParts; - separators = newSeparators; - - // Normalize the Windows root if needed. - if (root != null && style == Style.windows) { - root = root.replaceAll('/', '\\'); - } - removeTrailingSeparators(); - } - - String toString() { - var builder = new StringBuffer(); - if (root != null) builder.write(root); - for (var i = 0; i < parts.length; i++) { - builder.write(separators[i]); - builder.write(parts[i]); - } - builder.write(separators.last); - - return builder.toString(); - } - - /// Splits the last non-empty part of the path into a `[basename, extension`] - /// pair. - /// - /// 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 _splitExtension() { - var file = parts.lastWhere((p) => p != '', orElse: () => null); - - if (file == null) return ['', '']; - if (file == '..') return ['..', '']; - - var lastDot = file.lastIndexOf('.'); - - // If there is no dot, or it's the first character, like '.bashrc', it - // doesn't count. - if (lastDot <= 0) return [file, '']; - - return [file.substring(0, lastDot), file.substring(lastDot)]; - } - - _ParsedPath clone() => new _ParsedPath( - style, root, isRootRelative, - new List.from(parts), new List.from(separators)); -} - -/// An exception class that's thrown when a path operation is unable to be -/// computed accurately. -class PathException implements Exception { - String message; - - PathException(this.message); - - String toString() => "PathException: $message"; -} +Uri toUri(String path) => _context.toUri(path); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart new file mode 100644 index 00000000..b897c8ba --- /dev/null +++ b/pkgs/path/lib/src/context.dart @@ -0,0 +1,492 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.context; + +import 'style.dart'; +import 'parsed_path.dart'; +import 'path_exception.dart'; +import '../path.dart' as p; + +/// An instantiable class for manipulating paths. Unlike the top-level +/// functions, this lets you explicitly select what platform the paths will use. +class Context { + /// Creates a new path context for the given style and current directory. + /// + /// If [style] is omitted, it uses the host operating system's path style. If + /// only [current] is omitted, it defaults ".". If *both* [style] and + /// [current] are omitted, [current] defaults to the real current working + /// directory. + /// + /// On the browser, [style] defaults to [Style.url] and [current] defaults to + /// the current URL. + factory Context({Style style, String current}) { + if (current == null) { + if (style == null) { + current = p.current; + } else { + current = "."; + } + } + + if (style == null) style = Style.platform; + + return new Context._(style, current); + } + + Context._(this.style, this.current); + + /// The style of path that this context works with. + final Style style; + + /// The current directory that relative paths will be relative to. + final String current; + + /// Gets the path separator for the context's [style]. On Mac and Linux, + /// this is `/`. On Windows, it's `\`. + String get separator => style.separator; + + /// Creates a new path by appending the given path parts to [current]. + /// Equivalent to [join()] with [current] as the first argument. Example: + /// + /// var context = new Context(current: '/root'); + /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' + /// + /// If [current] isn't absolute, this won't return an absolute path. + String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) { + return join(current, part1, part2, part3, part4, part5, part6, part7); + } + + /// Gets the part of [path] after the last separator on the context's + /// platform. + /// + /// context.basename('path/to/foo.dart'); // -> 'foo.dart' + /// context.basename('path/to'); // -> 'to' + /// + /// Trailing separators are ignored. + /// + /// context.basename('path/to/'); // -> 'to' + String basename(String path) => _parse(path).basename; + + /// Gets the part of [path] after the last separator on the context's + /// platform, and without any trailing file extension. + /// + /// context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + /// + /// Trailing separators are ignored. + /// + /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' + String basenameWithoutExtension(String path) => + _parse(path).basenameWithoutExtension; + + /// Gets the part of [path] before the last separator. + /// + /// context.dirname('path/to/foo.dart'); // -> 'path/to' + /// context.dirname('path/to'); // -> 'path' + /// + /// Trailing separators are ignored. + /// + /// context.dirname('path/to/'); // -> 'path' + String dirname(String path) { + var parsed = _parse(path); + parsed.removeTrailingSeparators(); + if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; + } + parsed.parts.removeLast(); + parsed.separators.removeLast(); + parsed.removeTrailingSeparators(); + return parsed.toString(); + } + + /// Gets the file extension of [path]: the portion of [basename] from the last + /// `.` to the end (including the `.` itself). + /// + /// context.extension('path/to/foo.dart'); // -> '.dart' + /// context.extension('path/to/foo'); // -> '' + /// context.extension('path.to/foo'); // -> '' + /// context.extension('path/to/foo.dart.js'); // -> '.js' + /// + /// If the file name starts with a `.`, then it is not considered an + /// extension: + /// + /// context.extension('~/.bashrc'); // -> '' + /// context.extension('~/.notes.txt'); // -> '.txt' + String extension(String path) => _parse(path).extension; + + // 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 + /// relative. + /// + /// // Unix + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('/path/to/foo'); // -> '/' + /// + /// // Windows + /// context.rootPrefix(r'path\to\foo'); // -> '' + /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// + /// // URL + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('http://dartlang.org/path/to/foo'); + /// // -> 'http://dartlang.org' + String rootPrefix(String path) { + var root = _parse(path).root; + return root == null ? '' : root; + } + + /// Returns `true` if [path] is an absolute path and `false` if it is a + /// relative path. + /// + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + bool isAbsolute(String path) => _parse(path).isAbsolute; + + /// Returns `true` if [path] is a relative path and `false` if it is absolute. + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. + bool isRelative(String path) => !this.isAbsolute(path); + + /// Returns `true` if [path] is a root-relative path and `false` if it's not. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + /// + /// No POSIX and Windows paths are root-relative. + bool isRootRelative(String path) => _parse(path).isRootRelative; + + /// Joins the given path parts into a single path. Example: + /// + /// context.join('path', 'to', 'foo'); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.join('path', '/to', 'foo'); // -> '/to/foo' + /// + String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) { + var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + _validateArgList("join", parts); + return joinAll(parts.where((part) => part != null)); + } + + /// Joins the given path parts into a single path. Example: + /// + /// context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.joinAll(['path', '/to', 'foo']); // -> '/to/foo' + /// + /// For a fixed number of parts, [join] is usually terser. + String joinAll(Iterable parts) { + var buffer = new StringBuffer(); + var needsSeparator = false; + var isAbsoluteAndNotRootRelative = false; + + for (var part in parts.where((part) => part != '')) { + if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + // If the new part is root-relative, it preserves the previous root but + // replaces the path after it. + var parsed = _parse(part); + parsed.root = this.rootPrefix(buffer.toString()); + if (parsed.root.contains(style.needsSeparatorPattern)) { + parsed.separators[0] = style.separator; + } + buffer.clear(); + buffer.write(parsed.toString()); + } else if (this.isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !this.isRootRelative(part); + // An absolute path discards everything before it. + buffer.clear(); + buffer.write(part); + } else { + if (part.length > 0 && part[0].contains(style.separatorPattern)) { + // The part starts with a separator, so we don't need to add one. + } else if (needsSeparator) { + buffer.write(separator); + } + + buffer.write(part); + } + + // Unless this part ends with a separator, we'll need to add one before + // the next part. + needsSeparator = part.contains(style.needsSeparatorPattern); + } + + return buffer.toString(); + } + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Splits [path] into its components using the current platform's + /// [separator]. Example: + /// + /// context.split('path/to/foo'); // -> ['path', 'to', 'foo'] + /// + /// The path will *not* be normalized before splitting. + /// + /// context.split('path/../foo'); // -> ['path', '..', 'foo'] + /// + /// If [path] is absolute, the root directory will be the first element in the + /// array. Example: + /// + /// // Unix + /// context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + /// + /// // Windows + /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + List split(String path) { + var parsed = _parse(path); + // Filter out empty parts that exist due to multiple separators in a row. + parsed.parts = parsed.parts.where((part) => !part.isEmpty) + .toList(); + if (parsed.root != null) parsed.parts.insert(0, parsed.root); + return parsed.parts; + } + + /// Normalizes [path], simplifying it by handling `..`, and `.`, and + /// removing redundant path separators whenever possible. + /// + /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' + String normalize(String path) { + var parsed = _parse(path); + parsed.normalize(); + return parsed.toString(); + } + + /// Attempts to convert [path] to an equivalent relative path relative to + /// [root]. + /// + /// var context = new Context(current: '/root/path'); + /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + /// context.relative('/root/other.dart'); // -> '../other.dart' + /// + /// If the [from] argument is passed, [path] is made relative to that instead. + /// + /// context.relative('/root/path/a/b.dart', + /// from: '/root/path'); // -> 'a/b.dart' + /// context.relative('/root/other.dart', + /// from: '/root/path'); // -> '../other.dart' + /// + /// If [path] and/or [from] are relative paths, they are assumed to be + /// relative to [current]. + /// + /// Since there is no relative path from one drive letter to another on + /// Windows, this will return an absolute path in that case. + /// + /// context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' + /// + /// This will also return an absolute path if an absolute [path] is passed to + /// a context with a relative path for [current]. + /// + /// var context = new Context(r'some/relative/path'); + /// context.relative(r'/absolute/path'); // -> '/absolute/path' + /// + /// If [root] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is + /// "/", no path can be determined. In this case, a [PathException] will be + /// thrown. + String relative(String path, {String from}) { + from = from == null ? current : this.join(current, from); + + // We can't determine the path from a relative path to an absolute path. + if (this.isRelative(from) && this.isAbsolute(path)) { + return this.normalize(path); + } + + // If the given path is relative, resolve it relative to the context's + // current directory. + if (this.isRelative(path) || this.isRootRelative(path)) { + path = this.absolute(path); + } + + // If the path is still relative and `from` is absolute, we're unable to + // find a path from `from` to `path`. + if (this.isRelative(path) && this.isAbsolute(from)) { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + + var fromParsed = _parse(from)..normalize(); + var pathParsed = _parse(path)..normalize(); + + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + return pathParsed.toString(); + } + + // If the root prefixes don't match (for example, different drive letters + // on Windows), then there is no relative path, so just return the absolute + // one. In Windows, drive letters are case-insenstive and we allow + // calculation of relative paths, even if a path has not been normalized. + if (fromParsed.root != pathParsed.root && + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + return pathParsed.toString(); + } + + // Strip off their common prefix. + while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { + fromParsed.parts.removeAt(0); + fromParsed.separators.removeAt(1); + pathParsed.parts.removeAt(0); + pathParsed.separators.removeAt(1); + } + + // If there are any directories left in the from path, we need to walk up + // out of them. If a directory left in the from path is '..', it cannot + // be cancelled by adding a '..'. + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + pathParsed.parts.insertAll(0, + new List.filled(fromParsed.parts.length, '..')); + pathParsed.separators[0] = ''; + pathParsed.separators.insertAll(1, + new List.filled(fromParsed.parts.length, style.separator)); + + // Corner case: the paths completely collapsed. + if (pathParsed.parts.length == 0) return '.'; + + // Corner case: path was '.' and some '..' directories were added in front. + // Don't add a final '/.' in that case. + if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { + pathParsed.parts.removeLast(); + pathParsed.separators..removeLast()..removeLast()..add(''); + } + + // Make it relative. + pathParsed.root = ''; + pathParsed.removeTrailingSeparators(); + + return pathParsed.toString(); + } + + /// Returns `true` if [child] is a path beneath `parent`, and `false` + /// otherwise. + /// + /// path.isWithin('/root/path', '/root/path/a'); // -> true + /// path.isWithin('/root/path', '/root/other'); // -> false + /// path.isWithin('/root/path', '/root/path'); // -> false + bool isWithin(String parent, String child) { + var relative; + try { + relative = this.relative(child, from: parent); + } on PathException catch (_) { + // If no relative path from [parent] to [child] is found, [child] + // definitely isn't a child of [parent]. + return false; + } + + var parts = this.split(relative); + return this.isRelative(relative) && parts.first != '..' && + parts.first != '.'; + } + + /// Removes a trailing extension from the last part of [path]. + /// + /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + String withoutExtension(String path) { + var parsed = _parse(path); + + for (var i = parsed.parts.length - 1; i >= 0; i--) { + if (!parsed.parts[i].isEmpty) { + parsed.parts[i] = parsed.basenameWithoutExtension; + break; + } + } + + return parsed.toString(); + } + + /// Returns the path represented by [uri]. + /// + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL + /// style, this will just convert [uri] to a string. + /// + /// // POSIX + /// context.fromUri(Uri.parse('file:///path/to/foo')) + /// // -> '/path/to/foo' + /// + /// // Windows + /// context.fromUri(Uri.parse('file:///C:/path/to/foo')) + /// // -> r'C:\path\to\foo' + /// + /// // URL + /// context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) + /// // -> 'http://dartlang.org/path/to/foo' + String fromUri(Uri uri) => style.pathFromUri(uri); + + /// Returns the URI that represents [path]. + /// + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL + /// style, this will just convert [path] to a [Uri]. + /// + /// // POSIX + /// context.toUri('/path/to/foo') + /// // -> Uri.parse('file:///path/to/foo') + /// + /// // Windows + /// context.toUri(r'C:\path\to\foo') + /// // -> Uri.parse('file:///C:/path/to/foo') + /// + /// // URL + /// context.toUri('http://dartlang.org/path/to/foo') + /// // -> Uri.parse('http://dartlang.org/path/to/foo') + Uri toUri(String path) { + if (isRelative(path)) { + return style.relativePathToUri(path); + } else { + return style.absolutePathToUri(join(current, path)); + } + } + + ParsedPath _parse(String path) => new ParsedPath.parse(path, style); +} + +/// Validates that there are no non-null arguments following a null one and +/// throws an appropriate [ArgumentError] on failure. +_validateArgList(String method, List args) { + for (var i = 1; i < args.length; i++) { + // Ignore nulls hanging off the end. + if (args[i] == null || args[i - 1] != null) continue; + + var numArgs; + for (numArgs = args.length; numArgs >= 1; numArgs--) { + if (args[numArgs - 1] != null) break; + } + + // Show the arguments. + var message = new StringBuffer(); + message.write("$method("); + message.write(args.take(numArgs) + .map((arg) => arg == null ? "null" : '"$arg"') + .join(", ")); + message.write("): part ${i - 1} was null, but part $i was not."); + throw new ArgumentError(message.toString()); + } +} diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart new file mode 100644 index 00000000..5356b44c --- /dev/null +++ b/pkgs/path/lib/src/parsed_path.dart @@ -0,0 +1,186 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.parsed_path; + +import 'style.dart'; + +// TODO(rnystrom): Make this public? +class ParsedPath { + /// The [Style] that was used to parse this path. + Style style; + + /// The absolute root portion of the path, or `null` if the path is relative. + /// On POSIX systems, this will be `null` or "/". On Windows, it can be + /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive + /// letters. + String root; + + /// Whether this path is root-relative. + /// + /// See [Context.isRootRelative]. + bool isRootRelative; + + /// The path-separated parts of the path. All but the last will be + /// directories. + List parts; + + /// The path separators preceding each part. + /// + /// The first one will be an empty string unless the root requires a separator + /// between it and the path. The last one will be an empty string unless the + /// path ends with a trailing separator. + List separators; + + /// The file extension of the last non-empty part, or "" if it doesn't have + /// one. + String get extension => _splitExtension()[1]; + + /// `true` if this is an absolute path. + bool get isAbsolute => root != null; + + factory ParsedPath.parse(String path, Style style) { + var before = path; + + // Remove the root prefix, if any. + var root = style.getRoot(path); + var isRootRelative = style.getRelativeRoot(path) != null; + if (root != null) path = path.substring(root.length); + + // Split the parts on path separators. + var parts = []; + var separators = []; + + var firstSeparator = style.separatorPattern.matchAsPrefix(path); + if (firstSeparator != null) { + separators.add(firstSeparator[0]); + path = path.substring(firstSeparator[0].length); + } else { + separators.add(''); + } + + var start = 0; + for (var match in style.separatorPattern.allMatches(path)) { + parts.add(path.substring(start, match.start)); + separators.add(match[0]); + start = match.end; + } + + // Add the final part, if any. + if (start < path.length) { + parts.add(path.substring(start)); + separators.add(''); + } + + return new ParsedPath._(style, root, isRootRelative, parts, separators); + } + + ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, + this.separators); + + String get basename { + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy.parts.last; + } + + String get basenameWithoutExtension => _splitExtension()[0]; + + bool get hasTrailingSeparator => + !parts.isEmpty && (parts.last == '' || separators.last != ''); + + void removeTrailingSeparators() { + while (!parts.isEmpty && parts.last == '') { + parts.removeLast(); + separators.removeLast(); + } + if (separators.length > 0) separators[separators.length - 1] = ''; + } + + void normalize() { + // Handle '.', '..', and empty parts. + var leadingDoubles = 0; + var newParts = []; + for (var part in parts) { + if (part == '.' || part == '') { + // Do nothing. Ignore it. + } else if (part == '..') { + // Pop the last part off. + if (newParts.length > 0) { + newParts.removeLast(); + } else { + // Backed out past the beginning, so preserve the "..". + leadingDoubles++; + } + } else { + newParts.add(part); + } + } + + // A relative path can back out from the start directory. + if (!isAbsolute) { + newParts.insertAll(0, new List.filled(leadingDoubles, '..')); + } + + // If we collapsed down to nothing, do ".". + if (newParts.length == 0 && !isAbsolute) { + newParts.add('.'); + } + + // Canonicalize separators. + var newSeparators = new List.generate( + newParts.length, (_) => style.separator, growable: true); + newSeparators.insert(0, + isAbsolute && newParts.length > 0 && + root.contains(style.needsSeparatorPattern) ? + style.separator : ''); + + parts = newParts; + separators = newSeparators; + + // Normalize the Windows root if needed. + if (root != null && style == Style.windows) { + root = root.replaceAll('/', '\\'); + } + removeTrailingSeparators(); + } + + String toString() { + var builder = new StringBuffer(); + if (root != null) builder.write(root); + for (var i = 0; i < parts.length; i++) { + builder.write(separators[i]); + builder.write(parts[i]); + } + builder.write(separators.last); + + return builder.toString(); + } + + /// Splits the last non-empty part of the path into a `[basename, extension`] + /// pair. + /// + /// 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 _splitExtension() { + var file = parts.lastWhere((p) => p != '', orElse: () => null); + + if (file == null) return ['', '']; + if (file == '..') return ['..', '']; + + var lastDot = file.lastIndexOf('.'); + + // If there is no dot, or it's the first character, like '.bashrc', it + // doesn't count. + if (lastDot <= 0) return [file, '']; + + return [file.substring(0, lastDot), file.substring(lastDot)]; + } + + ParsedPath clone() => new ParsedPath._( + style, root, isRootRelative, + new List.from(parts), new List.from(separators)); +} + diff --git a/pkgs/path/lib/src/path_exception.dart b/pkgs/path/lib/src/path_exception.dart new file mode 100644 index 00000000..49bd268e --- /dev/null +++ b/pkgs/path/lib/src/path_exception.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.path_exception; + +/// An exception class that's thrown when a path operation is unable to be +/// computed accurately. +class PathException implements Exception { + String message; + + PathException(this.message); + + String toString() => "PathException: $message"; +} diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart new file mode 100644 index 00000000..9da4b43a --- /dev/null +++ b/pkgs/path/lib/src/style.dart @@ -0,0 +1,118 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.style; + +import 'context.dart'; +import 'style/posix.dart'; +import 'style/url.dart'; +import 'style/windows.dart'; + +/// An enum type describing a "flavor" of path. +abstract class Style { + /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths + /// start with "/". Used by UNIX, Linux, Mac OS X, and others. + static final posix = new PosixStyle(); + + /// Windows paths use "\" (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, "C:") or two backslashes + /// ("\\") for UNC paths. + // TODO(rnystrom): The UNC root prefix should include the drive name too, not + // just the "\\". + static final windows = new WindowsStyle(); + + /// URLs aren't filesystem paths, but they're supported to make it easier to + /// manipulate URL paths in the browser. + /// + /// URLs use "/" (forward slash) as separators. Absolute paths either start + /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// `file://`) or with "/". + static final url = new UrlStyle(); + + /// The style of the host platform. + /// + /// When running on the command line, this will be [windows] or [posix] based + /// on the host operating system. On a browser, this will be [url]. + static final platform = _getPlatformStyle(); + + /// Gets the type of the host platform. + static Style _getPlatformStyle() { + // If we're running a Dart file in the browser from a `file:` URI, + // [Uri.base] will point to a file. If we're running on the standalone, + // it will point to a directory. We can use that fact to determine which + // style to use. + if (Uri.base.scheme != 'file') return Style.url; + if (!Uri.base.path.endsWith('/')) return Style.url; + if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; + return Style.posix; + } + + /// The name of this path style. Will be "posix" or "windows". + String get name; + + /// The path separator for this style. On POSIX, this is `/`. On Windows, + /// it's `\`. + String get separator; + + /// The [Pattern] that can be used to match a separator for a path in this + /// style. Windows allows both "/" and "\" as path separators even though "\" + /// is the canonical one. + Pattern get separatorPattern; + + /// The [Pattern] that matches path components that need a separator after + /// them. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + Pattern get needsSeparatorPattern; + + /// The [Pattern] that can be used to match the root prefix of an absolute + /// path in this style. + Pattern get rootPattern; + + /// The [Pattern] that can be used to match the root prefix of a root-relative + /// path in this style. + /// + /// This can be null to indicate that this style doesn't support root-relative + /// paths. + final Pattern relativeRootPattern = null; + + /// A [Context] that uses this style. + Context get context => new Context(style: this); + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path) { + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = rootPattern.allMatches(path); + if (matches.isNotEmpty) return matches.first[0]; + return getRelativeRoot(path); + } + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path) { + if (relativeRootPattern == null) return null; + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = relativeRootPattern.allMatches(path); + if (matches.isEmpty) return null; + return matches.first[0]; + } + + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: context.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); + + String toString() => name; +} diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart new file mode 100644 index 00000000..72e70441 --- /dev/null +++ b/pkgs/path/lib/src/style/posix.dart @@ -0,0 +1,42 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.style.posix; + +import '../parsed_path.dart'; +import '../style.dart'; + +/// The style for POSIX paths. +class PosixStyle extends Style { + PosixStyle(); + + final name = 'posix'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp(r'[^/]$'); + final rootPattern = new RegExp(r'^/'); + + String pathFromUri(Uri uri) { + if (uri.scheme == '' || uri.scheme == 'file') { + return Uri.decodeComponent(uri.path); + } + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "/"), [components] will + // currently be empty. We add two empty components so the URL constructor + // produces "file:///", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } +} diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart new file mode 100644 index 00000000..4a7003de --- /dev/null +++ b/pkgs/path/lib/src/style/url.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.style.url; + +import '../style.dart'; + +/// The style for URL paths. +class UrlStyle extends Style { + UrlStyle(); + + final name = 'url'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp( + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = new RegExp(r"^/"); + + String pathFromUri(Uri uri) => uri.toString(); + + Uri relativePathToUri(String path) => Uri.parse(path); + Uri absolutePathToUri(String path) => Uri.parse(path); +} diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart new file mode 100644 index 00000000..1750578e --- /dev/null +++ b/pkgs/path/lib/src/style/windows.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.style.windows; + +import '../parsed_path.dart'; +import '../style.dart'; + +/// The style for Windows paths. +class WindowsStyle extends Style { + WindowsStyle(); + + final name = 'windows'; + final separator = '\\'; + final separatorPattern = new RegExp(r'[/\\]'); + final needsSeparatorPattern = new RegExp(r'[^/\\]$'); + final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + + String pathFromUri(Uri uri) { + if (uri.scheme != '' && uri.scheme != 'file') { + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + var path = uri.path; + if (uri.host == '') { + // Drive-letter paths look like "file:///C:/path/to/file". The + // replaceFirst removes the extra initial slash. + if (path.startsWith('/')) path = path.replaceFirst("/", ""); + } else { + // Network paths look like "file://hostname/path/to/file". + path = '\\\\${uri.host}$path'; + } + return Uri.decodeComponent(path.replaceAll("/", "\\")); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.root.startsWith(r'\\')) { + // Network paths become "file://server/share/path/to/file". + + // The root is of the form "\\server\share". We want "server" to be the + // URI host, and "share" to be the first element of the path. + var rootParts = parsed.root.split('\\').where((part) => part != ''); + parsed.parts.insert(0, rootParts.last); + + if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', host: rootParts.first, + pathSegments: parsed.parts); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently + // be empty. We add an empty component so the URL constructor produces + // "file:///C:/", with a trailing slash. We also add an empty component if + // the URL otherwise has a trailing slash. + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + parsed.parts.add(""); + } + + // Get rid of the trailing "\" in "C:\" because the URI constructor will + // add a separator on its own. + parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } + } +} \ No newline at end of file diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index a6e8a680..d8584a46 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -11,21 +11,21 @@ import 'package:path/path.dart' as path; main() { useHtmlConfiguration(); - group('new Builder()', () { + group('new Context()', () { test('uses the window location if root and style are omitted', () { - var builder = new path.Builder(); - expect(builder.root, + var context = new path.Context(); + expect(context.current, Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { - var builder = new path.Builder(style: path.Style.platform); - expect(builder.root, "."); + var context = new path.Context(style: path.Style.platform); + expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var builder = new path.Builder(); - expect(builder.style, path.Style.platform); + var context = new path.Context(); + expect(context.style, path.Style.platform); }); }); diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 2d4ac0c5..3e52bb95 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -8,20 +8,20 @@ import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; main() { - group('new Builder()', () { + group('new Context()', () { test('uses the current directory if root and style are omitted', () { - var builder = new path.Builder(); - expect(builder.root, io.Directory.current.path); + var context = new path.Context(); + expect(context.current, io.Directory.current.path); }); test('uses "." if root is omitted', () { - var builder = new path.Builder(style: path.Style.platform); - expect(builder.root, "."); + var context = new path.Context(style: path.Style.platform); + expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var builder = new path.Builder(); - expect(builder.style, path.Style.platform); + var context = new path.Context(); + expect(context.style, path.Style.platform); }); }); diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index d6c2f349..19397cf8 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -23,30 +23,30 @@ main() { }); }); - group('new Builder()', () { - test('uses the given root directory', () { - var builder = new path.Builder(root: '/a/b/c'); - expect(builder.root, '/a/b/c'); + group('new Context()', () { + test('uses the given current directory', () { + var context = new path.Context(current: '/a/b/c'); + expect(context.current, '/a/b/c'); }); test('uses the given style', () { - var builder = new path.Builder(style: path.Style.windows); - expect(builder.style, path.Style.windows); + var context = new path.Context(style: path.Style.windows); + expect(context.style, path.Style.windows); }); }); - test('posix is a default Builder for the POSIX style', () { + test('posix is a default Context for the POSIX style', () { expect(path.posix.style, path.Style.posix); - expect(path.posix.root, "."); + expect(path.posix.current, "."); }); - test('windows is a default Builder for the Windows style', () { + test('windows is a default Context for the Windows style', () { expect(path.windows.style, path.Style.windows); - expect(path.windows.root, "."); + expect(path.windows.current, "."); }); - test('url is a default Builder for the URL style', () { + test('url is a default Context for the URL style', () { expect(path.url.style, path.Style.url); - expect(path.url.root, "."); + expect(path.url.current, "."); }); } diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index b693fcf4..83a473e7 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -10,361 +10,355 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var builder = new path.Builder(style: path.Style.posix, root: '/root/path'); - - if (new path.Builder().style == path.Style.posix) { - group('absolute', () { - expect(path.absolute('a/b.txt'), path.join(path.current, 'a/b.txt')); - expect(path.absolute('/a/b.txt'), '/a/b.txt'); - }); - } + var context = new path.Context( + style: path.Style.posix, current: '/root/path'); test('separator', () { - expect(builder.separator, '/'); + expect(context.separator, '/'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('.'), ''); - expect(builder.extension('..'), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('a.b/c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension('~/.bashrc'), ''); - expect(builder.extension(r'a.b\c'), r'.b\c'); - expect(builder.extension('foo.dart/'), '.dart'); - expect(builder.extension('foo.dart//'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('.'), ''); + expect(context.extension('..'), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('a.b/c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension('~/.bashrc'), ''); + expect(context.extension(r'a.b\c'), r'.b\c'); + expect(context.extension('foo.dart/'), '.dart'); + expect(context.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix('a/b'), ''); - expect(builder.rootPrefix('/a/c'), '/'); - expect(builder.rootPrefix('/'), '/'); + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix('a/b'), ''); + expect(context.rootPrefix('/a/c'), '/'); + expect(context.rootPrefix('/'), '/'); }); test('dirname', () { - expect(builder.dirname(''), '.'); - expect(builder.dirname('.'), '.'); - expect(builder.dirname('..'), '.'); - expect(builder.dirname('../..'), '..'); - expect(builder.dirname('a'), '.'); - expect(builder.dirname('a/b'), 'a'); - expect(builder.dirname('a/b/c'), 'a/b'); - expect(builder.dirname('a/b.c'), 'a'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname('a/.'), 'a'); - expect(builder.dirname('a/..'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname('/a'), '/'); - expect(builder.dirname('///a'), '/'); - expect(builder.dirname('/'), '/'); - expect(builder.dirname('///'), '/'); - expect(builder.dirname('a/b/'), 'a'); - expect(builder.dirname(r'a/b\c'), 'a'); - expect(builder.dirname('a//'), '.'); - expect(builder.dirname('a/b//'), 'a'); - expect(builder.dirname('a//b'), 'a'); + expect(context.dirname(''), '.'); + expect(context.dirname('.'), '.'); + expect(context.dirname('..'), '.'); + expect(context.dirname('../..'), '..'); + expect(context.dirname('a'), '.'); + expect(context.dirname('a/b'), 'a'); + expect(context.dirname('a/b/c'), 'a/b'); + expect(context.dirname('a/b.c'), 'a'); + expect(context.dirname('a/'), '.'); + expect(context.dirname('a/.'), 'a'); + expect(context.dirname('a/..'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname('/a'), '/'); + expect(context.dirname('///a'), '/'); + expect(context.dirname('/'), '/'); + expect(context.dirname('///'), '/'); + expect(context.dirname('a/b/'), 'a'); + expect(context.dirname(r'a/b\c'), 'a'); + expect(context.dirname('a//'), '.'); + expect(context.dirname('a/b//'), 'a'); + expect(context.dirname('a//b'), 'a'); }); test('basename', () { - expect(builder.basename(''), ''); - expect(builder.basename('.'), '.'); - expect(builder.basename('..'), '..'); - expect(builder.basename('.foo'), '.foo'); - expect(builder.basename('a'), 'a'); - expect(builder.basename('a/b'), 'b'); - expect(builder.basename('a/b/c'), 'c'); - expect(builder.basename('a/b.c'), 'b.c'); - expect(builder.basename('a/'), 'a'); - expect(builder.basename('a/.'), '.'); - expect(builder.basename('a/..'), '..'); - expect(builder.basename(r'a\b/c'), 'c'); - expect(builder.basename('/a'), 'a'); - expect(builder.basename('/'), '/'); - expect(builder.basename('a/b/'), 'b'); - expect(builder.basename(r'a/b\c'), r'b\c'); - expect(builder.basename('a//'), 'a'); - expect(builder.basename('a/b//'), 'b'); - expect(builder.basename('a//b'), 'b'); + expect(context.basename(''), ''); + expect(context.basename('.'), '.'); + expect(context.basename('..'), '..'); + expect(context.basename('.foo'), '.foo'); + expect(context.basename('a'), 'a'); + expect(context.basename('a/b'), 'b'); + expect(context.basename('a/b/c'), 'c'); + expect(context.basename('a/b.c'), 'b.c'); + expect(context.basename('a/'), 'a'); + expect(context.basename('a/.'), '.'); + expect(context.basename('a/..'), '..'); + expect(context.basename(r'a\b/c'), 'c'); + expect(context.basename('/a'), 'a'); + expect(context.basename('/'), '/'); + expect(context.basename('a/b/'), 'b'); + expect(context.basename(r'a/b\c'), r'b\c'); + expect(context.basename('a//'), 'a'); + expect(context.basename('a/b//'), 'b'); + expect(context.basename('a//b'), 'b'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension('a/b'), 'b'); - expect(builder.basenameWithoutExtension('a/b/c'), 'c'); - expect(builder.basenameWithoutExtension('a/b.c'), 'b'); - expect(builder.basenameWithoutExtension('a/'), 'a'); - expect(builder.basenameWithoutExtension('a/.'), '.'); - expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); - expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension('a//'), 'a'); - expect(builder.basenameWithoutExtension('a/b//'), 'b'); - expect(builder.basenameWithoutExtension('a//b'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); - expect(builder.basenameWithoutExtension('a/b c.d e'), 'b c'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension('a/b'), 'b'); + expect(context.basenameWithoutExtension('a/b/c'), 'c'); + expect(context.basenameWithoutExtension('a/b.c'), 'b'); + expect(context.basenameWithoutExtension('a/'), 'a'); + expect(context.basenameWithoutExtension('a/.'), '.'); + expect(context.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(context.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension('a//'), 'a'); + expect(context.basenameWithoutExtension('a/b//'), 'b'); + expect(context.basenameWithoutExtension('a//b'), 'b'); + expect(context.basenameWithoutExtension('a/b.c/'), 'b'); + expect(context.basenameWithoutExtension('a/b.c//'), 'b'); + expect(context.basenameWithoutExtension('a/b c.d e'), 'b c'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute('a/b'), false); - expect(builder.isAbsolute('/a'), true); - expect(builder.isAbsolute('/a/b'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('..'), false); - expect(builder.isAbsolute('.foo'), false); - expect(builder.isAbsolute('../a'), false); - expect(builder.isAbsolute('C:/a'), false); - expect(builder.isAbsolute(r'C:\a'), false); - expect(builder.isAbsolute(r'\\a'), false); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute('a/b'), false); + expect(context.isAbsolute('/a'), true); + expect(context.isAbsolute('/a/b'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('..'), false); + expect(context.isAbsolute('.foo'), false); + expect(context.isAbsolute('../a'), false); + expect(context.isAbsolute('C:/a'), false); + expect(context.isAbsolute(r'C:\a'), false); + expect(context.isAbsolute(r'\\a'), false); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative('a/b'), true); - expect(builder.isRelative('/a'), false); - expect(builder.isRelative('/a/b'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('..'), true); - expect(builder.isRelative('.foo'), true); - expect(builder.isRelative('../a'), true); - expect(builder.isRelative('C:/a'), true); - expect(builder.isRelative(r'C:\a'), true); - expect(builder.isRelative(r'\\a'), true); + expect(context.isRelative(''), true); + expect(context.isRelative('a'), true); + expect(context.isRelative('a/b'), true); + expect(context.isRelative('/a'), false); + expect(context.isRelative('/a/b'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('..'), true); + expect(context.isRelative('.foo'), true); + expect(context.isRelative('../a'), true); + expect(context.isRelative('C:/a'), true); + expect(context.isRelative(r'C:\a'), true); + expect(context.isRelative(r'\\a'), true); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), 'a/b'); - expect(builder.join('a', 'b', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), 'a/b'); + expect(context.join('a', 'b', 'c'), 'a/b/c'); + expect(context.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); }); test('does not add separator if a part ends in one', () { - expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); - expect(builder.join('a\\', 'b'), r'a\/b'); + expect(context.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(context.join('a\\', 'b'), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', '/', 'b', 'c'), '/b/c'); - expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); - expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + expect(context.join('a', '/', 'b', 'c'), '/b/c'); + expect(context.join('a', '/b', '/c', 'd'), '/c/d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(context.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals('a/b/c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', ''), 'a/b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(context.join('a', 'b', ''), 'a/b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), 'a/b/./c/..//d/.././..//e/f//'); - expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); - expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), 'a/b/c/d/e/f/g/h/i'); }); test('does not add separator if a part ends in one', () { - expect(builder.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d'); - expect(builder.joinAll(['a\\', 'b']), r'a\/b'); + expect(context.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d'); + expect(context.joinAll(['a\\', 'b']), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); - expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/../bar/./baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/../bar/./baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split('//'), equals(['/'])); + expect(context.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split('//'), equals(['/'])); }); test('includes the root for absolute paths', () { - expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); - expect(builder.split('/'), equals(['/'])); + expect(context.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(context.split('/'), equals(['/'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('/'), '/'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize('C:/'), 'C:'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\'), r'\\'); - expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('/'), '/'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize('C:/'), 'C:'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\'), r'\\'); + expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a/b/c'), r'a/b/c'); - expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + expect(context.normalize(r'a/b/c'), r'a/b/c'); + expect(context.normalize(r'a//b///c////d'), r'a/b/c/d'); }); test('does not collapse separators for other platform', () { - expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + expect(context.normalize(r'a\\b\\\c'), r'a\\b\\\c'); }); test('eliminates "." parts', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('/.'), '/'); - expect(builder.normalize('/./'), '/'); - expect(builder.normalize('./.'), '.'); - expect(builder.normalize('a/./b'), 'a/b'); - expect(builder.normalize('a/.b/c'), 'a/.b/c'); - expect(builder.normalize('a/././b/./c'), 'a/b/c'); - expect(builder.normalize('././a'), 'a'); - expect(builder.normalize('a/./.'), 'a'); + expect(context.normalize('./'), '.'); + expect(context.normalize('/.'), '/'); + expect(context.normalize('/./'), '/'); + expect(context.normalize('./.'), '.'); + expect(context.normalize('a/./b'), 'a/b'); + expect(context.normalize('a/.b/c'), 'a/.b/c'); + expect(context.normalize('a/././b/./c'), 'a/b/c'); + expect(context.normalize('././a'), 'a'); + expect(context.normalize('a/./.'), 'a'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('../../..'), '../../..'); - expect(builder.normalize('../../../'), '../../..'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('/../../..'), '/'); - expect(builder.normalize('/../../../a'), '/a'); - expect(builder.normalize('c:/..'), '.'); - expect(builder.normalize('A:/../../..'), '../..'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize(r'z/a/b/../../..\../c'), r'z/..\../c'); - expect(builder.normalize(r'a/b\c/../d'), 'a/d'); + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('../../..'), '../../..'); + expect(context.normalize('../../../'), '../../..'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('/../../..'), '/'); + expect(context.normalize('/../../../a'), '/a'); + expect(context.normalize('c:/..'), '.'); + expect(context.normalize('A:/../../..'), '../..'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize(r'z/a/b/../../..\../c'), r'z/..\../c'); + expect(context.normalize(r'a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('http://dartlang.org/..'), 'http:'); - expect(builder.normalize('http://dartlang.org/../../a'), 'a'); - expect(builder.normalize('file:///..'), '.'); - expect(builder.normalize('file:///../../a'), '../a'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('../a'), '../a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('c:/../a'), 'a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('../a/b/..'), '../a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('http://dartlang.org/..'), 'http:'); + expect(context.normalize('http://dartlang.org/../../a'), 'a'); + expect(context.normalize('file:///..'), '.'); + expect(context.normalize('file:///../../a'), '../a'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('../a'), '../a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('c:/../a'), 'a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('../a/b/..'), '../a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('.//'), '.'); - expect(builder.normalize('a/'), 'a'); - expect(builder.normalize('a/b/'), 'a/b'); - expect(builder.normalize(r'a/b\'), r'a/b\'); - expect(builder.normalize('a/b///'), 'a/b'); + expect(context.normalize('./'), '.'); + expect(context.normalize('.//'), '.'); + expect(context.normalize('a/'), 'a'); + expect(context.normalize('a/b/'), 'a/b'); + expect(context.normalize(r'a/b\'), r'a/b\'); + expect(context.normalize('a/b///'), 'a/b'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative('/'), '../..'); - expect(builder.relative('/root'), '..'); - expect(builder.relative('/root/path'), '.'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/'), '../..'); + expect(context.relative('/root'), '..'); + expect(context.relative('/root/path'), '.'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path outside of root', () { - expect(builder.relative('/a/b'), '../../a/b'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/a/b'), '../../a/b'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative('a/b.txt'), 'a/b.txt'); - expect(builder.relative('../a/b.txt'), '../a/b.txt'); - expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative('a/b.txt'), 'a/b.txt'); + expect(context.relative('../a/b.txt'), '../a/b.txt'); + expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative('/', from: '/'), '.'); - expect(builder.relative('/root/path', from: '/'), 'root/path'); + expect(context.relative('/', from: '/'), '.'); + expect(context.relative('/root/path', from: '/'), 'root/path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('/'), equals('/')); @@ -385,20 +379,21 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.posix, root: '/dir.ext'); + var r = new path.Context(style: path.Style.posix, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + expect(context.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + expect(context.relative('..', from: 'foo/bar'), equals('../../..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'relative/root'); + var r = new path.Context( + style: path.Style.posix, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); expect(r.relative('/foo/bar/baz', from: 'foo/bar'), @@ -407,7 +402,7 @@ main() { }); test('from a . root', () { - var r = new path.Builder(style: path.Style.posix, root: '.'); + var r = new path.Context(style: path.Style.posix, current: '.'); expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); }); @@ -415,94 +410,95 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); - expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); - expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); - expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect(builder.isWithin('/', '/foo/bar'), isTrue); - expect(builder.isWithin('baz', '/root/path/baz/bang'), isTrue); - expect(builder.isWithin('baz', '/root/path/bang/baz'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(context.isWithin('/', '/foo/bar'), isTrue); + expect(context.isWithin('baz', '/root/path/baz/bang'), isTrue); + expect(context.isWithin('baz', '/root/path/bang/baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); - expect(builder.isWithin('.', 'a/b/c'), isTrue); - expect(builder.isWithin('.', '../a/b/c'), isFalse); - expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(builder.isWithin('/', '/baz/bang'), isTrue); - expect(builder.isWithin('.', '/baz/bang'), isFalse); + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + expect(context.isWithin('.', 'a/b/c'), isTrue); + expect(context.isWithin('.', '../a/b/c'), isFalse); + expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(context.isWithin('/', '/baz/bang'), isTrue); + expect(context.isWithin('.', '/baz/bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), '/root/path/a'); - expect(builder.resolve('a', 'b'), '/root/path/a/b'); - expect(builder.resolve('a', 'b', 'c'), '/root/path/a/b/c'); - expect(builder.resolve('a', 'b', 'c', 'd'), '/root/path/a/b/c/d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a'), '/root/path/a'); + expect(context.absolute('a', 'b'), '/root/path/a/b'); + expect(context.absolute('a', 'b', 'c'), '/root/path/a/b/c'); + expect(context.absolute('a', 'b', 'c', 'd'), '/root/path/a/b/c/d'); + expect(context.absolute('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), '/root/path/a/b/c/d/e/f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), '/root/path/a/b/c/d/e/f/g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d'); - expect(builder.resolve(r'a\', 'b'), r'/root/path/a\/b'); + expect(context.absolute('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d'); + expect(context.absolute(r'a\', 'b'), r'/root/path/a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d'); - expect(builder.resolve('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); + expect(context.absolute('a', '/b', '/c', 'd'), '/c/d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), + r'/root/path/a/c:\b/c/d'); + expect(context.absolute('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension('a/b.c'), 'a/b'); - expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); - expect(builder.withoutExtension('a/'), 'a/'); - expect(builder.withoutExtension('a/b/'), 'a/b/'); - expect(builder.withoutExtension('a/.'), 'a/.'); - expect(builder.withoutExtension('a/.b'), 'a/.b'); - expect(builder.withoutExtension('a.b/c'), 'a.b/c'); - expect(builder.withoutExtension(r'a.b\c'), r'a'); - expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); - expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); - expect(builder.withoutExtension('a/b.c/'), 'a/b/'); - expect(builder.withoutExtension('a/b.c//'), 'a/b//'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension('a/b.c'), 'a/b'); + expect(context.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(context.withoutExtension('a/'), 'a/'); + expect(context.withoutExtension('a/b/'), 'a/b/'); + expect(context.withoutExtension('a/.'), 'a/.'); + expect(context.withoutExtension('a/.b'), 'a/.b'); + expect(context.withoutExtension('a.b/c'), 'a.b/c'); + expect(context.withoutExtension(r'a.b\c'), r'a'); + expect(context.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(context.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(context.withoutExtension('a/b.c/'), 'a/b/'); + expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); - expect(builder.fromUri(Uri.parse('file:///')), '/'); - expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(builder.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); + expect(context.fromUri(Uri.parse('file:///')), '/'); + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo%23bar')), '/path/to/foo#bar'); - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); - expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); test('toUri', () { - expect(builder.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); - expect(builder.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); - expect(builder.toUri('/'), Uri.parse('file:///')); - expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); - expect(builder.toUri('/path/to/foo#bar'), + expect(context.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); + expect(context.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); + expect(context.toUri('/'), Uri.parse('file:///')); + expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(context.toUri('/path/to/foo#bar'), Uri.parse('file:///path/to/foo%23bar')); - expect(builder.toUri(r'/_{_}_`_^_ _"_%_'), + expect(context.toUri(r'/_{_}_`_^_ _"_%_'), Uri.parse('file:///_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index e9762f47..58163d25 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // -// Test "relative" on all styles of path.Builder, on all platforms. +// Test "relative" on all styles of path.Context, on all platforms. import "package:unittest/unittest.dart"; import "package:path/path.dart" as path; @@ -11,27 +11,27 @@ import "utils.dart"; void main() { test("test relative", () { - relativeTest(new path.Builder(style: path.Style.posix, root: '.'), '/'); - relativeTest(new path.Builder(style: path.Style.posix, root: '/'), '/'); - relativeTest(new path.Builder(style: path.Style.windows, root: r'd:\'), + relativeTest(new path.Context(style: path.Style.posix, current: '.'), '/'); + relativeTest(new path.Context(style: path.Style.posix, current: '/'), '/'); + relativeTest(new path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); - relativeTest(new path.Builder(style: path.Style.windows, root: '.'), + relativeTest(new path.Context(style: path.Style.windows, current: '.'), r'c:\'); - relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), 'http://myserver/'); - relativeTest(new path.Builder(style: path.Style.url, root: '.'), + relativeTest(new path.Context(style: path.Style.url, current: '.'), 'http://myserver/'); - relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), '/'); - relativeTest(new path.Builder(style: path.Style.url, root: '.'), '/'); + relativeTest(new path.Context(style: path.Style.url, current: '.'), '/'); }); } -void relativeTest(path.Builder builder, String prefix) { - var isRelative = (builder.root == '.'); +void relativeTest(path.Context context, String prefix) { + var isRelative = (context.current == '.'); // Cases where the arguments are absolute paths. expectRelative(result, pathArg, fromArg) { - expect(builder.normalize(result), builder.relative(pathArg, from: fromArg)); + expect(context.normalize(result), context.relative(pathArg, from: fromArg)); } expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); @@ -77,10 +77,10 @@ void relativeTest(path.Builder builder, String prefix) { // Should always throw - no relative path can be constructed. if (isRelative) { - expect(() => builder.relative('.', from: '..'), throwsPathException); - expect(() => builder.relative('a/b', from: '../../d'), + expect(() => context.relative('.', from: '..'), throwsPathException); + expect(() => context.relative('a/b', from: '../../d'), throwsPathException); - expect(() => builder.relative('a/b', from: '${prefix}a/b'), + expect(() => context.relative('a/b', from: '${prefix}a/b'), throwsPathException); // An absolute path relative from a relative path returns the absolute path. expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 9fb6f079..e149b4f0 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -6,489 +6,489 @@ import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; main() { - var builder = new path.Builder(style: path.Style.url, - root: 'http://dartlang.org/root/path'); + var context = new path.Context(style: path.Style.url, + current: 'http://dartlang.org/root/path'); test('separator', () { - expect(builder.separator, '/'); + expect(context.separator, '/'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('a.b/c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension(r'a.b\c'), r'.b\c'); - expect(builder.extension('foo.dart/'), '.dart'); - expect(builder.extension('foo.dart//'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('a.b/c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension(r'a.b\c'), r'.b\c'); + expect(context.extension('foo.dart/'), '.dart'); + expect(context.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix('a/b'), ''); - expect(builder.rootPrefix('http://dartlang.org/a/c'), + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix('a/b'), ''); + expect(context.rootPrefix('http://dartlang.org/a/c'), 'http://dartlang.org'); - expect(builder.rootPrefix('file:///a/c'), 'file://'); - expect(builder.rootPrefix('/a/c'), '/'); - expect(builder.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.rootPrefix('file:///'), 'file://'); - expect(builder.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.rootPrefix('file://'), 'file://'); - expect(builder.rootPrefix('/'), '/'); + expect(context.rootPrefix('file:///a/c'), 'file://'); + expect(context.rootPrefix('/a/c'), '/'); + expect(context.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.rootPrefix('file:///'), 'file://'); + expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); + expect(context.rootPrefix('file://'), 'file://'); + expect(context.rootPrefix('/'), '/'); }); test('dirname', () { - expect(builder.dirname(''), '.'); - expect(builder.dirname('a'), '.'); - expect(builder.dirname('a/b'), 'a'); - expect(builder.dirname('a/b/c'), 'a/b'); - expect(builder.dirname('a/b.c'), 'a'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname('a/.'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname('http://dartlang.org/a'), 'http://dartlang.org'); - expect(builder.dirname('file:///a'), 'file://'); - expect(builder.dirname('/a'), '/'); - expect(builder.dirname('http://dartlang.org///a'), 'http://dartlang.org'); - expect(builder.dirname('file://///a'), 'file://'); - expect(builder.dirname('///a'), '/'); - expect(builder.dirname('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.dirname('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.dirname('file:///'), 'file://'); - expect(builder.dirname('file://'), 'file://'); - expect(builder.dirname('/'), '/'); - expect(builder.dirname('http://dartlang.org///'), 'http://dartlang.org'); - expect(builder.dirname('file://///'), 'file://'); - expect(builder.dirname('///'), '/'); - expect(builder.dirname('a/b/'), 'a'); - expect(builder.dirname(r'a/b\c'), 'a'); - expect(builder.dirname('a//'), '.'); - expect(builder.dirname('a/b//'), 'a'); - expect(builder.dirname('a//b'), 'a'); + expect(context.dirname(''), '.'); + expect(context.dirname('a'), '.'); + expect(context.dirname('a/b'), 'a'); + expect(context.dirname('a/b/c'), 'a/b'); + expect(context.dirname('a/b.c'), 'a'); + expect(context.dirname('a/'), '.'); + expect(context.dirname('a/.'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname('http://dartlang.org/a'), 'http://dartlang.org'); + expect(context.dirname('file:///a'), 'file://'); + expect(context.dirname('/a'), '/'); + expect(context.dirname('http://dartlang.org///a'), 'http://dartlang.org'); + expect(context.dirname('file://///a'), 'file://'); + expect(context.dirname('///a'), '/'); + expect(context.dirname('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.dirname('http://dartlang.org'), 'http://dartlang.org'); + expect(context.dirname('file:///'), 'file://'); + expect(context.dirname('file://'), 'file://'); + expect(context.dirname('/'), '/'); + expect(context.dirname('http://dartlang.org///'), 'http://dartlang.org'); + expect(context.dirname('file://///'), 'file://'); + expect(context.dirname('///'), '/'); + expect(context.dirname('a/b/'), 'a'); + expect(context.dirname(r'a/b\c'), 'a'); + expect(context.dirname('a//'), '.'); + expect(context.dirname('a/b//'), 'a'); + expect(context.dirname('a//b'), 'a'); }); test('basename', () { - expect(builder.basename(''), ''); - expect(builder.basename('a'), 'a'); - expect(builder.basename('a/b'), 'b'); - expect(builder.basename('a/b/c'), 'c'); - expect(builder.basename('a/b.c'), 'b.c'); - expect(builder.basename('a/'), 'a'); - expect(builder.basename('a/.'), '.'); - expect(builder.basename(r'a\b/c'), 'c'); - expect(builder.basename('http://dartlang.org/a'), 'a'); - expect(builder.basename('file:///a'), 'a'); - expect(builder.basename('/a'), 'a'); - expect(builder.basename('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.basename('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.basename('file:///'), 'file://'); - expect(builder.basename('file://'), 'file://'); - expect(builder.basename('/'), '/'); - expect(builder.basename('a/b/'), 'b'); - expect(builder.basename(r'a/b\c'), r'b\c'); - expect(builder.basename('a//'), 'a'); - expect(builder.basename('a/b//'), 'b'); - expect(builder.basename('a//b'), 'b'); - expect(builder.basename('a b/c d.e f'), 'c d.e f'); + expect(context.basename(''), ''); + expect(context.basename('a'), 'a'); + expect(context.basename('a/b'), 'b'); + expect(context.basename('a/b/c'), 'c'); + expect(context.basename('a/b.c'), 'b.c'); + expect(context.basename('a/'), 'a'); + expect(context.basename('a/.'), '.'); + expect(context.basename(r'a\b/c'), 'c'); + expect(context.basename('http://dartlang.org/a'), 'a'); + expect(context.basename('file:///a'), 'a'); + expect(context.basename('/a'), 'a'); + expect(context.basename('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.basename('http://dartlang.org'), 'http://dartlang.org'); + expect(context.basename('file:///'), 'file://'); + expect(context.basename('file://'), 'file://'); + expect(context.basename('/'), '/'); + expect(context.basename('a/b/'), 'b'); + expect(context.basename(r'a/b\c'), r'b\c'); + expect(context.basename('a//'), 'a'); + expect(context.basename('a/b//'), 'b'); + expect(context.basename('a//b'), 'b'); + expect(context.basename('a b/c d.e f'), 'c d.e f'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension('a/b'), 'b'); - expect(builder.basenameWithoutExtension('a/b/c'), 'c'); - expect(builder.basenameWithoutExtension('a/b.c'), 'b'); - expect(builder.basenameWithoutExtension('a/'), 'a'); - expect(builder.basenameWithoutExtension('a/.'), '.'); - expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); - expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension('a//'), 'a'); - expect(builder.basenameWithoutExtension('a/b//'), 'b'); - expect(builder.basenameWithoutExtension('a//b'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); - expect(builder.basenameWithoutExtension('a/b c.d e.f g'), 'b c.d e'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension('a/b'), 'b'); + expect(context.basenameWithoutExtension('a/b/c'), 'c'); + expect(context.basenameWithoutExtension('a/b.c'), 'b'); + expect(context.basenameWithoutExtension('a/'), 'a'); + expect(context.basenameWithoutExtension('a/.'), '.'); + expect(context.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(context.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension('a//'), 'a'); + expect(context.basenameWithoutExtension('a/b//'), 'b'); + expect(context.basenameWithoutExtension('a//b'), 'b'); + expect(context.basenameWithoutExtension('a/b.c/'), 'b'); + expect(context.basenameWithoutExtension('a/b.c//'), 'b'); + expect(context.basenameWithoutExtension('a/b c.d e.f g'), 'b c.d e'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute('a/b'), false); - expect(builder.isAbsolute('http://dartlang.org/a'), true); - expect(builder.isAbsolute('file:///a'), true); - expect(builder.isAbsolute('/a'), true); - expect(builder.isAbsolute('http://dartlang.org/a/b'), true); - expect(builder.isAbsolute('file:///a/b'), true); - expect(builder.isAbsolute('/a/b'), true); - expect(builder.isAbsolute('http://dartlang.org/'), true); - expect(builder.isAbsolute('file:///'), true); - expect(builder.isAbsolute('http://dartlang.org'), true); - expect(builder.isAbsolute('file://'), true); - expect(builder.isAbsolute('/'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('../a'), false); - expect(builder.isAbsolute('C:/a'), false); - expect(builder.isAbsolute(r'C:\a'), false); - expect(builder.isAbsolute(r'\\a'), false); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute('a/b'), false); + expect(context.isAbsolute('http://dartlang.org/a'), true); + expect(context.isAbsolute('file:///a'), true); + expect(context.isAbsolute('/a'), true); + expect(context.isAbsolute('http://dartlang.org/a/b'), true); + expect(context.isAbsolute('file:///a/b'), true); + expect(context.isAbsolute('/a/b'), true); + expect(context.isAbsolute('http://dartlang.org/'), true); + expect(context.isAbsolute('file:///'), true); + expect(context.isAbsolute('http://dartlang.org'), true); + expect(context.isAbsolute('file://'), true); + expect(context.isAbsolute('/'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('../a'), false); + expect(context.isAbsolute('C:/a'), false); + expect(context.isAbsolute(r'C:\a'), false); + expect(context.isAbsolute(r'\\a'), false); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative('a/b'), true); - expect(builder.isRelative('http://dartlang.org/a'), false); - expect(builder.isRelative('file:///a'), false); - expect(builder.isRelative('/a'), false); - expect(builder.isRelative('http://dartlang.org/a/b'), false); - expect(builder.isRelative('file:///a/b'), false); - expect(builder.isRelative('/a/b'), false); - expect(builder.isRelative('http://dartlang.org/'), false); - expect(builder.isRelative('file:///'), false); - expect(builder.isRelative('http://dartlang.org'), false); - expect(builder.isRelative('file://'), false); - expect(builder.isRelative('/'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('../a'), true); - expect(builder.isRelative('C:/a'), true); - expect(builder.isRelative(r'C:\a'), true); - expect(builder.isRelative(r'\\a'), true); + expect(context.isRelative(''), true); + expect(context.isRelative('a'), true); + expect(context.isRelative('a/b'), true); + expect(context.isRelative('http://dartlang.org/a'), false); + expect(context.isRelative('file:///a'), false); + expect(context.isRelative('/a'), false); + expect(context.isRelative('http://dartlang.org/a/b'), false); + expect(context.isRelative('file:///a/b'), false); + expect(context.isRelative('/a/b'), false); + expect(context.isRelative('http://dartlang.org/'), false); + expect(context.isRelative('file:///'), false); + expect(context.isRelative('http://dartlang.org'), false); + expect(context.isRelative('file://'), false); + expect(context.isRelative('/'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('../a'), true); + expect(context.isRelative('C:/a'), true); + expect(context.isRelative(r'C:\a'), true); + expect(context.isRelative(r'\\a'), true); }); test('isRootRelative', () { - expect(builder.isRootRelative(''), false); - expect(builder.isRootRelative('a'), false); - expect(builder.isRootRelative('a/b'), false); - expect(builder.isRootRelative('http://dartlang.org/a'), false); - expect(builder.isRootRelative('file:///a'), false); - expect(builder.isRootRelative('/a'), true); - expect(builder.isRootRelative('http://dartlang.org/a/b'), false); - expect(builder.isRootRelative('file:///a/b'), false); - expect(builder.isRootRelative('/a/b'), true); - expect(builder.isRootRelative('http://dartlang.org/'), false); - expect(builder.isRootRelative('file:///'), false); - expect(builder.isRootRelative('http://dartlang.org'), false); - expect(builder.isRootRelative('file://'), false); - expect(builder.isRootRelative('/'), true); - expect(builder.isRootRelative('~'), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative('../a'), false); - expect(builder.isRootRelative('C:/a'), false); - expect(builder.isRootRelative(r'C:\a'), false); - expect(builder.isRootRelative(r'\\a'), false); + expect(context.isRootRelative(''), false); + expect(context.isRootRelative('a'), false); + expect(context.isRootRelative('a/b'), false); + expect(context.isRootRelative('http://dartlang.org/a'), false); + expect(context.isRootRelative('file:///a'), false); + expect(context.isRootRelative('/a'), true); + expect(context.isRootRelative('http://dartlang.org/a/b'), false); + expect(context.isRootRelative('file:///a/b'), false); + expect(context.isRootRelative('/a/b'), true); + expect(context.isRootRelative('http://dartlang.org/'), false); + expect(context.isRootRelative('file:///'), false); + expect(context.isRootRelative('http://dartlang.org'), false); + expect(context.isRootRelative('file://'), false); + expect(context.isRootRelative('/'), true); + expect(context.isRootRelative('~'), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative('../a'), false); + expect(context.isRootRelative('C:/a'), false); + expect(context.isRootRelative(r'C:\a'), false); + expect(context.isRootRelative(r'\\a'), false); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), 'a/b'); - expect(builder.join('a', 'b', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), 'a/b'); + expect(context.join('a', 'b', 'c'), 'a/b/c'); + expect(context.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); }); test('does not add separator if a part ends in one', () { - expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); - expect(builder.join('a\\', 'b'), r'a\/b'); + expect(context.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(context.join('a\\', 'b'), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', 'http://dartlang.org', 'b', 'c'), + expect(context.join('a', 'http://dartlang.org', 'b', 'c'), 'http://dartlang.org/b/c'); - expect(builder.join('a', 'file://', 'b', 'c'), 'file:///b/c'); - expect(builder.join('a', '/', 'b', 'c'), '/b/c'); - expect(builder.join('a', '/b', 'http://dartlang.org/c', 'd'), + expect(context.join('a', 'file://', 'b', 'c'), 'file:///b/c'); + expect(context.join('a', '/', 'b', 'c'), '/b/c'); + expect(context.join('a', '/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.join( + expect(context.join( 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); - expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + expect(context.join('a', '/b', '/c', 'd'), '/c/d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(context.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); }); test('preserves roots before a root-relative path', () { - expect(builder.join('http://dartlang.org', 'a', '/b', 'c'), + expect(context.join('http://dartlang.org', 'a', '/b', 'c'), 'http://dartlang.org/b/c'); - expect(builder.join('file://', 'a', '/b', 'c'), 'file:///b/c'); - expect(builder.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); + expect(context.join('file://', 'a', '/b', 'c'), 'file:///b/c'); + expect(context.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals('a/b/c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', ''), 'a/b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(context.join('a', 'b', ''), 'a/b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('Join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), 'a/b/./c/..//d/.././..//e/f//'); - expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); - expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), 'a/b/c/d/e/f/g/h/i'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', 'http://dartlang.org', 'b', 'c']), + expect(context.joinAll(['a', 'http://dartlang.org', 'b', 'c']), 'http://dartlang.org/b/c'); - expect(builder.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); - expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); - expect(builder.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), + expect(context.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); + expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(context.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(builder.joinAll( + expect(context.joinAll( ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); - expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); }); test('preserves roots before a root-relative path', () { - expect(builder.joinAll(['http://dartlang.org', 'a', '/b', 'c']), + expect(context.joinAll(['http://dartlang.org', 'a', '/b', 'c']), 'http://dartlang.org/b/c'); - expect(builder.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); - expect(builder.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); + expect(context.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); + expect(context.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/../bar/./baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/../bar/./baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split('http://dartlang.org//'), + expect(context.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split('http://dartlang.org//'), equals(['http://dartlang.org'])); - expect(builder.split('file:////'), equals(['file://'])); - expect(builder.split('//'), equals(['/'])); + expect(context.split('file:////'), equals(['file://'])); + expect(context.split('//'), equals(['/'])); }); test('includes the root for absolute paths', () { - expect(builder.split('http://dartlang.org/foo/bar/baz'), + expect(context.split('http://dartlang.org/foo/bar/baz'), equals(['http://dartlang.org', 'foo', 'bar', 'baz'])); - expect(builder.split('file:///foo/bar/baz'), + expect(context.split('file:///foo/bar/baz'), equals(['file://', 'foo', 'bar', 'baz'])); - expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); - expect(builder.split('http://dartlang.org/'), + expect(context.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(context.split('http://dartlang.org/'), equals(['http://dartlang.org'])); - expect(builder.split('http://dartlang.org'), + expect(context.split('http://dartlang.org'), equals(['http://dartlang.org'])); - expect(builder.split('file:///'), equals(['file://'])); - expect(builder.split('file://'), equals(['file://'])); - expect(builder.split('/'), equals(['/'])); + expect(context.split('file:///'), equals(['file://'])); + expect(context.split('file://'), equals(['file://'])); + expect(context.split('/'), equals(['/'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.normalize('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.normalize('file://'), 'file://'); - expect(builder.normalize('file:///'), 'file://'); - expect(builder.normalize('/'), '/'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize('C:/'), 'C:'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\'), r'\\'); - expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.normalize('http://dartlang.org'), 'http://dartlang.org'); + expect(context.normalize('file://'), 'file://'); + expect(context.normalize('file:///'), 'file://'); + expect(context.normalize('/'), '/'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize('C:/'), 'C:'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\'), r'\\'); + expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a/b/c'), r'a/b/c'); - expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + expect(context.normalize(r'a/b/c'), r'a/b/c'); + expect(context.normalize(r'a//b///c////d'), r'a/b/c/d'); }); test('does not collapse separators for other platform', () { - expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + expect(context.normalize(r'a\\b\\\c'), r'a\\b\\\c'); }); test('eliminates "." parts', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('http://dartlang.org/.'), 'http://dartlang.org'); - expect(builder.normalize('file:///.'), 'file://'); - expect(builder.normalize('/.'), '/'); - expect(builder.normalize('http://dartlang.org/./'), + expect(context.normalize('./'), '.'); + expect(context.normalize('http://dartlang.org/.'), 'http://dartlang.org'); + expect(context.normalize('file:///.'), 'file://'); + expect(context.normalize('/.'), '/'); + expect(context.normalize('http://dartlang.org/./'), 'http://dartlang.org'); - expect(builder.normalize('file:///./'), 'file://'); - expect(builder.normalize('/./'), '/'); - expect(builder.normalize('./.'), '.'); - expect(builder.normalize('a/./b'), 'a/b'); - expect(builder.normalize('a/.b/c'), 'a/.b/c'); - expect(builder.normalize('a/././b/./c'), 'a/b/c'); - expect(builder.normalize('././a'), 'a'); - expect(builder.normalize('a/./.'), 'a'); + expect(context.normalize('file:///./'), 'file://'); + expect(context.normalize('/./'), '/'); + expect(context.normalize('./.'), '.'); + expect(context.normalize('a/./b'), 'a/b'); + expect(context.normalize('a/.b/c'), 'a/.b/c'); + expect(context.normalize('a/././b/./c'), 'a/b/c'); + expect(context.normalize('././a'), 'a'); + expect(context.normalize('a/./.'), 'a'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('../../..'), '../../..'); - expect(builder.normalize('../../../'), '../../..'); - expect(builder.normalize('http://dartlang.org/..'), + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('../../..'), '../../..'); + expect(context.normalize('../../../'), '../../..'); + expect(context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); - expect(builder.normalize('file:///..'), 'file://'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('http://dartlang.org/../../..'), + expect(context.normalize('file:///..'), 'file://'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('http://dartlang.org/../../..'), 'http://dartlang.org'); - expect(builder.normalize('file:///../../..'), 'file://'); - expect(builder.normalize('/../../..'), '/'); - expect(builder.normalize('http://dartlang.org/../../../a'), + expect(context.normalize('file:///../../..'), 'file://'); + expect(context.normalize('/../../..'), '/'); + expect(context.normalize('http://dartlang.org/../../../a'), 'http://dartlang.org/a'); - expect(builder.normalize('file:///../../../a'), 'file:///a'); - expect(builder.normalize('/../../../a'), '/a'); - expect(builder.normalize('c:/..'), '.'); - expect(builder.normalize('A:/../../..'), '../..'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('z/a/b/../../..\../c'), 'z/..\../c'); - expect(builder.normalize('a/b\c/../d'), 'a/d'); + expect(context.normalize('file:///../../../a'), 'file:///a'); + expect(context.normalize('/../../../a'), '/a'); + expect(context.normalize('c:/..'), '.'); + expect(context.normalize('A:/../../..'), '../..'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('z/a/b/../../..\../c'), 'z/..\../c'); + expect(context.normalize('a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('http://dartlang.org/..'), + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); - expect(builder.normalize('http://dartlang.org/../a'), + expect(context.normalize('http://dartlang.org/../a'), 'http://dartlang.org/a'); - expect(builder.normalize('file:///..'), 'file://'); - expect(builder.normalize('file:///../a'), 'file:///a'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('../a'), '../a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('c:/../a'), 'a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('../a/b/..'), '../a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); + expect(context.normalize('file:///..'), 'file://'); + expect(context.normalize('file:///../a'), 'file:///a'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('../a'), '../a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('c:/../a'), 'a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('../a/b/..'), '../a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('.//'), '.'); - expect(builder.normalize('a/'), 'a'); - expect(builder.normalize('a/b/'), 'a/b'); - expect(builder.normalize(r'a/b\'), r'a/b\'); - expect(builder.normalize('a/b///'), 'a/b'); + expect(context.normalize('./'), '.'); + expect(context.normalize('.//'), '.'); + expect(context.normalize('a/'), 'a'); + expect(context.normalize('a/b/'), 'a/b'); + expect(context.normalize(r'a/b\'), r'a/b\'); + expect(context.normalize('a/b///'), 'a/b'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative('http://dartlang.org'), '../..'); - expect(builder.relative('http://dartlang.org/'), '../..'); - expect(builder.relative('/'), '../..'); - expect(builder.relative('http://dartlang.org/root'), '..'); - expect(builder.relative('/root'), '..'); - expect(builder.relative('http://dartlang.org/root/path'), '.'); - expect(builder.relative('/root/path'), '.'); - expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org'), '../..'); + expect(context.relative('http://dartlang.org/'), '../..'); + expect(context.relative('/'), '../..'); + expect(context.relative('http://dartlang.org/root'), '..'); + expect(context.relative('/root'), '..'); + expect(context.relative('http://dartlang.org/root/path'), '.'); + expect(context.relative('/root/path'), '.'); + expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/a/b.txt'), + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path outside of root', () { - expect(builder.relative('http://dartlang.org/a/b'), '../../a/b'); - expect(builder.relative('/a/b'), '../../a/b'); - expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org/a/b'), '../../a/b'); + expect(context.relative('/a/b'), '../../a/b'); + expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/a/b.txt'), + expect(context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path with different hostname/protocol', () { - expect(builder.relative(r'http://google.com/a/b'), + expect(context.relative(r'http://google.com/a/b'), r'http://google.com/a/b'); - expect(builder.relative(r'file:///a/b'), + expect(context.relative(r'file:///a/b'), r'file:///a/b'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative('a/b.txt'), 'a/b.txt'); - expect(builder.relative('../a/b.txt'), '../a/b.txt'); - expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative('a/b.txt'), 'a/b.txt'); + expect(context.relative('../a/b.txt'), '../a/b.txt'); + expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative('http://dartlang.org', + expect(context.relative('http://dartlang.org', from: 'http://dartlang.org'), '.'); - expect(builder.relative('http://dartlang.org/root/path', + expect(context.relative('http://dartlang.org/root/path', from: 'http://dartlang.org'), 'root/path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); + var r = new path.Context(style: path.Style.url, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -513,7 +513,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Builder(style: path.Style.url, root: '/foo/bar'); + var r = new path.Context(style: path.Style.url, current: '/foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -538,50 +538,50 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.url, root: '/dir.ext'); + var r = new path.Context(style: path.Style.url, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(context.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect( - builder.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + context.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); expect( - builder.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + context.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); expect( - builder.relative('/foo/bar/baz', from: 'file:///foo/bar'), + context.relative('/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect( - builder.relative('file:///foo/bar/baz', from: '/foo/bar'), + context.relative('file:///foo/bar/baz', from: '/foo/bar'), equals('file:///foo/bar/baz')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('..', from: 'http://dartlang.org/foo/bar'), + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('..', from: 'http://dartlang.org/foo/bar'), equals('../../root')); - expect(builder.relative('..', from: 'file:///foo/bar'), + expect(context.relative('..', from: 'file:///foo/bar'), equals('http://dartlang.org/root')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('file:///foo/bar/baz', from: 'foo/bar'), + expect(context.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); - expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + expect(context.relative('/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + expect(context.relative('..', from: 'foo/bar'), equals('../../..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'relative/root'); + var r = new path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect( r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), @@ -606,7 +606,7 @@ main() { }); test('from a . root', () { - var r = new path.Builder(style: path.Style.url, root: '.'); + var r = new path.Context(style: path.Style.url, current: '.'); expect(r.relative('http://dartlang.org/foo/bar/baz'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); @@ -617,120 +617,120 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); - expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); - expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); - expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect(builder.isWithin( + expect(context.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(context.isWithin( 'http://dartlang.org', 'http://dartlang.org/foo/bar'), isTrue); - expect(builder.isWithin( + expect(context.isWithin( 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), isFalse); - expect(builder.isWithin('http://dartlang.org', '/foo/bar'), isTrue); - expect(builder.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); - expect(builder.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); - expect(builder.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), + expect(context.isWithin('http://dartlang.org', '/foo/bar'), isTrue); + expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); + expect(context.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); + expect(context.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), isTrue); - expect(builder.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), + expect(context.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); - expect(builder.isWithin('.', 'a/b/c'), isTrue); - expect(builder.isWithin('.', '../a/b/c'), isFalse); - expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(builder.isWithin( + var r = new path.Context(style: path.Style.url, current: 'foo/bar'); + expect(context.isWithin('.', 'a/b/c'), isTrue); + expect(context.isWithin('.', '../a/b/c'), isFalse); + expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(context.isWithin( 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), isTrue); - expect(builder.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); + expect(context.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), 'http://dartlang.org/root/path/a'); - expect(builder.resolve('a', 'b'), 'http://dartlang.org/root/path/a/b'); - expect(builder.resolve('a', 'b', 'c'), + expect(context.absolute('a'), 'http://dartlang.org/root/path/a'); + expect(context.absolute('a', 'b'), 'http://dartlang.org/root/path/a/b'); + expect(context.absolute('a', 'b', 'c'), 'http://dartlang.org/root/path/a/b/c'); - expect(builder.resolve('a', 'b', 'c', 'd'), + expect(context.absolute('a', 'b', 'c', 'd'), 'http://dartlang.org/root/path/a/b/c/d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + expect(context.absolute('a', 'b', 'c', 'd', 'e'), 'http://dartlang.org/root/path/a/b/c/d/e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), 'http://dartlang.org/root/path/a/b/c/d/e/f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'http://dartlang.org/root/path/a/b/c/d/e/f/g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve('a/', 'b', 'c/', 'd'), + expect(context.absolute('a/', 'b', 'c/', 'd'), 'http://dartlang.org/root/path/a/b/c/d'); - expect(builder.resolve(r'a\', 'b'), + expect(context.absolute(r'a\', 'b'), r'http://dartlang.org/root/path/a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.resolve('a', '/b', 'file:///c', 'd'), 'file:///c/d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), + expect(context.absolute('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); + expect(context.absolute('a', '/b', 'file:///c', 'd'), 'file:///c/d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), r'http://dartlang.org/root/path/a/c:\b/c/d'); - expect(builder.resolve('a', r'\\b', 'c', 'd'), + expect(context.absolute('a', r'\\b', 'c', 'd'), r'http://dartlang.org/root/path/a/\\b/c/d'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension('a/b.c'), 'a/b'); - expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); - expect(builder.withoutExtension('a/'), 'a/'); - expect(builder.withoutExtension('a/b/'), 'a/b/'); - expect(builder.withoutExtension('a/.'), 'a/.'); - expect(builder.withoutExtension('a/.b'), 'a/.b'); - expect(builder.withoutExtension('a.b/c'), 'a.b/c'); - expect(builder.withoutExtension(r'a.b\c'), r'a'); - expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); - expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); - expect(builder.withoutExtension('a/b.c/'), 'a/b/'); - expect(builder.withoutExtension('a/b.c//'), 'a/b//'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension('a/b.c'), 'a/b'); + expect(context.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(context.withoutExtension('a/'), 'a/'); + expect(context.withoutExtension('a/b/'), 'a/b/'); + expect(context.withoutExtension('a/.'), 'a/.'); + expect(context.withoutExtension('a/.b'), 'a/.b'); + expect(context.withoutExtension('a.b/c'), 'a.b/c'); + expect(context.withoutExtension(r'a.b\c'), r'a'); + expect(context.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(context.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(context.withoutExtension('a/b.c/'), 'a/b/'); + expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), 'http://dartlang.org/path/to/foo'); - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), 'http://dartlang.org/path/to/foo/'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo')), + expect(context.fromUri(Uri.parse('file:///path/to/foo')), 'file:///path/to/foo'); - expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), 'http://dartlang.org/path/to/foo%23bar'); // Since the resulting "path" is also a URL, special characters should // remain percent-encoded in the result. - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_%7B_%7D_%60_%5E_%20_%22_%25_'); }); test('toUri', () { - expect(builder.toUri('http://dartlang.org/path/to/foo'), + expect(context.toUri('http://dartlang.org/path/to/foo'), Uri.parse('http://dartlang.org/path/to/foo')); - expect(builder.toUri('http://dartlang.org/path/to/foo/'), + expect(context.toUri('http://dartlang.org/path/to/foo/'), Uri.parse('http://dartlang.org/path/to/foo/')); - expect(builder.toUri('file:///path/to/foo'), + expect(context.toUri('file:///path/to/foo'), Uri.parse('file:///path/to/foo')); - expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); - expect(builder.toUri('http://dartlang.org/path/to/foo%23bar'), + expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(context.toUri('http://dartlang.org/path/to/foo%23bar'), Uri.parse('http://dartlang.org/path/to/foo%23bar')); // Since the input path is also a URI, special characters should already // be percent encoded there too. - expect(builder.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), + expect(context.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), + expect(context.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index f1a2395e..e5dcac49 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -10,437 +10,429 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var builder = new path.Builder(style: path.Style.windows, - root: r'C:\root\path'); - - if (new path.Builder().style == path.Style.windows) { - group('absolute', () { - expect(path.absolute(r'a\b.txt'), path.join(path.current, r'a\b.txt')); - expect(path.absolute(r'C:\a\b.txt'), r'C:\a\b.txt'); - expect(path.absolute(r'\\a\b.txt'), r'\\a\b.txt'); - }); - } + var context = new path.Context(style: path.Style.windows, + current: r'C:\root\path'); group('separator', () { - expect(builder.separator, '\\'); + expect(context.separator, '\\'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('.'), ''); - expect(builder.extension('..'), ''); - expect(builder.extension('a/..'), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('foo bar\gule fisk.dart.js'), '.js'); - expect(builder.extension(r'a.b\c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension(r'~\.bashrc'), ''); - expect(builder.extension(r'a.b/c'), r''); - expect(builder.extension(r'foo.dart\'), '.dart'); - expect(builder.extension(r'foo.dart\\'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('.'), ''); + expect(context.extension('..'), ''); + expect(context.extension('a/..'), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('foo bar\gule fisk.dart.js'), '.js'); + expect(context.extension(r'a.b\c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension(r'~\.bashrc'), ''); + expect(context.extension(r'a.b/c'), r''); + expect(context.extension(r'foo.dart\'), '.dart'); + expect(context.extension(r'foo.dart\\'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix(r'a\b'), ''); - expect(builder.rootPrefix(r'C:\a\c'), r'C:\'); - expect(builder.rootPrefix('C:\\'), r'C:\'); - expect(builder.rootPrefix('C:/'), 'C:/'); - expect(builder.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); - expect(builder.rootPrefix(r'\a\b'), r'\'); - expect(builder.rootPrefix(r'/a/b'), r'/'); - expect(builder.rootPrefix(r'\'), r'\'); - expect(builder.rootPrefix(r'/'), r'/'); + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix(r'a\b'), ''); + expect(context.rootPrefix(r'C:\a\c'), r'C:\'); + expect(context.rootPrefix('C:\\'), r'C:\'); + expect(context.rootPrefix('C:/'), 'C:/'); + expect(context.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); + expect(context.rootPrefix(r'\a\b'), r'\'); + expect(context.rootPrefix(r'/a/b'), r'/'); + expect(context.rootPrefix(r'\'), r'\'); + expect(context.rootPrefix(r'/'), r'/'); }); test('dirname', () { - expect(builder.dirname(r''), '.'); - expect(builder.dirname(r'a'), '.'); - expect(builder.dirname(r'a\b'), 'a'); - expect(builder.dirname(r'a\b\c'), r'a\b'); - expect(builder.dirname(r'a\b.c'), 'a'); - expect(builder.dirname(r'a\'), '.'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname(r'a\.'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname(r'C:\a'), r'C:\'); - expect(builder.dirname(r'C:\\\a'), r'C:\'); - expect(builder.dirname(r'C:\'), r'C:\'); - expect(builder.dirname(r'C:\\\'), r'C:\'); - expect(builder.dirname(r'a\b\'), r'a'); - expect(builder.dirname(r'a/b\c'), 'a/b'); - expect(builder.dirname(r'a\\'), r'.'); - expect(builder.dirname(r'a\b\\'), 'a'); - expect(builder.dirname(r'a\\b'), 'a'); - expect(builder.dirname(r'foo bar\gule fisk'), 'foo bar'); - expect(builder.dirname(r'\\server\share'), r'\\server\share'); - expect(builder.dirname(r'\\server\share\dir'), r'\\server\share'); - expect(builder.dirname(r'\a'), r'\'); - expect(builder.dirname(r'/a'), r'/'); - expect(builder.dirname(r'\'), r'\'); - expect(builder.dirname(r'/'), r'/'); + expect(context.dirname(r''), '.'); + expect(context.dirname(r'a'), '.'); + expect(context.dirname(r'a\b'), 'a'); + expect(context.dirname(r'a\b\c'), r'a\b'); + expect(context.dirname(r'a\b.c'), 'a'); + expect(context.dirname(r'a\'), '.'); + expect(context.dirname('a/'), '.'); + expect(context.dirname(r'a\.'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname(r'C:\a'), r'C:\'); + expect(context.dirname(r'C:\\\a'), r'C:\'); + expect(context.dirname(r'C:\'), r'C:\'); + expect(context.dirname(r'C:\\\'), r'C:\'); + expect(context.dirname(r'a\b\'), r'a'); + expect(context.dirname(r'a/b\c'), 'a/b'); + expect(context.dirname(r'a\\'), r'.'); + expect(context.dirname(r'a\b\\'), 'a'); + expect(context.dirname(r'a\\b'), 'a'); + expect(context.dirname(r'foo bar\gule fisk'), 'foo bar'); + expect(context.dirname(r'\\server\share'), r'\\server\share'); + expect(context.dirname(r'\\server\share\dir'), r'\\server\share'); + expect(context.dirname(r'\a'), r'\'); + expect(context.dirname(r'/a'), r'/'); + expect(context.dirname(r'\'), r'\'); + expect(context.dirname(r'/'), r'/'); }); test('basename', () { - expect(builder.basename(r''), ''); - expect(builder.basename(r'.'), '.'); - expect(builder.basename(r'..'), '..'); - expect(builder.basename(r'.hest'), '.hest'); - expect(builder.basename(r'a'), 'a'); - expect(builder.basename(r'a\b'), 'b'); - expect(builder.basename(r'a\b\c'), 'c'); - expect(builder.basename(r'a\b.c'), 'b.c'); - expect(builder.basename(r'a\'), 'a'); - expect(builder.basename(r'a/'), 'a'); - expect(builder.basename(r'a\.'), '.'); - expect(builder.basename(r'a\b/c'), r'c'); - expect(builder.basename(r'C:\a'), 'a'); - expect(builder.basename(r'C:\'), r'C:\'); - expect(builder.basename(r'a\b\'), 'b'); - expect(builder.basename(r'a/b\c'), 'c'); - expect(builder.basename(r'a\\'), 'a'); - expect(builder.basename(r'a\b\\'), 'b'); - expect(builder.basename(r'a\\b'), 'b'); - expect(builder.basename(r'a\\b'), 'b'); - expect(builder.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); - expect(builder.basename(r'\\server\share'), r'\\server\share'); - expect(builder.basename(r'\\server\share\dir'), r'dir'); - expect(builder.basename(r'\a'), r'a'); - expect(builder.basename(r'/a'), r'a'); - expect(builder.basename(r'\'), r'\'); - expect(builder.basename(r'/'), r'/'); + expect(context.basename(r''), ''); + expect(context.basename(r'.'), '.'); + expect(context.basename(r'..'), '..'); + expect(context.basename(r'.hest'), '.hest'); + expect(context.basename(r'a'), 'a'); + expect(context.basename(r'a\b'), 'b'); + expect(context.basename(r'a\b\c'), 'c'); + expect(context.basename(r'a\b.c'), 'b.c'); + expect(context.basename(r'a\'), 'a'); + expect(context.basename(r'a/'), 'a'); + expect(context.basename(r'a\.'), '.'); + expect(context.basename(r'a\b/c'), r'c'); + expect(context.basename(r'C:\a'), 'a'); + expect(context.basename(r'C:\'), r'C:\'); + expect(context.basename(r'a\b\'), 'b'); + expect(context.basename(r'a/b\c'), 'c'); + expect(context.basename(r'a\\'), 'a'); + expect(context.basename(r'a\b\\'), 'b'); + expect(context.basename(r'a\\b'), 'b'); + expect(context.basename(r'a\\b'), 'b'); + expect(context.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); + expect(context.basename(r'\\server\share'), r'\\server\share'); + expect(context.basename(r'\\server\share\dir'), r'dir'); + expect(context.basename(r'\a'), r'a'); + expect(context.basename(r'/a'), r'a'); + expect(context.basename(r'\'), r'\'); + expect(context.basename(r'/'), r'/'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('.hest'), '.hest'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension(r'a\b'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b\c'), 'c'); - expect(builder.basenameWithoutExtension(r'a\b.c'), 'b'); - expect(builder.basenameWithoutExtension(r'a\'), 'a'); - expect(builder.basenameWithoutExtension(r'a\.'), '.'); - expect(builder.basenameWithoutExtension(r'a\b/c'), r'c'); - expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension(r'a\\'), 'a'); - expect(builder.basenameWithoutExtension(r'a\b\\'), 'b'); - expect(builder.basenameWithoutExtension(r'a\\b'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b.c\'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b.c\\'), 'b'); - expect(builder.basenameWithoutExtension(r'C:\f h.ma pa.f s'), 'f h.ma pa'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('.hest'), '.hest'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension(r'a\b'), 'b'); + expect(context.basenameWithoutExtension(r'a\b\c'), 'c'); + expect(context.basenameWithoutExtension(r'a\b.c'), 'b'); + expect(context.basenameWithoutExtension(r'a\'), 'a'); + expect(context.basenameWithoutExtension(r'a\.'), '.'); + expect(context.basenameWithoutExtension(r'a\b/c'), r'c'); + expect(context.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension(r'a\\'), 'a'); + expect(context.basenameWithoutExtension(r'a\b\\'), 'b'); + expect(context.basenameWithoutExtension(r'a\\b'), 'b'); + expect(context.basenameWithoutExtension(r'a\b.c\'), 'b'); + expect(context.basenameWithoutExtension(r'a\b.c\\'), 'b'); + expect(context.basenameWithoutExtension(r'C:\f h.ma pa.f s'), 'f h.ma pa'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('..'), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute(r'a\b'), false); - expect(builder.isAbsolute(r'\a\b'), true); - expect(builder.isAbsolute(r'\'), true); - expect(builder.isAbsolute(r'/a/b'), true); - expect(builder.isAbsolute(r'/'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute(r'..\a'), false); - expect(builder.isAbsolute(r'a:/a\b'), true); - expect(builder.isAbsolute(r'D:/a/b'), true); - expect(builder.isAbsolute(r'c:\'), true); - expect(builder.isAbsolute(r'B:\'), true); - expect(builder.isAbsolute(r'c:\a'), true); - expect(builder.isAbsolute(r'C:\a'), true); - expect(builder.isAbsolute(r'\\server\share'), true); - expect(builder.isAbsolute(r'\\server\share\path'), true); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('..'), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute(r'a\b'), false); + expect(context.isAbsolute(r'\a\b'), true); + expect(context.isAbsolute(r'\'), true); + expect(context.isAbsolute(r'/a/b'), true); + expect(context.isAbsolute(r'/'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute(r'..\a'), false); + expect(context.isAbsolute(r'a:/a\b'), true); + expect(context.isAbsolute(r'D:/a/b'), true); + expect(context.isAbsolute(r'c:\'), true); + expect(context.isAbsolute(r'B:\'), true); + expect(context.isAbsolute(r'c:\a'), true); + expect(context.isAbsolute(r'C:\a'), true); + expect(context.isAbsolute(r'\\server\share'), true); + expect(context.isAbsolute(r'\\server\share\path'), true); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('..'), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative(r'a\b'), true); - expect(builder.isRelative(r'\a\b'), false); - expect(builder.isRelative(r'\'), false); - expect(builder.isRelative(r'/a/b'), false); - expect(builder.isRelative(r'/'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative(r'..\a'), true); - expect(builder.isRelative(r'a:/a\b'), false); - expect(builder.isRelative(r'D:/a/b'), false); - expect(builder.isRelative(r'c:\'), false); - expect(builder.isRelative(r'B:\'), false); - expect(builder.isRelative(r'c:\a'), false); - expect(builder.isRelative(r'C:\a'), false); - expect(builder.isRelative(r'\\server\share'), false); - expect(builder.isRelative(r'\\server\share\path'), false); + expect(context.isRelative(''), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('..'), true); + expect(context.isRelative('a'), true); + expect(context.isRelative(r'a\b'), true); + expect(context.isRelative(r'\a\b'), false); + expect(context.isRelative(r'\'), false); + expect(context.isRelative(r'/a/b'), false); + expect(context.isRelative(r'/'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative(r'..\a'), true); + expect(context.isRelative(r'a:/a\b'), false); + expect(context.isRelative(r'D:/a/b'), false); + expect(context.isRelative(r'c:\'), false); + expect(context.isRelative(r'B:\'), false); + expect(context.isRelative(r'c:\a'), false); + expect(context.isRelative(r'C:\a'), false); + expect(context.isRelative(r'\\server\share'), false); + expect(context.isRelative(r'\\server\share\path'), false); }); test('isRootRelative', () { - expect(builder.isRootRelative(''), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative('..'), false); - expect(builder.isRootRelative('a'), false); - expect(builder.isRootRelative(r'a\b'), false); - expect(builder.isRootRelative(r'\a\b'), true); - expect(builder.isRootRelative(r'\'), true); - expect(builder.isRootRelative(r'/a/b'), true); - expect(builder.isRootRelative(r'/'), true); - expect(builder.isRootRelative('~'), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative(r'..\a'), false); - expect(builder.isRootRelative(r'a:/a\b'), false); - expect(builder.isRootRelative(r'D:/a/b'), false); - expect(builder.isRootRelative(r'c:\'), false); - expect(builder.isRootRelative(r'B:\'), false); - expect(builder.isRootRelative(r'c:\a'), false); - expect(builder.isRootRelative(r'C:\a'), false); - expect(builder.isRootRelative(r'\\server\share'), false); - expect(builder.isRootRelative(r'\\server\share\path'), false); + expect(context.isRootRelative(''), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative('..'), false); + expect(context.isRootRelative('a'), false); + expect(context.isRootRelative(r'a\b'), false); + expect(context.isRootRelative(r'\a\b'), true); + expect(context.isRootRelative(r'\'), true); + expect(context.isRootRelative(r'/a/b'), true); + expect(context.isRootRelative(r'/'), true); + expect(context.isRootRelative('~'), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative(r'..\a'), false); + expect(context.isRootRelative(r'a:/a\b'), false); + expect(context.isRootRelative(r'D:/a/b'), false); + expect(context.isRootRelative(r'c:\'), false); + expect(context.isRootRelative(r'B:\'), false); + expect(context.isRootRelative(r'c:\a'), false); + expect(context.isRootRelative(r'C:\a'), false); + expect(context.isRootRelative(r'\\server\share'), false); + expect(context.isRootRelative(r'\\server\share\path'), false); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), r'a\b'); - expect(builder.join('a', 'b', 'c'), r'a\b\c'); - expect(builder.join('a', 'b', 'c', 'd'), r'a\b\c\d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), r'a\b'); + expect(context.join('a', 'b', 'c'), r'a\b\c'); + expect(context.join('a', 'b', 'c', 'd'), r'a\b\c\d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), r'a\b\c\d\e\f\g\h'); }); test('does not add separator if a part ends or begins in one', () { - expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); - expect(builder.join('a/', 'b'), r'a/b'); + expect(context.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); + expect(context.join('a/', 'b'), r'a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', r'\b', r'\c', 'd'), r'\c\d'); - expect(builder.join('a', '/b', '/c', 'd'), r'/c\d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); - expect(builder.join('a', r'c:\b', r'\c', 'd'), r'c:\c\d'); - expect(builder.join('a', r'\\b\c\d', r'\e', 'f'), r'\\b\c\e\f'); + expect(context.join('a', r'\b', r'\c', 'd'), r'\c\d'); + expect(context.join('a', '/b', '/c', 'd'), r'/c\d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(context.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); + expect(context.join('a', r'c:\b', r'\c', 'd'), r'c:\c\d'); + expect(context.join('a', r'\\b\c\d', r'\e', 'f'), r'\\b\c\e\f'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals(r'a\b\c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals(r'a\b\c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), r'a\b\c'); - expect(builder.join('a', 'b', ''), r'a\b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), r'a\b\c'); + expect(context.join('a', 'b', ''), r'a\b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), r'a\b\./c\..\\d\..\.\..\\e\f\\'); - expect(builder.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); - expect(builder.join(r'a', 'b${builder.separator}'), r'a\b\'); + expect(context.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); + expect(context.join(r'a', 'b${context.separator}'), r'a\b\'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), r'a\b\c\d\e\f\g\h\i'); }); test('does not add separator if a part ends or begins in one', () { - expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); - expect(builder.joinAll(['a/', 'b']), r'a/b'); + expect(context.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); + expect(context.joinAll(['a/', 'b']), r'a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', r'\b', r'\c', 'd']), r'\c\d'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), r'/c\d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); - expect(builder.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); - expect(builder.joinAll(['a', r'c:\b', r'\c', 'd']), r'c:\c\d'); - expect(builder.joinAll(['a', r'\\b\c\d', r'\e', 'f']), r'\\b\c\e\f'); + expect(context.joinAll(['a', r'\b', r'\c', 'd']), r'\c\d'); + expect(context.joinAll(['a', '/b', '/c', 'd']), r'/c\d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); + expect(context.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); + expect(context.joinAll(['a', r'c:\b', r'\c', 'd']), r'c:\c\d'); + expect(context.joinAll(['a', r'\\b\c\d', r'\e', 'f']), r'\\b\c\e\f'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split(r'foo\bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split(r'foo\bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split(r'foo\..\bar\.\baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split(r'foo\bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split(r'foo\bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split(r'foo\..\bar\.\baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split(r'foo\\bar\\\baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split(r'foo\/\baz'), equals(['foo', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split(r'C:\'), equals([r'C:\'])); + expect(context.split(r'foo\\bar\\\baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split(r'foo\/\baz'), equals(['foo', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split(r'C:\'), equals([r'C:\'])); }); test('includes the root for absolute paths', () { - expect(builder.split(r'C:\foo\bar\baz'), + expect(context.split(r'C:\foo\bar\baz'), equals([r'C:\', 'foo', 'bar', 'baz'])); - expect(builder.split(r'C:\\'), equals([r'C:\'])); + expect(context.split(r'C:\\'), equals([r'C:\'])); - expect(builder.split(r'\\server\share\foo\bar\baz'), + expect(context.split(r'\\server\share\foo\bar\baz'), equals([r'\\server\share', 'foo', 'bar', 'baz'])); - expect(builder.split(r'\\server\share'), equals([r'\\server\share'])); + expect(context.split(r'\\server\share'), equals([r'\\server\share'])); - expect(builder.split(r'\foo\bar\baz'), + expect(context.split(r'\foo\bar\baz'), equals([r'\', 'foo', 'bar', 'baz'])); - expect(builder.split(r'\'), equals([r'\'])); + expect(context.split(r'\'), equals([r'\'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('/a/b'), r'\a\b'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize(r'\a\b'), r'\a\b'); - expect(builder.normalize('/'), r'\'); - expect(builder.normalize('C:/'), r'C:\'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\server\share'), r'\\server\share'); - expect(builder.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('/a/b'), r'\a\b'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize(r'\a\b'), r'\a\b'); + expect(context.normalize('/'), r'\'); + expect(context.normalize('C:/'), r'C:\'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\server\share'), r'\\server\share'); + expect(context.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a\b\c'), r'a\b\c'); - expect(builder.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d'); + expect(context.normalize(r'a\b\c'), r'a\b\c'); + expect(context.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d'); }); test('eliminates "." parts', () { - expect(builder.normalize(r'.\'), '.'); - expect(builder.normalize(r'c:\.'), r'c:\'); - expect(builder.normalize(r'B:\.\'), r'B:\'); - expect(builder.normalize(r'\\server\share\.'), r'\\server\share'); - expect(builder.normalize(r'.\.'), '.'); - expect(builder.normalize(r'a\.\b'), r'a\b'); - expect(builder.normalize(r'a\.b\c'), r'a\.b\c'); - expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c'); - expect(builder.normalize(r'.\./a'), 'a'); - expect(builder.normalize(r'a/.\.'), 'a'); - expect(builder.normalize(r'\.'), r'\'); - expect(builder.normalize('/.'), r'\'); + expect(context.normalize(r'.\'), '.'); + expect(context.normalize(r'c:\.'), r'c:\'); + expect(context.normalize(r'B:\.\'), r'B:\'); + expect(context.normalize(r'\\server\share\.'), r'\\server\share'); + expect(context.normalize(r'.\.'), '.'); + expect(context.normalize(r'a\.\b'), r'a\b'); + expect(context.normalize(r'a\.b\c'), r'a\.b\c'); + expect(context.normalize(r'a\./.\b\.\c'), r'a\b\c'); + expect(context.normalize(r'.\./a'), 'a'); + expect(context.normalize(r'a/.\.'), 'a'); + expect(context.normalize(r'\.'), r'\'); + expect(context.normalize('/.'), r'\'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize(r'..\'), '..'); - expect(builder.normalize(r'..\..\..'), r'..\..\..'); - expect(builder.normalize(r'../..\..\'), r'..\..\..'); - expect(builder.normalize(r'\\server\share\..'), r'\\server\share'); - expect(builder.normalize(r'\\server\share\..\../..\a'), + expect(context.normalize('..'), '..'); + expect(context.normalize(r'..\'), '..'); + expect(context.normalize(r'..\..\..'), r'..\..\..'); + expect(context.normalize(r'../..\..\'), r'..\..\..'); + expect(context.normalize(r'\\server\share\..'), r'\\server\share'); + expect(context.normalize(r'\\server\share\..\../..\a'), r'\\server\share\a'); - expect(builder.normalize(r'c:\..'), r'c:\'); - expect(builder.normalize(r'A:/..\..\..'), r'A:\'); - expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); - expect(builder.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); - expect(builder.normalize(r'a\..'), '.'); - expect(builder.normalize(r'..\a'), r'..\a'); - expect(builder.normalize(r'c:\..\a'), r'c:\a'); - expect(builder.normalize(r'\..\a'), r'\a'); - expect(builder.normalize(r'a\b\..'), 'a'); - expect(builder.normalize(r'..\a\b\..'), r'..\a'); - expect(builder.normalize(r'a\..\b'), 'b'); - expect(builder.normalize(r'a\.\..\b'), 'b'); - expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); - expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); - expect(builder.normalize(r'a/b/c/../../..d/./.e/f././'), r'a\..d\.e\f.'); + expect(context.normalize(r'c:\..'), r'c:\'); + expect(context.normalize(r'A:/..\..\..'), r'A:\'); + expect(context.normalize(r'b:\..\..\..\a'), r'b:\a'); + expect(context.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); + expect(context.normalize(r'a\..'), '.'); + expect(context.normalize(r'..\a'), r'..\a'); + expect(context.normalize(r'c:\..\a'), r'c:\a'); + expect(context.normalize(r'\..\a'), r'\a'); + expect(context.normalize(r'a\b\..'), 'a'); + expect(context.normalize(r'..\a\b\..'), r'..\a'); + expect(context.normalize(r'a\..\b'), 'b'); + expect(context.normalize(r'a\.\..\b'), 'b'); + expect(context.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); + expect(context.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); + expect(context.normalize(r'a/b/c/../../..d/./.e/f././'), r'a\..d\.e\f.'); }); test('removes trailing separators', () { - expect(builder.normalize(r'.\'), '.'); - expect(builder.normalize(r'.\\'), '.'); - expect(builder.normalize(r'a/'), 'a'); - expect(builder.normalize(r'a\b\'), r'a\b'); - expect(builder.normalize(r'a\b\\\'), r'a\b'); + expect(context.normalize(r'.\'), '.'); + expect(context.normalize(r'.\\'), '.'); + expect(context.normalize(r'a/'), 'a'); + expect(context.normalize(r'a\b\'), r'a\b'); + expect(context.normalize(r'a\b\\\'), r'a\b'); }); test('normalizes separators', () { - expect(builder.normalize(r'a/b\c'), r'a\b\c'); + expect(context.normalize(r'a/b\c'), r'a\b\c'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative(r'C:\'), r'..\..'); - expect(builder.relative(r'C:\root'), '..'); - expect(builder.relative(r'\root'), '..'); - expect(builder.relative(r'C:\root\path'), '.'); - expect(builder.relative(r'\root\path'), '.'); - expect(builder.relative(r'C:\root\path\a'), 'a'); - expect(builder.relative(r'\root\path\a'), 'a'); - expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'C:/'), r'..\..'); - expect(builder.relative(r'C:/root'), '..'); - expect(builder.relative(r'c:\'), r'..\..'); - expect(builder.relative(r'c:\root'), '..'); + expect(context.relative(r'C:\'), r'..\..'); + expect(context.relative(r'C:\root'), '..'); + expect(context.relative(r'\root'), '..'); + expect(context.relative(r'C:\root\path'), '.'); + expect(context.relative(r'\root\path'), '.'); + expect(context.relative(r'C:\root\path\a'), 'a'); + expect(context.relative(r'\root\path\a'), 'a'); + expect(context.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(context.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'C:/'), r'..\..'); + expect(context.relative(r'C:/root'), '..'); + expect(context.relative(r'c:\'), r'..\..'); + expect(context.relative(r'c:\root'), '..'); }); test('given absolute path outside of root', () { - expect(builder.relative(r'C:\a\b'), r'..\..\a\b'); - expect(builder.relative(r'\a\b'), r'..\..\a\b'); - expect(builder.relative(r'C:\root\path\a'), 'a'); - expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'C:/a/b'), r'..\..\a\b'); - expect(builder.relative(r'C:/root/path/a'), 'a'); - expect(builder.relative(r'c:\a\b'), r'..\..\a\b'); - expect(builder.relative(r'c:\root\path\a'), 'a'); + expect(context.relative(r'C:\a\b'), r'..\..\a\b'); + expect(context.relative(r'\a\b'), r'..\..\a\b'); + expect(context.relative(r'C:\root\path\a'), 'a'); + expect(context.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(context.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'C:/a/b'), r'..\..\a\b'); + expect(context.relative(r'C:/root/path/a'), 'a'); + expect(context.relative(r'c:\a\b'), r'..\..\a\b'); + expect(context.relative(r'c:\root\path\a'), 'a'); }); test('given absolute path on different drive', () { - expect(builder.relative(r'D:\a\b'), r'D:\a\b'); + expect(context.relative(r'D:\a\b'), r'D:\a\b'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative(r'a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative(r'a\b.txt'), r'a\b.txt'); + expect(context.relative(r'..\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative(r'C:\', from: r'C:\'), '.'); - expect(builder.relative(r'C:\root\path', from: r'C:\'), r'root\path'); + expect(context.relative(r'C:\', from: r'C:\'), '.'); + expect(context.relative(r'C:\root\path', from: r'C:\'), r'root\path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); + var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -463,7 +455,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'\foo\bar'); + var r = new path.Context(style: path.Style.windows, current: r'\foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -488,23 +480,25 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.windows, root: r'C:\dir.ext'); + var r = new path.Context( + style: path.Style.windows, current: r'C:\dir.ext'); expect(r.relative(r'C:\dir.ext\file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), + expect(context.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); - expect(builder.relative('..', from: r'C:\foo\bar'), + expect(context.relative('..', from: r'C:\foo\bar'), equals(r'..\..\root')); - expect(builder.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); - expect(builder.relative(r'C:\foo\bar\baz', from: r'foo\bar'), + expect(context.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); + expect(context.relative(r'C:\foo\bar\baz', from: r'foo\bar'), equals(r'..\..\..\..\foo\bar\baz')); - expect(builder.relative('..', from: r'foo\bar'), equals(r'..\..\..')); + expect(context.relative('..', from: r'foo\bar'), equals(r'..\..\..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'relative\root'); + var r = new path.Context( + style: path.Style.windows, current: r'relative\root'); expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); expect(() => r.relative('..', from: r'C:\foo\bar'), throwsPathException); expect(r.relative(r'C:\foo\bar\baz', from: r'foo\bar'), @@ -513,12 +507,12 @@ main() { }); test('given absolute with different root prefix', () { - expect(builder.relative(r'D:\a\b'), r'D:\a\b'); - expect(builder.relative(r'\\server\share\a\b'), r'\\server\share\a\b'); + expect(context.relative(r'D:\a\b'), r'D:\a\b'); + expect(context.relative(r'\\server\share\a\b'), r'\\server\share\a\b'); }); test('from a . root', () { - var r = new path.Builder(style: path.Style.windows, root: '.'); + var r = new path.Context(style: path.Style.windows, current: '.'); expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); expect(r.relative(r'\foo\bar\baz'), equals(r'\foo\bar\baz')); @@ -527,115 +521,115 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin(r'foo\bar', r'foo\bar'), isFalse); - expect(builder.isWithin(r'foo\bar', r'foo\bar\baz'), isTrue); - expect(builder.isWithin(r'foo\bar', r'foo\baz'), isFalse); - expect(builder.isWithin(r'foo\bar', r'..\path\foo\bar\baz'), isTrue); - expect(builder.isWithin(r'C:\', r'C:\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\', r'D:\foo\bar'), isFalse); - expect(builder.isWithin(r'C:\', r'\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\foo', r'\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\foo', r'\bar\baz'), isFalse); - expect(builder.isWithin(r'baz', r'C:\root\path\baz\bang'), isTrue); - expect(builder.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar\baz'), isTrue); + expect(context.isWithin(r'foo\bar', r'foo\baz'), isFalse); + expect(context.isWithin(r'foo\bar', r'..\path\foo\bar\baz'), isTrue); + expect(context.isWithin(r'C:\', r'C:\foo\bar'), isTrue); + expect(context.isWithin(r'C:\', r'D:\foo\bar'), isFalse); + expect(context.isWithin(r'C:\', r'\foo\bar'), isTrue); + expect(context.isWithin(r'C:\foo', r'\foo\bar'), isTrue); + expect(context.isWithin(r'C:\foo', r'\bar\baz'), isFalse); + expect(context.isWithin(r'baz', r'C:\root\path\baz\bang'), isTrue); + expect(context.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); - expect(builder.isWithin('.', r'a\b\c'), isTrue); - expect(builder.isWithin('.', r'..\a\b\c'), isFalse); - expect(builder.isWithin('.', r'..\..\a\foo\b\c'), isFalse); - expect(builder.isWithin(r'C:\', r'C:\baz\bang'), isTrue); - expect(builder.isWithin('.', r'C:\baz\bang'), isFalse); + var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + expect(context.isWithin('.', r'a\b\c'), isTrue); + expect(context.isWithin('.', r'..\a\b\c'), isFalse); + expect(context.isWithin('.', r'..\..\a\foo\b\c'), isFalse); + expect(context.isWithin(r'C:\', r'C:\baz\bang'), isTrue); + expect(context.isWithin('.', r'C:\baz\bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), r'C:\root\path\a'); - expect(builder.resolve('a', 'b'), r'C:\root\path\a\b'); - expect(builder.resolve('a', 'b', 'c'), r'C:\root\path\a\b\c'); - expect(builder.resolve('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + expect(context.absolute('a'), r'C:\root\path\a'); + expect(context.absolute('a', 'b'), r'C:\root\path\a\b'); + expect(context.absolute('a', 'b', 'c'), r'C:\root\path\a\b\c'); + expect(context.absolute('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); + expect(context.absolute('a', 'b', 'c', 'd', 'e'), r'C:\root\path\a\b\c\d\e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), r'C:\root\path\a\b\c\d\e\f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'C:\root\path\a\b\c\d\e\f\g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d'); - expect(builder.resolve('a/', 'b'), r'C:\root\path\a/b'); + expect(context.absolute(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d'); + expect(context.absolute('a/', 'b'), r'C:\root\path\a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\c\d'); - expect(builder.resolve('a', r'\b', r'\c', 'd'), r'C:\c\d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.resolve('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); + expect(context.absolute('a', '/b', '/c', 'd'), r'C:\c\d'); + expect(context.absolute('a', r'\b', r'\c', 'd'), r'C:\c\d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(context.absolute('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension(r'a\b.c'), r'a\b'); - expect(builder.withoutExtension(r'a\b.c.d'), r'a\b.c'); - expect(builder.withoutExtension(r'a\'), r'a\'); - expect(builder.withoutExtension(r'a\b\'), r'a\b\'); - expect(builder.withoutExtension(r'a\.'), r'a\.'); - expect(builder.withoutExtension(r'a\.b'), r'a\.b'); - expect(builder.withoutExtension(r'a.b\c'), r'a.b\c'); - expect(builder.withoutExtension(r'a/b.c/d'), r'a/b.c/d'); - expect(builder.withoutExtension(r'a\b/c'), r'a\b/c'); - expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c'); - expect(builder.withoutExtension(r'a.b/c'), r'a.b/c'); - expect(builder.withoutExtension(r'a\b.c\'), r'a\b\'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension(r'a\b.c'), r'a\b'); + expect(context.withoutExtension(r'a\b.c.d'), r'a\b.c'); + expect(context.withoutExtension(r'a\'), r'a\'); + expect(context.withoutExtension(r'a\b\'), r'a\b\'); + expect(context.withoutExtension(r'a\.'), r'a\.'); + expect(context.withoutExtension(r'a\.b'), r'a\.b'); + expect(context.withoutExtension(r'a.b\c'), r'a.b\c'); + expect(context.withoutExtension(r'a/b.c/d'), r'a/b.c/d'); + expect(context.withoutExtension(r'a\b/c'), r'a\b/c'); + expect(context.withoutExtension(r'a\b/c.d'), r'a\b/c'); + expect(context.withoutExtension(r'a.b/c'), r'a.b/c'); + expect(context.withoutExtension(r'a\b.c\'), r'a\b\'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo')), + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo')), + expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), r'\\server\share\path\to\foo'); - expect(builder.fromUri(Uri.parse('file:///C:/')), r'C:\'); - expect(builder.fromUri(Uri.parse('file://server/share')), + expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); + expect(context.fromUri(Uri.parse('file://server/share')), r'\\server\share'); - expect(builder.fromUri(Uri.parse('foo/bar')), r'foo\bar'); - expect(builder.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('//server/share/path/to/foo')), + expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); + expect(context.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('//server/share/path/to/foo')), r'\\server\share\path\to\foo'); - expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), r'C:\path\to\foo#bar'); - expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), r'\\server\share\path\to\foo#bar'); - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); - expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); test('toUri', () { - expect(builder.toUri(r'C:\path\to\foo'), + expect(context.toUri(r'C:\path\to\foo'), Uri.parse('file:///C:/path/to/foo')); - expect(builder.toUri(r'C:\path\to\foo\'), + expect(context.toUri(r'C:\path\to\foo\'), Uri.parse('file:///C:/path/to/foo/')); - expect(builder.toUri(r'C:\'), Uri.parse('file:///C:/')); - expect(builder.toUri(r'\\server\share'), Uri.parse('file://server/share')); - expect(builder.toUri(r'\\server\share\'), + expect(context.toUri(r'C:\'), Uri.parse('file:///C:/')); + expect(context.toUri(r'\\server\share'), Uri.parse('file://server/share')); + expect(context.toUri(r'\\server\share\'), Uri.parse('file://server/share/')); - expect(builder.toUri(r'foo\bar'), Uri.parse('foo/bar')); - expect(builder.toUri(r'C:\path\to\foo#bar'), + expect(context.toUri(r'foo\bar'), Uri.parse('foo/bar')); + expect(context.toUri(r'C:\path\to\foo#bar'), Uri.parse('file:///C:/path/to/foo%23bar')); - expect(builder.toUri(r'\\server\share\path\to\foo#bar'), + expect(context.toUri(r'\\server\share\path\to\foo#bar'), Uri.parse('file://server/share/path/to/foo%23bar')); - expect(builder.toUri(r'C:\_{_}_`_^_ _"_%_'), + expect(context.toUri(r'C:\_{_}_`_^_ _"_%_'), Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } From 3de97567206cacf277f3f4cd766b30303496537f Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 20 Nov 2013 21:02:03 +0000 Subject: [PATCH 038/183] Make the stack_trace package print relative URLs for browser paths. Prior to this, only console paths were printed as relative. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//75543013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30484 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/path.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 6f522c5b..7ff858ce 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -79,6 +79,11 @@ Context get _context { } Context _cachedContext; +/// Returns the [Style] of the current context. +/// +/// This is the style that all top-level path functions will use. +Style get style => _context.style; + /// Gets the path to the current working directory. /// /// In the browser, this means the current URL, without the last file segment. From 8ecf6f890ac9ccc5b8b12001e6a9f513af562c69 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 21 Nov 2013 22:48:52 +0000 Subject: [PATCH 039/183] Bump pkg/path's version to 1.0.0-rc.1 R=rnystrom@google.com Review URL: https://codereview.chromium.org//79883002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30552 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index aa905cff..a391d96b 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 0.9.0 +version: 1.0.0-rc.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -10,4 +10,4 @@ documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: - sdk: ">=0.8.10+6 <2.0.0" + sdk: ">=1.0.0+3.r30188 <2.0.0" From 25ddf165d77a84dd9ddf4c92eb8ef3e088d62232 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 26 Nov 2013 21:57:25 +0000 Subject: [PATCH 040/183] Mark path's SDK constraint lower bound as 1.0.0 exactly. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//81843003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@30679 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index a391d96b..491c3bca 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -10,4 +10,4 @@ documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: - sdk: ">=1.0.0+3.r30188 <2.0.0" + sdk: ">=1.0.0 <2.0.0" From 01398a34d80a86b51f1bd05cda6fcca2c77b98e6 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 10 Dec 2013 00:24:01 +0000 Subject: [PATCH 041/183] Make pkg/path 1.0.0 and upgrade dependencies appropriately. All existing packages that use pkg/path (other than pkg/stack_trace, which has already been updated) are compatible with both the pre-1.0 and post-1.0 path API, so I've marked their version constraints as ">=0.9.0 <2.0.0". I've also incremented their patch versions and I intend to release new versions as soon as this CL lands. R=alanknight@google.com, efortuna@google.com, jmesserly@google.com, rnystrom@google.com, scheglov@google.com Review URL: https://codereview.chromium.org//110873002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@31005 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 491c3bca..3b555a1c 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.0.0-rc.1 +version: 1.0.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 563950625caf0dceab9b5404616ddd51bc25e18c Mon Sep 17 00:00:00 2001 From: "kevmoo@google.com" Date: Mon, 13 Jan 2014 18:07:45 +0000 Subject: [PATCH 042/183] pkg/unittest: added LICENSE R=rnystrom@google.com Review URL: https://codereview.chromium.org//135343002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@31750 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/LICENSE | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 pkgs/path/LICENSE diff --git a/pkgs/path/LICENSE b/pkgs/path/LICENSE new file mode 100644 index 00000000..5c60afea --- /dev/null +++ b/pkgs/path/LICENSE @@ -0,0 +1,26 @@ +Copyright 2014, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 1c3e43a5248549d1ce264aa25acef8b8318c93b3 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 18 Mar 2014 22:02:03 +0000 Subject: [PATCH 043/183] Allow [path.fromUri] to take a string as well as a URI. R=rnystrom@google.com Review URL: https://codereview.chromium.org//203673003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@34067 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 3 ++ pkgs/path/lib/path.dart | 14 +++++---- pkgs/path/lib/src/context.dart | 17 +++++++---- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 33 ++++++++++++--------- pkgs/path/test/url_test.dart | 36 ++++++++++++++--------- pkgs/path/test/windows_test.dart | 50 ++++++++++++++++++-------------- 7 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 pkgs/path/CHANGELOG.md diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md new file mode 100644 index 00000000..0d48c070 --- /dev/null +++ b/pkgs/path/CHANGELOG.md @@ -0,0 +1,3 @@ +# 1.1.0 + +* `path.fromUri` now accepts strings as well as `Uri` objects. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 7ff858ce..524f16dc 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -322,23 +322,27 @@ bool isWithin(String parent, String child) => _context.isWithin(parent, child); /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) => _context.withoutExtension(path); -/// Returns the path represented by [uri]. +/// Returns the path represented by [uri], which may be a [String] or a [Uri]. /// /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL /// style, this will just convert [uri] to a string. /// /// // POSIX -/// path.fromUri(Uri.parse('file:///path/to/foo')) +/// context.fromUri('file:///path/to/foo') /// // -> '/path/to/foo' /// /// // Windows -/// path.fromUri(Uri.parse('file:///C:/path/to/foo')) +/// context.fromUri('file:///C:/path/to/foo') /// // -> r'C:\path\to\foo' /// /// // URL -/// path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) +/// context.fromUri('http://dartlang.org/path/to/foo') /// // -> 'http://dartlang.org/path/to/foo' -String fromUri(Uri uri) => _context.fromUri(uri); +/// +/// If [uri] is relative, a relative path will be returned. +/// +/// path.fromUri('path/to/foo'); // -> 'path/to/foo' +String fromUri(uri) => _context.fromUri(uri); /// Returns the URI that represents [path]. /// diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index b897c8ba..9c8e1e46 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -423,23 +423,30 @@ class Context { return parsed.toString(); } - /// Returns the path represented by [uri]. + /// Returns the path represented by [uri], which may be a [String] or a [Uri]. /// /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL /// style, this will just convert [uri] to a string. /// /// // POSIX - /// context.fromUri(Uri.parse('file:///path/to/foo')) + /// context.fromUri('file:///path/to/foo') /// // -> '/path/to/foo' /// /// // Windows - /// context.fromUri(Uri.parse('file:///C:/path/to/foo')) + /// context.fromUri('file:///C:/path/to/foo') /// // -> r'C:\path\to\foo' /// /// // URL - /// context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) + /// context.fromUri('http://dartlang.org/path/to/foo') /// // -> 'http://dartlang.org/path/to/foo' - String fromUri(Uri uri) => style.pathFromUri(uri); + /// + /// If [uri] is relative, a relative path will be returned. + /// + /// path.fromUri('path/to/foo'); // -> 'path/to/foo' + String fromUri(uri) { + if (uri is String) uri = Uri.parse(uri); + return style.pathFromUri(uri); + } /// Returns the URI that represents [path]. /// diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 3b555a1c..8095c889 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.0.0 +version: 1.1.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 83a473e7..9da97c96 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -474,19 +474,26 @@ main() { expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); - test('fromUri', () { - expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); - expect(context.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); - expect(context.fromUri(Uri.parse('file:///')), '/'); - expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(context.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); - expect(context.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); - expect(context.fromUri(Uri.parse('file:///path/to/foo%23bar')), - '/path/to/foo#bar'); - expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), - r'_{_}_`_^_ _"_%_'); - expect(() => context.fromUri(Uri.parse('http://dartlang.org')), - throwsArgumentError); + group('fromUri', () { + test('with a URI', () { + expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo/')), + '/path/to/foo/'); + expect(context.fromUri(Uri.parse('file:///')), '/'); + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo%23bar')), + '/path/to/foo#bar'); + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_{_}_`_^_ _"_%_'); + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), + throwsArgumentError); + }); + + test('with a string', () { + expect(context.fromUri('file:///path/to/foo'), '/path/to/foo'); + }); }); test('toUri', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index e149b4f0..58c7b929 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -700,20 +700,28 @@ main() { expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); - test('fromUri', () { - expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), - 'http://dartlang.org/path/to/foo'); - expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), - 'http://dartlang.org/path/to/foo/'); - expect(context.fromUri(Uri.parse('file:///path/to/foo')), - 'file:///path/to/foo'); - expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), - 'http://dartlang.org/path/to/foo%23bar'); - // Since the resulting "path" is also a URL, special characters should - // remain percent-encoded in the result. - expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), - r'_%7B_%7D_%60_%5E_%20_%22_%25_'); + group('fromUri', () { + test('with a URI', () { + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), + 'http://dartlang.org/path/to/foo'); + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), + 'http://dartlang.org/path/to/foo/'); + expect(context.fromUri(Uri.parse('file:///path/to/foo')), + 'file:///path/to/foo'); + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri( + Uri.parse('http://dartlang.org/path/to/foo%23bar')), + 'http://dartlang.org/path/to/foo%23bar'); + // Since the resulting "path" is also a URL, special characters should + // remain percent-encoded in the result. + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_%7B_%7D_%60_%5E_%20_%22_%25_'); + }); + + test('with a string', () { + expect(context.fromUri('http://dartlang.org/path/to/foo'), + 'http://dartlang.org/path/to/foo'); + }); }); test('toUri', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index e5dcac49..d1946635 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -590,27 +590,35 @@ main() { expect(context.withoutExtension(r'a\b.c\'), r'a\b\'); }); - test('fromUri', () { - expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), - r'C:\path\to\foo'); - expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), - r'\\server\share\path\to\foo'); - expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); - expect(context.fromUri(Uri.parse('file://server/share')), - r'\\server\share'); - expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); - expect(context.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); - expect(context.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); - expect(context.fromUri(Uri.parse('//server/share/path/to/foo')), - r'\\server\share\path\to\foo'); - expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), - r'C:\path\to\foo#bar'); - expect(context.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), - r'\\server\share\path\to\foo#bar'); - expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), - r'_{_}_`_^_ _"_%_'); - expect(() => context.fromUri(Uri.parse('http://dartlang.org')), - throwsArgumentError); + group('fromUri', () { + test('with a URI', () { + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), + r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), + r'\\server\share\path\to\foo'); + expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); + expect(context.fromUri(Uri.parse('file://server/share')), + r'\\server\share'); + expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); + expect(context.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('///C:/path/to/foo')), + r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('//server/share/path/to/foo')), + r'\\server\share\path\to\foo'); + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), + r'C:\path\to\foo#bar'); + expect(context.fromUri( + Uri.parse('file://server/share/path/to/foo%23bar')), + r'\\server\share\path\to\foo#bar'); + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + r'_{_}_`_^_ _"_%_'); + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), + throwsArgumentError); + }); + + test('with a string', () { + expect(context.fromUri('file:///C:/path/to/foo'), r'C:\path\to\foo'); + }); }); test('toUri', () { From 3e9bd65b01eb1b7482d1b5abbfae4e5836c47c18 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Fri, 23 May 2014 23:04:39 +0000 Subject: [PATCH 044/183] Add a path.formatUri method and release path 1.2.0. R=rnystrom@google.com Review URL: https://codereview.chromium.org//296233002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@36597 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 5 ++++ pkgs/path/lib/path.dart | 23 +++++++++++++++++ pkgs/path/lib/src/context.dart | 42 ++++++++++++++++++++++++++++++++ pkgs/path/pubspec.yaml | 4 +-- pkgs/path/test/posix_test.dart | 26 ++++++++++++++++++++ pkgs/path/test/url_test.dart | 30 +++++++++++++++++++++++ pkgs/path/test/windows_test.dart | 30 +++++++++++++++++++++++ 7 files changed, 158 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 0d48c070..22fc34f6 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.2.0 + +* Added `path.prettyUri`, which produces a human-readable representation of a + URI. + # 1.1.0 * `path.fromUri` now accepts strings as well as `Uri` objects. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 524f16dc..599a3515 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -366,3 +366,26 @@ String fromUri(uri) => _context.fromUri(uri); /// path.toUri('path/to/foo') /// // -> Uri.parse('path/to/foo') Uri toUri(String path) => _context.toUri(path); + +/// Returns a terse, human-readable representation of [uri]. +/// +/// [uri] can be a [String] or a [Uri]. If it can be made relative to the +/// current working directory, that's done. Otherwise, it's returned as-is. This +/// gracefully handles non-`file:` URIs for [Style.posix] and [Style.windows]. +/// +/// The returned value is meant for human consumption, and may be either URI- +/// or path-formatted. +/// +/// // POSIX at "/root/path" +/// path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // Windows at "C:\root\path" +/// path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' +/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// +/// // URL at "http://dartlang.org/root/path" +/// path.prettyUri('http://dartlang.org/root/path/a/b.dart'); +/// // -> r'a/b.dart' +/// path.prettyUri('file:///root/path'); // -> 'file:///root/path' +String prettyUri(uri) => _context.prettyUri(uri); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 9c8e1e46..88ca61fa 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -472,6 +472,48 @@ class Context { } } + /// Returns a terse, human-readable representation of [uri]. + /// + /// [uri] can be a [String] or a [Uri]. If it can be made relative to the + /// current working directory, that's done. Otherwise, it's returned as-is. + /// This gracefully handles non-`file:` URIs for [Style.posix] and + /// [Style.windows]. + /// + /// The returned value is meant for human consumption, and may be either URI- + /// or path-formatted. + /// + /// // POSIX + /// var context = new Context(current: '/root/path'); + /// context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // Windows + /// var context = new Context(current: r'C:\root\path'); + /// context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' + /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// + /// // URL + /// var context = new Context(current: 'http://dartlang.org/root/path'); + /// context.prettyUri('http://dartlang.org/root/path/a/b.dart'); + /// // -> r'a/b.dart' + /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' + String prettyUri(uri) { + if (uri is String) uri = Uri.parse(uri); + if (uri.scheme == 'file' && style == Style.url) return uri.toString(); + if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) { + return uri.toString(); + } + + var path = normalize(fromUri(uri)); + var rel = relative(path); + var components = split(rel); + + // Only return a relative path if it's actually shorter than the absolute + // path. This avoids ugly things like long "../" chains to get to the root + // and then go back down. + return split(rel).length > split(path).length ? path : rel; + } + ParsedPath _parse(String path) => new ParsedPath.parse(path, style); } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 8095c889..f21ac50e 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.1.0 +version: 1.2.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -8,6 +8,6 @@ description: > homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: - unittest: ">=0.9.0 <0.10.0" + unittest: ">=0.9.0 <0.12.0" environment: sdk: ">=1.0.0 <2.0.0" diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 9da97c96..5b6d1315 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -508,4 +508,30 @@ main() { expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); + + group('prettyUri', () { + test('with a file: URI', () { + expect(context.prettyUri('file:///root/path/a/b'), 'a/b'); + expect(context.prettyUri('file:///root/path/a/../b'), 'b'); + expect(context.prettyUri('file:///other/path/a/b'), '/other/path/a/b'); + expect(context.prettyUri('file:///root/other'), '../other'); + }); + + test('with an http: URI', () { + expect(context.prettyUri('http://dartlang.org/a/b'), + 'http://dartlang.org/a/b'); + }); + + test('with a relative URI', () { + expect(context.prettyUri('a/b'), 'a/b'); + }); + + test('with a root-relative URI', () { + expect(context.prettyUri('/a/b'), '/a/b'); + }); + + test('with a Uri object', () { + expect(context.prettyUri(Uri.parse('a/b')), 'a/b'); + }); + }); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 58c7b929..27691d85 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -741,4 +741,34 @@ main() { expect(context.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); + + group('prettyUri', () { + test('with a file: URI', () { + expect(context.prettyUri(Uri.parse('file:///root/path/a/b')), + 'file:///root/path/a/b'); + }); + + test('with an http: URI', () { + expect(context.prettyUri('http://dartlang.org/root/path/a/b'), 'a/b'); + expect(context.prettyUri('http://dartlang.org/root/path/a/../b'), 'b'); + expect(context.prettyUri('http://dartlang.org/other/path/a/b'), + 'http://dartlang.org/other/path/a/b'); + expect(context.prettyUri('http://pub.dartlang.org/root/path'), + 'http://pub.dartlang.org/root/path'); + expect(context.prettyUri('http://dartlang.org/root/other'), + '../other'); + }); + + test('with a relative URI', () { + expect(context.prettyUri('a/b'), 'a/b'); + }); + + test('with a root-relative URI', () { + expect(context.prettyUri('/a/b'), '/a/b'); + }); + + test('with a Uri object', () { + expect(context.prettyUri(Uri.parse('a/b')), 'a/b'); + }); + }); } diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index d1946635..7c16e319 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -640,4 +640,34 @@ main() { expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); + + group('prettyUri', () { + test('with a file: URI', () { + expect(context.prettyUri('file:///C:/root/path/a/b'), r'a\b'); + expect(context.prettyUri('file:///C:/root/path/a/../b'), r'b'); + expect(context.prettyUri('file:///C:/other/path/a/b'), + r'C:\other\path\a\b'); + expect(context.prettyUri('file:///D:/root/path/a/b'), + r'D:\root\path\a\b'); + expect(context.prettyUri('file:///C:/root/other'), + r'..\other'); + }); + + test('with an http: URI', () { + expect(context.prettyUri('http://dartlang.org/a/b'), + 'http://dartlang.org/a/b'); + }); + + test('with a relative URI', () { + expect(context.prettyUri('a/b'), r'a\b'); + }); + + test('with a root-relative URI', () { + expect(context.prettyUri('/D:/a/b'), r'D:\a\b'); + }); + + test('with a Uri object', () { + expect(context.prettyUri(Uri.parse('a/b')), r'a\b'); + }); + }); } From 26c2bebaa555dc0bc7ab4387605dc1e5ac9ff27e Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Mon, 2 Jun 2014 19:06:22 +0000 Subject: [PATCH 045/183] Deprecate publicly-visible properties on Style in path. R=rnystrom@google.com Review URL: https://codereview.chromium.org//304843002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@36893 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 5 ++ pkgs/path/lib/src/context.dart | 10 +++- pkgs/path/lib/src/internal_style.dart | 75 +++++++++++++++++++++++++++ pkgs/path/lib/src/parsed_path.dart | 8 +-- pkgs/path/lib/src/style.dart | 64 ++++++----------------- pkgs/path/lib/src/style/posix.dart | 4 +- pkgs/path/lib/src/style/url.dart | 4 +- pkgs/path/lib/src/style/windows.dart | 4 +- pkgs/path/pubspec.yaml | 2 +- 9 files changed, 116 insertions(+), 60 deletions(-) create mode 100644 pkgs/path/lib/src/internal_style.dart diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 22fc34f6..9a6eccfc 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.2.1 + +* Many members on `Style` that provided access to patterns and functions used + internally for parsing paths have been deprecated. + # 1.2.0 * Added `path.prettyUri`, which produces a human-readable representation of a diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 88ca61fa..f3826d78 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -4,6 +4,7 @@ library path.context; +import 'internal_style.dart'; import 'style.dart'; import 'parsed_path.dart'; import 'path_exception.dart'; @@ -30,7 +31,12 @@ class Context { } } - if (style == null) style = Style.platform; + if (style == null) { + style = Style.platform; + } else if (style is! InternalStyle) { + throw new ArgumentError("Only styles defined by the path package are " + "allowed."); + } return new Context._(style, current); } @@ -38,7 +44,7 @@ class Context { Context._(this.style, this.current); /// The style of path that this context works with. - final Style style; + final InternalStyle style; /// The current directory that relative paths will be relative to. final String current; diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart new file mode 100644 index 00000000..13fb57e1 --- /dev/null +++ b/pkgs/path/lib/src/internal_style.dart @@ -0,0 +1,75 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.internal_style; + +import 'context.dart'; +import 'style.dart'; + +/// The internal interface for the [Style] type. +/// +/// Users should be able to pass around instances of [Style] like an enum, but +/// the members that [Context] uses should be hidden from them. Those members +/// are defined on this class instead. +abstract class InternalStyle extends Style { + /// The path separator for this style. On POSIX, this is `/`. On Windows, + /// it's `\`. + String get separator; + + /// The [Pattern] that can be used to match a separator for a path in this + /// style. Windows allows both "/" and "\" as path separators even though "\" + /// is the canonical one. + Pattern get separatorPattern; + + /// The [Pattern] that matches path components that need a separator after + /// them. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + Pattern get needsSeparatorPattern; + + /// The [Pattern] that can be used to match the root prefix of an absolute + /// path in this style. + Pattern get rootPattern; + + /// The [Pattern] that can be used to match the root prefix of a root-relative + /// path in this style. + /// + /// This can be null to indicate that this style doesn't support root-relative + /// paths. + final Pattern relativeRootPattern = null; + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path) { + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = rootPattern.allMatches(path); + if (matches.isNotEmpty) return matches.first[0]; + return getRelativeRoot(path); + } + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path) { + if (relativeRootPattern == null) return null; + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = relativeRootPattern.allMatches(path); + if (matches.isEmpty) return null; + return matches.first[0]; + } + + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: context.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); +} diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 5356b44c..3f3c3b9c 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -4,12 +4,12 @@ library path.parsed_path; +import 'internal_style.dart'; import 'style.dart'; -// TODO(rnystrom): Make this public? class ParsedPath { - /// The [Style] that was used to parse this path. - Style style; + /// The [InternalStyle] that was used to parse this path. + InternalStyle style; /// The absolute root portion of the path, or `null` if the path is relative. /// On POSIX systems, this will be `null` or "/". On Windows, it can be @@ -40,7 +40,7 @@ class ParsedPath { /// `true` if this is an absolute path. bool get isAbsolute => root != null; - factory ParsedPath.parse(String path, Style style) { + factory ParsedPath.parse(String path, InternalStyle style) { var before = path; // Remove the root prefix, if any. diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 9da4b43a..a94ec68c 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -51,67 +51,37 @@ abstract class Style { /// The name of this path style. Will be "posix" or "windows". String get name; - /// The path separator for this style. On POSIX, this is `/`. On Windows, - /// it's `\`. + /// A [Context] that uses this style. + Context get context => new Context(style: this); + + @Deprecated("Most Style members will be removed in path 2.0.") String get separator; - /// The [Pattern] that can be used to match a separator for a path in this - /// style. Windows allows both "/" and "\" as path separators even though "\" - /// is the canonical one. + @Deprecated("Most Style members will be removed in path 2.0.") Pattern get separatorPattern; - /// The [Pattern] that matches path components that need a separator after - /// them. - /// - /// Windows and POSIX styles just need separators when the previous component - /// doesn't already end in a separator, but the URL always needs to place a - /// separator between the root and the first component, even if the root - /// already ends in a separator character. For example, to join "file://" and - /// "usr", an additional "/" is needed (making "file:///usr"). + @Deprecated("Most Style members will be removed in path 2.0.") Pattern get needsSeparatorPattern; - /// The [Pattern] that can be used to match the root prefix of an absolute - /// path in this style. + @Deprecated("Most Style members will be removed in path 2.0.") Pattern get rootPattern; - /// The [Pattern] that can be used to match the root prefix of a root-relative - /// path in this style. - /// - /// This can be null to indicate that this style doesn't support root-relative - /// paths. - final Pattern relativeRootPattern = null; + @Deprecated("Most Style members will be removed in path 2.0.") + Pattern get relativeRootPattern; - /// A [Context] that uses this style. - Context get context => new Context(style: this); - - /// Gets the root prefix of [path] if path is absolute. If [path] is relative, - /// returns `null`. - String getRoot(String path) { - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = rootPattern.allMatches(path); - if (matches.isNotEmpty) return matches.first[0]; - return getRelativeRoot(path); - } + @Deprecated("Most style members will be removed in path 2.0.") + String getRoot(String path); - /// Gets the root prefix of [path] if it's root-relative. - /// - /// If [path] is relative or absolute and not root-relative, returns `null`. - String getRelativeRoot(String path) { - if (relativeRootPattern == null) return null; - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = relativeRootPattern.allMatches(path); - if (matches.isEmpty) return null; - return matches.first[0]; - } + @Deprecated("Most style members will be removed in path 2.0.") + String getRelativeRoot(String path); - /// Returns the path represented by [uri] in this style. + @Deprecated("Most style members will be removed in path 2.0.") String pathFromUri(Uri uri); - /// Returns the URI that represents the relative path made of [parts]. - Uri relativePathToUri(String path) => - new Uri(pathSegments: context.split(path)); + @Deprecated("Most style members will be removed in path 2.0.") + Uri relativePathToUri(String path); - /// Returns the URI that represents [path], which is assumed to be absolute. + @Deprecated("Most style members will be removed in path 2.0.") Uri absolutePathToUri(String path); String toString() => name; diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 72e70441..e0b60174 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -5,10 +5,10 @@ library path.style.posix; import '../parsed_path.dart'; -import '../style.dart'; +import '../internal_style.dart'; /// The style for POSIX paths. -class PosixStyle extends Style { +class PosixStyle extends InternalStyle { PosixStyle(); final name = 'posix'; diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 4a7003de..1e84917d 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -4,10 +4,10 @@ library path.style.url; -import '../style.dart'; +import '../internal_style.dart'; /// The style for URL paths. -class UrlStyle extends Style { +class UrlStyle extends InternalStyle { UrlStyle(); final name = 'url'; diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 1750578e..be9f45fd 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -5,10 +5,10 @@ library path.style.windows; import '../parsed_path.dart'; -import '../style.dart'; +import '../internal_style.dart'; /// The style for Windows paths. -class WindowsStyle extends Style { +class WindowsStyle extends InternalStyle { WindowsStyle(); final name = 'windows'; diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index f21ac50e..86e7d951 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.2.0 +version: 1.2.1-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 1c329bac419da0ed1d57ef4b9f132cfff07f7818 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Mon, 2 Jun 2014 23:57:09 +0000 Subject: [PATCH 046/183] Avoid using RegExps in path to work around issue 19090. R=rnystrom@google.com Review URL: https://codereview.chromium.org//309803003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@36909 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 3 ++ pkgs/path/lib/src/characters.dart | 19 ++++++++ pkgs/path/lib/src/context.dart | 6 +-- pkgs/path/lib/src/internal_style.dart | 45 +++++------------- pkgs/path/lib/src/parsed_path.dart | 23 ++++----- pkgs/path/lib/src/style/posix.dart | 20 ++++++++ pkgs/path/lib/src/style/url.dart | 63 +++++++++++++++++++++++++ pkgs/path/lib/src/style/windows.dart | 68 ++++++++++++++++++++++++++- pkgs/path/lib/src/utils.dart | 16 +++++++ pkgs/path/pubspec.yaml | 2 +- 10 files changed, 215 insertions(+), 50 deletions(-) create mode 100644 pkgs/path/lib/src/characters.dart create mode 100644 pkgs/path/lib/src/utils.dart diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 9a6eccfc..f1d24858 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -3,6 +3,9 @@ * Many members on `Style` that provided access to patterns and functions used internally for parsing paths have been deprecated. +* Manually parse paths (rather than using RegExps to do so) for better + performance. + # 1.2.0 * Added `path.prettyUri`, which produces a human-readable representation of a diff --git a/pkgs/path/lib/src/characters.dart b/pkgs/path/lib/src/characters.dart new file mode 100644 index 00000000..ff196a67 --- /dev/null +++ b/pkgs/path/lib/src/characters.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/// This library contains character-code definitions. +library path.characters; + +const PLUS = 0x2b; +const MINUS = 0x2d; +const PERIOD = 0x2e; +const SLASH = 0x2f; +const ZERO = 0x30; +const NINE = 0x39; +const COLON = 0x3a; +const UPPER_A = 0x41; +const UPPER_Z = 0x5a; +const LOWER_A = 0x61; +const LOWER_Z = 0x7a; +const BACKSLASH = 0x5c; diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index f3826d78..823e0c28 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -219,7 +219,7 @@ class Context { // replaces the path after it. var parsed = _parse(part); parsed.root = this.rootPrefix(buffer.toString()); - if (parsed.root.contains(style.needsSeparatorPattern)) { + if (style.needsSeparator(parsed.root)) { parsed.separators[0] = style.separator; } buffer.clear(); @@ -230,7 +230,7 @@ class Context { buffer.clear(); buffer.write(part); } else { - if (part.length > 0 && part[0].contains(style.separatorPattern)) { + if (part.length > 0 && style.containsSeparator(part[0])) { // The part starts with a separator, so we don't need to add one. } else if (needsSeparator) { buffer.write(separator); @@ -241,7 +241,7 @@ class Context { // Unless this part ends with a separator, we'll need to add one before // the next part. - needsSeparator = part.contains(style.needsSeparatorPattern); + needsSeparator = style.needsSeparator(part); } return buffer.toString(); diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 13fb57e1..67b5d343 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -13,55 +13,34 @@ import 'style.dart'; /// the members that [Context] uses should be hidden from them. Those members /// are defined on this class instead. abstract class InternalStyle extends Style { - /// The path separator for this style. On POSIX, this is `/`. On Windows, - /// it's `\`. + /// The default path separator for this style. + /// + /// On POSIX, this is `/`. On Windows, it's `\`. String get separator; - /// The [Pattern] that can be used to match a separator for a path in this - /// style. Windows allows both "/" and "\" as path separators even though "\" - /// is the canonical one. - Pattern get separatorPattern; + /// Returns whether [path] contains a separator. + bool containsSeparator(String path); + + /// Returns whether [codeUnit] is the character code of a separator. + bool isSeparator(int codeUnit); - /// The [Pattern] that matches path components that need a separator after - /// them. + /// Returns whether this path component needs a separator after it. /// /// Windows and POSIX styles just need separators when the previous component /// doesn't already end in a separator, but the URL always needs to place a /// separator between the root and the first component, even if the root /// already ends in a separator character. For example, to join "file://" and /// "usr", an additional "/" is needed (making "file:///usr"). - Pattern get needsSeparatorPattern; - - /// The [Pattern] that can be used to match the root prefix of an absolute - /// path in this style. - Pattern get rootPattern; - - /// The [Pattern] that can be used to match the root prefix of a root-relative - /// path in this style. - /// - /// This can be null to indicate that this style doesn't support root-relative - /// paths. - final Pattern relativeRootPattern = null; + bool needsSeparator(String path); /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. - String getRoot(String path) { - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = rootPattern.allMatches(path); - if (matches.isNotEmpty) return matches.first[0]; - return getRelativeRoot(path); - } + String getRoot(String path); /// Gets the root prefix of [path] if it's root-relative. /// /// If [path] is relative or absolute and not root-relative, returns `null`. - String getRelativeRoot(String path) { - if (relativeRootPattern == null) return null; - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = relativeRootPattern.allMatches(path); - if (matches.isEmpty) return null; - return matches.first[0]; - } + String getRelativeRoot(String path); /// Returns the path represented by [uri] in this style. String pathFromUri(Uri uri); diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 3f3c3b9c..57773eee 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -52,19 +52,21 @@ class ParsedPath { var parts = []; var separators = []; - var firstSeparator = style.separatorPattern.matchAsPrefix(path); - if (firstSeparator != null) { - separators.add(firstSeparator[0]); - path = path.substring(firstSeparator[0].length); + var start = 0; + + if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) { + separators.add(path[0]); + start = 1; } else { separators.add(''); } - var start = 0; - for (var match in style.separatorPattern.allMatches(path)) { - parts.add(path.substring(start, match.start)); - separators.add(match[0]); - start = match.end; + for (var i = start; i < path.length; i++) { + if (style.isSeparator(path.codeUnitAt(i))) { + parts.add(path.substring(start, i)); + separators.add(path[i]); + start = i + 1; + } } // Add the final part, if any. @@ -133,8 +135,7 @@ class ParsedPath { var newSeparators = new List.generate( newParts.length, (_) => style.separator, growable: true); newSeparators.insert(0, - isAbsolute && newParts.length > 0 && - root.contains(style.needsSeparatorPattern) ? + isAbsolute && newParts.length > 0 && style.needsSeparator(root) ? style.separator : ''); parts = newParts; diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index e0b60174..b8b82b40 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -4,6 +4,7 @@ library path.style.posix; +import '../characters.dart' as chars; import '../parsed_path.dart'; import '../internal_style.dart'; @@ -13,9 +14,28 @@ class PosixStyle extends InternalStyle { final name = 'posix'; final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + final separatorPattern = new RegExp(r'/'); final needsSeparatorPattern = new RegExp(r'[^/]$'); final rootPattern = new RegExp(r'^/'); + final relativeRootPattern = null; + + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) => + path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); + + String getRoot(String path) { + if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return '/'; + return null; + } + + String getRelativeRoot(String path) => null; String pathFromUri(Uri uri) { if (uri.scheme == '' || uri.scheme == 'file') { diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 1e84917d..f383923d 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -4,7 +4,9 @@ library path.style.url; +import '../characters.dart' as chars; import '../internal_style.dart'; +import '../utils.dart'; /// The style for URL paths. class UrlStyle extends InternalStyle { @@ -12,14 +14,75 @@ class UrlStyle extends InternalStyle { final name = 'url'; final separator = '/'; + final separators = const ['/']; + + // Deprecated properties. + final separatorPattern = new RegExp(r'/'); final needsSeparatorPattern = new RegExp( r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); final relativeRootPattern = new RegExp(r"^/"); + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + + // A URL that doesn't end in "/" always needs a separator. + if (!isSeparator(path.codeUnitAt(path.length - 1))) return true; + + // A URI that's just "scheme://" needs an extra separator, despite ending + // with "/". + var root = _getRoot(path); + return root != null && root.endsWith('://'); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + return isSeparator(path.codeUnitAt(0)) ? "/" : null; + } + String pathFromUri(Uri uri) => uri.toString(); Uri relativePathToUri(String path) => Uri.parse(path); Uri absolutePathToUri(String path) => Uri.parse(path); + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.isEmpty) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*". + + if (!isAlphabetic(path.codeUnitAt(0))) return null; + var start = 1; + for (; start < path.length; start++) { + var char = path.codeUnitAt(start); + if (isAlphabetic(char)) continue; + if (isNumeric(char)) continue; + if (char == chars.MINUS || char == chars.PLUS || char == chars.PERIOD) { + continue; + } + + break; + } + + if (start + 3 > path.length) return null; + if (path.substring(start, start + 3) != '://') return null; + start += 3; + + // A URL root can end with a non-"/" prefix. + while (start < path.length && !isSeparator(path.codeUnitAt(start))) { + start++; + } + return path.substring(0, start); + } } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index be9f45fd..2965f1ee 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -4,8 +4,10 @@ library path.style.windows; -import '../parsed_path.dart'; +import '../characters.dart' as chars; import '../internal_style.dart'; +import '../parsed_path.dart'; +import '../utils.dart'; /// The style for Windows paths. class WindowsStyle extends InternalStyle { @@ -13,11 +15,37 @@ class WindowsStyle extends InternalStyle { final name = 'windows'; final separator = '\\'; + final separators = const ['/', '\\']; + + // Deprecated properties. + final separatorPattern = new RegExp(r'[/\\]'); final needsSeparatorPattern = new RegExp(r'[^/\\]$'); final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + bool containsSeparator(String path) => path.contains('/'); + + bool isSeparator(int codeUnit) => + codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; + + bool needsSeparator(String path) { + if (path.isEmpty) return false; + return !isSeparator(path.codeUnitAt(path.length - 1)); + } + + String getRoot(String path) { + var root = _getRoot(path); + return root == null ? getRelativeRoot(path) : root; + } + + String getRelativeRoot(String path) { + if (path.isEmpty) return null; + if (!isSeparator(path.codeUnitAt(0))) return null; + if (path.length > 1 && isSeparator(path.codeUnitAt(1))) return null; + return path[0]; + } + String pathFromUri(Uri uri) { if (uri.scheme != '' && uri.scheme != 'file') { throw new ArgumentError("Uri $uri must have scheme 'file:'."); @@ -66,9 +94,45 @@ class WindowsStyle extends InternalStyle { // Get rid of the trailing "\" in "C:\" because the URI constructor will // add a separator on its own. - parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); + parsed.parts.insert(0, + parsed.root.replaceAll("/", "").replaceAll("\\", "")); return new Uri(scheme: 'file', pathSegments: parsed.parts); } } + + // A helper method for [getRoot] that doesn't handle relative roots. + String _getRoot(String path) { + if (path.length < 3) return null; + + // We aren't using a RegExp for this because they're slow (issue 19090). If + // we could, we'd match against r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'. + + // Try roots like "C:\". + if (isAlphabetic(path.codeUnitAt(0))) { + if (path.codeUnitAt(1) != chars.COLON) return null; + if (!isSeparator(path.codeUnitAt(2))) return null; + return path.substring(0, 3); + } + + // Try roots like "\\server\share". + if (!path.startsWith('\\\\')) return null; + + var start = 2; + // The server is one or more non-"\" characters. + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + if (start == 2 || start == path.length) return null; + + // The share is one or more non-"\" characters. + start += 1; + if (path.codeUnitAt(start) == chars.BACKSLASH) return null; + start += 1; + while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { + start++; + } + + return path.substring(0, start); + } } \ No newline at end of file diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart new file mode 100644 index 00000000..0636261c --- /dev/null +++ b/pkgs/path/lib/src/utils.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library path.utils; + +import 'characters.dart' as chars; + +/// Returns whether [char] is the code for an ASCII letter (uppercase or +/// lowercase). +bool isAlphabetic(int char) => + (char >= chars.UPPER_A && char <= chars.UPPER_Z) || + (char >= chars.LOWER_A && char <= chars.LOWER_Z); + +/// Returns whether [char] is the code for an ASCII digit. +bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 86e7d951..7bc83bff 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.2.1-dev +version: 1.2.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 7254b5b02a09e7f33fbf8a761d1418ab81d4a1f9 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 24 Jul 2014 20:23:52 +0000 Subject: [PATCH 047/183] Remove documentation links for repo packages. pub.dartlang.org will now automatically link to dartdocs.org for packages with no documentation link. BUG= http://dartbug.com/20177, http://dartbug.com/20178, http://dartbug.com/20186 R=sigmund@google.com Review URL: https://codereview.chromium.org//416973003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38551 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/pubspec.yaml | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index f1d24858..53316354 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.2.2 + +* Remove the documentation link from the pubspec so this is linked to + pub.dartlang.org by default. + # 1.2.1 * Many members on `Style` that provided access to patterns and functions used diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 7bc83bff..e9948de0 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,12 +1,11 @@ name: path -version: 1.2.1 +version: 1.2.2 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know and love, with solid support on both Windows and POSIX (Linux and Mac OS X) machines. homepage: http://www.dartlang.org -documentation: http://api.dartlang.org/docs/pkg/path dev_dependencies: unittest: ">=0.9.0 <0.12.0" environment: From e16450abd2a3ef6f7eaf0a622a1e52ca9783fac7 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Mon, 4 Aug 2014 06:40:18 +0000 Subject: [PATCH 048/183] Don't cache path Context based on cwd, as cwd involves a system-call to compute. Instead, delay evaluation of cwd to the actual use of it. BUG= R=rnystrom@google.com Review URL: https://codereview.chromium.org//429173002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38844 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/lib/path.dart | 17 ++--------------- pkgs/path/lib/src/context.dart | 15 ++++++++++++--- pkgs/path/pubspec.yaml | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 53316354..9b5520b3 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.2.3 + +* Don't cache path Context based on cwd, as cwd involves a system-call to + compute. + ## 1.2.2 * Remove the documentation link from the pubspec so this is linked to diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 599a3515..b9a4175b 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -49,7 +49,7 @@ library path; import 'src/context.dart'; import 'src/style.dart'; -export 'src/context.dart'; +export 'src/context.dart' hide createInternal; export 'src/path_exception.dart'; export 'src/style.dart'; @@ -62,22 +62,9 @@ final windows = new Context(style: Style.windows); /// A default context for manipulating URLs. final url = new Context(style: Style.url); -/// The result of [Uri.base] last time the current working directory was -/// calculated. -/// -/// This is used to invalidate [_cachedContext] when the working directory has -/// changed since the last time a function was called. -Uri _lastBaseUri; - /// An internal context for the current OS so we can provide a straight /// functional interface and not require users to create one. -Context get _context { - if (_cachedContext != null && Uri.base == _lastBaseUri) return _cachedContext; - _lastBaseUri = Uri.base; - _cachedContext = new Context(); - return _cachedContext; -} -Context _cachedContext; +final Context _context = createInternal(); /// Returns the [Style] of the current context. /// diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 823e0c28..89010491 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -10,6 +10,8 @@ import 'parsed_path.dart'; import 'path_exception.dart'; import '../path.dart' as p; +Context createInternal() => new Context._internal(); + /// An instantiable class for manipulating paths. Unlike the top-level /// functions, this lets you explicitly select what platform the paths will use. class Context { @@ -41,13 +43,20 @@ class Context { return new Context._(style, current); } - Context._(this.style, this.current); + /// Create a [Context] to be used internally within path. + Context._internal() : style = Style.platform; + + Context._(this.style, this._current); /// The style of path that this context works with. final InternalStyle style; - /// The current directory that relative paths will be relative to. - final String current; + /// The current directory given when Context was created. If null, current + /// directory is evaluated from 'p.current'. + final String _current; + + /// The current directory that relative paths are relative to. + String get current => _current != null ? _current : p.current; /// Gets the path separator for the context's [style]. On Mac and Linux, /// this is `/`. On Windows, it's `\`. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index e9948de0..da69c0eb 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.2.2 +version: 1.2.3 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 196517069f0cc9e35049b39b47f2f4314317fef0 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Mon, 4 Aug 2014 07:09:27 +0000 Subject: [PATCH 049/183] Fix internal constructor in context. BUG= R=lrn@google.com Review URL: https://codereview.chromium.org//434393002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38845 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/src/context.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 89010491..7a88909f 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -44,7 +44,7 @@ class Context { } /// Create a [Context] to be used internally within path. - Context._internal() : style = Style.platform; + Context._internal() : style = Style.platform, _current = null; Context._(this.style, this._current); From 8f2715d4196ef14e6e7dc4b6089107d898108bda Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 6 Aug 2014 22:18:58 +0000 Subject: [PATCH 050/183] Make path's top-level context field public. This is useful for libraries that want to take a context object as an argument, but want to default to respecting changes in the working directory. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//447773003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38957 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/CHANGELOG.md | 5 ++++ pkgs/path/lib/path.dart | 51 ++++++++++++++++++++----------------- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/io_test.dart | 4 +++ 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 9b5520b3..59f38931 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.3.0 + +* Expose a top-level `context` field that provides access to a `Context` object + for the current system. + ## 1.2.3 * Don't cache path Context based on cwd, as cwd involves a system-call to diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index b9a4175b..be6e95a7 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -62,14 +62,17 @@ final windows = new Context(style: Style.windows); /// A default context for manipulating URLs. final url = new Context(style: Style.url); -/// An internal context for the current OS so we can provide a straight -/// functional interface and not require users to create one. -final Context _context = createInternal(); +/// The system path context. +/// +/// This differs from a context created with [new Context] in that its +/// [Context.current] is always the current working directory, rather than being +/// set once when the context is created. +final Context context = createInternal(); /// Returns the [Style] of the current context. /// /// This is the style that all top-level path functions will use. -Style get style => _context.style; +Style get style => context.style; /// Gets the path to the current working directory. /// @@ -89,7 +92,7 @@ String get current { /// Gets the path separator for the current platform. This is `\` on Windows /// and `/` on other platforms (including the browser). -String get separator => _context.separator; +String get separator => context.separator; /// Creates a new path by appending the given path parts to [current]. /// Equivalent to [join()] with [current] as the first argument. Example: @@ -97,7 +100,7 @@ String get separator => _context.separator; /// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' String absolute(String part1, [String part2, String part3, String part4, String part5, String part6, String part7]) => - _context.absolute(part1, part2, part3, part4, part5, part6, part7); + context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// @@ -107,7 +110,7 @@ String absolute(String part1, [String part2, String part3, String part4, /// Trailing separators are ignored. /// /// path.basename('path/to/'); // -> 'to' -String basename(String path) => _context.basename(path); +String basename(String path) => context.basename(path); /// Gets the part of [path] after the last separator, and without any trailing /// file extension. @@ -118,7 +121,7 @@ String basename(String path) => _context.basename(path); /// /// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => - _context.basenameWithoutExtension(path); + context.basenameWithoutExtension(path); /// Gets the part of [path] before the last separator. /// @@ -139,7 +142,7 @@ String basenameWithoutExtension(String path) => /// /// path.dirname('foo'); // -> '.' /// path.dirname(''); // -> '.' -String dirname(String path) => _context.dirname(path); +String dirname(String path) => context.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last /// `.` to the end (including the `.` itself). @@ -154,7 +157,7 @@ String dirname(String path) => _context.dirname(path); /// /// path.extension('~/.bashrc'); // -> '' /// path.extension('~/.notes.txt'); // -> '.txt' -String extension(String path) => _context.extension(path); +String extension(String path) => context.extension(path); // 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 @@ -172,7 +175,7 @@ String extension(String path) => _context.extension(path); /// path.rootPrefix('path/to/foo'); // -> '' /// path.rootPrefix('http://dartlang.org/path/to/foo'); /// // -> 'http://dartlang.org' -String rootPrefix(String path) => _context.rootPrefix(path); +String rootPrefix(String path) => context.rootPrefix(path); /// Returns `true` if [path] is an absolute path and `false` if it is a /// relative path. @@ -186,13 +189,13 @@ String rootPrefix(String path) => _context.rootPrefix(path); /// relative to the root of the current URL. Since root-relative paths are still /// absolute in every other sense, [isAbsolute] will return true for them. They /// can be detected using [isRootRelative]. -bool isAbsolute(String path) => _context.isAbsolute(path); +bool isAbsolute(String path) => context.isAbsolute(path); /// Returns `true` if [path] is a relative path and `false` if it is absolute. /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. -bool isRelative(String path) => _context.isRelative(path); +bool isRelative(String path) => context.isRelative(path); /// Returns `true` if [path] is a root-relative path and `false` if it's not. /// @@ -202,7 +205,7 @@ bool isRelative(String path) => _context.isRelative(path); /// can be detected using [isRootRelative]. /// /// No POSIX and Windows paths are root-relative. -bool isRootRelative(String path) => _context.isRootRelative(path); +bool isRootRelative(String path) => context.isRootRelative(path); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -219,7 +222,7 @@ bool isRootRelative(String path) => _context.isRootRelative(path); /// path.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) => - _context.join(part1, part2, part3, part4, part5, part6, part7, part8); + context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -236,7 +239,7 @@ String join(String part1, [String part2, String part3, String part4, /// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' /// /// For a fixed number of parts, [join] is usually terser. -String joinAll(Iterable parts) => _context.joinAll(parts); +String joinAll(Iterable parts) => context.joinAll(parts); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. @@ -259,13 +262,13 @@ String joinAll(Iterable parts) => _context.joinAll(parts); /// // Browser /// path.split('http://dartlang.org/path/to/foo'); /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] -List split(String path) => _context.split(path); +List split(String path) => context.split(path); /// Normalizes [path], simplifying it by handling `..`, and `.`, and /// removing redundant path separators whenever possible. /// /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' -String normalize(String path) => _context.normalize(path); +String normalize(String path) => context.normalize(path); /// Attempts to convert [path] to an equivalent relative path from the current /// directory. @@ -295,19 +298,19 @@ String normalize(String path) => _context.normalize(path); /// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); /// // -> 'http://dartlang.org' String relative(String path, {String from}) => - _context.relative(path, from: from); + context.relative(path, from: from); /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. /// /// path.isWithin('/root/path', '/root/path/a'); // -> true /// path.isWithin('/root/path', '/root/other'); // -> false /// path.isWithin('/root/path', '/root/path') // -> false -bool isWithin(String parent, String child) => _context.isWithin(parent, child); +bool isWithin(String parent, String child) => context.isWithin(parent, child); /// Removes a trailing extension from the last part of [path]. /// /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' -String withoutExtension(String path) => _context.withoutExtension(path); +String withoutExtension(String path) => context.withoutExtension(path); /// Returns the path represented by [uri], which may be a [String] or a [Uri]. /// @@ -329,7 +332,7 @@ String withoutExtension(String path) => _context.withoutExtension(path); /// If [uri] is relative, a relative path will be returned. /// /// path.fromUri('path/to/foo'); // -> 'path/to/foo' -String fromUri(uri) => _context.fromUri(uri); +String fromUri(uri) => context.fromUri(uri); /// Returns the URI that represents [path]. /// @@ -352,7 +355,7 @@ String fromUri(uri) => _context.fromUri(uri); /// /// path.toUri('path/to/foo') /// // -> Uri.parse('path/to/foo') -Uri toUri(String path) => _context.toUri(path); +Uri toUri(String path) => context.toUri(path); /// Returns a terse, human-readable representation of [uri]. /// @@ -375,4 +378,4 @@ Uri toUri(String path) => _context.toUri(path); /// path.prettyUri('http://dartlang.org/root/path/a/b.dart'); /// // -> r'a/b.dart' /// path.prettyUri('file:///root/path'); // -> 'file:///root/path' -String prettyUri(uri) => _context.prettyUri(uri); +String prettyUri(uri) => context.prettyUri(uri); diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index da69c0eb..2491882e 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.2.3 +version: 1.3.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 3e52bb95..56639205 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -41,10 +41,14 @@ main() { var dir = io.Directory.current.path; try { expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); + expect(path.absolute('foo/bar'), + equals(path.context.join(dir, 'foo/bar'))); io.Directory.current = path.dirname(dir); expect(path.normalize(path.absolute('foo/bar')), equals(path.normalize(path.join(dir, '../foo/bar')))); + expect(path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.context.join(dir, '../foo/bar')))); } finally { io.Directory.current = dir; } From 27dfe34b77402f6dbd75309ebe8fd8bf9ee95a92 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Thu, 7 Aug 2014 08:11:33 +0000 Subject: [PATCH 051/183] Add InternalStyle:rootLength to implement isAbsolute and rootPrefix. This is the first step at adding a few helper methods for improving path package performance. BUG= R=nweiz@google.com, rnystrom@google.com Review URL: https://codereview.chromium.org//439223002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@38966 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/lib/src/context.dart | 9 ++-- pkgs/path/lib/src/internal_style.dart | 19 +++++-- pkgs/path/lib/src/parsed_path.dart | 2 +- pkgs/path/lib/src/style/posix.dart | 8 +-- pkgs/path/lib/src/style/url.dart | 57 +++++++-------------- pkgs/path/lib/src/style/windows.dart | 73 +++++++++++---------------- pkgs/path/test/url_test.dart | 1 + pkgs/path/test/windows_test.dart | 3 ++ 8 files changed, 75 insertions(+), 97 deletions(-) diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 7a88909f..e9237a2b 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -148,10 +148,7 @@ class Context { /// context.rootPrefix('path/to/foo'); // -> '' /// context.rootPrefix('http://dartlang.org/path/to/foo'); /// // -> 'http://dartlang.org' - String rootPrefix(String path) { - var root = _parse(path).root; - return root == null ? '' : root; - } + String rootPrefix(String path) => path.substring(0, style.rootLength(path)); /// Returns `true` if [path] is an absolute path and `false` if it is a /// relative path. @@ -165,7 +162,7 @@ class Context { /// relative to the root of the current URL. Since root-relative paths are /// still absolute in every other sense, [isAbsolute] will return true for /// them. They can be detected using [isRootRelative]. - bool isAbsolute(String path) => _parse(path).isAbsolute; + bool isAbsolute(String path) => style.rootLength(path) > 0; /// Returns `true` if [path] is a relative path and `false` if it is absolute. /// On POSIX systems, absolute paths start with a `/` (forward slash). On @@ -181,7 +178,7 @@ class Context { /// them. They can be detected using [isRootRelative]. /// /// No POSIX and Windows paths are root-relative. - bool isRootRelative(String path) => _parse(path).isRootRelative; + bool isRootRelative(String path) => style.isRootRelative(path); /// Joins the given path parts into a single path. Example: /// diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 67b5d343..db2d3462 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -33,14 +33,25 @@ abstract class InternalStyle extends Style { /// "usr", an additional "/" is needed (making "file:///usr"). bool needsSeparator(String path); + /// Returns the number of characters of the root part. + /// + /// Returns 0 if the path is relative. + /// + /// If the path is root-relative, the root length is 1. + int rootLength(String path); + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. - String getRoot(String path); + String getRoot(String path) { + var length = rootLength(path); + if (length > 0) return path.substring(0, length); + return isRootRelative(path) ? path[0] : null; + } - /// Gets the root prefix of [path] if it's root-relative. + /// Returns whether [path] is root-relative. /// - /// If [path] is relative or absolute and not root-relative, returns `null`. - String getRelativeRoot(String path); + /// If [path] is relative or absolute and not root-relative, returns `false`. + bool isRootRelative(String path); /// Returns the path represented by [uri] in this style. String pathFromUri(Uri uri); diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 57773eee..a7b0afd3 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -45,7 +45,7 @@ class ParsedPath { // Remove the root prefix, if any. var root = style.getRoot(path); - var isRootRelative = style.getRelativeRoot(path) != null; + var isRootRelative = style.isRootRelative(path); if (root != null) path = path.substring(root.length); // Split the parts on path separators. diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index b8b82b40..74aeb4c5 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -30,11 +30,13 @@ class PosixStyle extends InternalStyle { bool needsSeparator(String path) => path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); - String getRoot(String path) { - if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return '/'; - return null; + int rootLength(String path) { + if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1; + return 0; } + bool isRootRelative(String path) => false; + String getRelativeRoot(String path) => null; String pathFromUri(Uri uri) { diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index f383923d..d5d0fdb8 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -36,53 +36,30 @@ class UrlStyle extends InternalStyle { // A URI that's just "scheme://" needs an extra separator, despite ending // with "/". - var root = _getRoot(path); - return root != null && root.endsWith('://'); + return path.endsWith("://") && rootLength(path) == path.length; } - String getRoot(String path) { - var root = _getRoot(path); - return root == null ? getRelativeRoot(path) : root; + int rootLength(String path) { + if (path.isEmpty) return 0; + if (isSeparator(path.codeUnitAt(0))) return 1; + var index = path.indexOf("/"); + if (index > 0 && path.startsWith('://', index - 1)) { + // The root part is up until the next '/', or the full path. Skip + // '://' and search for '/' after that. + index = path.indexOf('/', index + 2); + if (index > 0) return index; + return path.length; + } + return 0; } - String getRelativeRoot(String path) { - if (path.isEmpty) return null; - return isSeparator(path.codeUnitAt(0)) ? "/" : null; - } + bool isRootRelative(String path) => + path.isNotEmpty && isSeparator(path.codeUnitAt(0)); + + String getRelativeRoot(String path) => isRootRelative(path) ? '/' : null; String pathFromUri(Uri uri) => uri.toString(); Uri relativePathToUri(String path) => Uri.parse(path); Uri absolutePathToUri(String path) => Uri.parse(path); - - // A helper method for [getRoot] that doesn't handle relative roots. - String _getRoot(String path) { - if (path.isEmpty) return null; - - // We aren't using a RegExp for this because they're slow (issue 19090). If - // we could, we'd match against r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*". - - if (!isAlphabetic(path.codeUnitAt(0))) return null; - var start = 1; - for (; start < path.length; start++) { - var char = path.codeUnitAt(start); - if (isAlphabetic(char)) continue; - if (isNumeric(char)) continue; - if (char == chars.MINUS || char == chars.PLUS || char == chars.PERIOD) { - continue; - } - - break; - } - - if (start + 3 > path.length) return null; - if (path.substring(start, start + 3) != '://') return null; - start += 3; - - // A URL root can end with a non-"/" prefix. - while (start < path.length && !isSeparator(path.codeUnitAt(start))) { - start++; - } - return path.substring(0, start); - } } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 2965f1ee..16e14d59 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -34,16 +34,38 @@ class WindowsStyle extends InternalStyle { return !isSeparator(path.codeUnitAt(path.length - 1)); } - String getRoot(String path) { - var root = _getRoot(path); - return root == null ? getRelativeRoot(path) : root; + int rootLength(String path) { + if (path.isEmpty) return 0; + if (path.codeUnitAt(0) == chars.SLASH) return 1; + if (path.codeUnitAt(0) == chars.BACKSLASH) { + if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; + // The path is a network share. Search for up to two '\'s, as they are + // the server and share - and part of the root part. + var index = path.indexOf('\\', 2); + if (index > 0) { + index = path.indexOf('\\', index + 1); + if (index > 0) return index; + } + return path.length; + } + // If the path is of the form 'C:/' or 'C:\', with C being any letter, it's + // a root part. + if (path.length < 3) return 0; + // Check for the letter. + if (!isAlphabetic(path.codeUnitAt(0))) return 0; + // Check for the ':'. + if (path.codeUnitAt(1) != chars.COLON) return 0; + // Check for either '/' or '\'. + if (!isSeparator(path.codeUnitAt(2))) return 0; + return 3; } + bool isRootRelative(String path) => rootLength(path) == 1; + String getRelativeRoot(String path) { - if (path.isEmpty) return null; - if (!isSeparator(path.codeUnitAt(0))) return null; - if (path.length > 1 && isSeparator(path.codeUnitAt(1))) return null; - return path[0]; + var length = rootLength(path); + if (length == 1) return path[0]; + return null; } String pathFromUri(Uri uri) { @@ -100,39 +122,4 @@ class WindowsStyle extends InternalStyle { return new Uri(scheme: 'file', pathSegments: parsed.parts); } } - - // A helper method for [getRoot] that doesn't handle relative roots. - String _getRoot(String path) { - if (path.length < 3) return null; - - // We aren't using a RegExp for this because they're slow (issue 19090). If - // we could, we'd match against r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'. - - // Try roots like "C:\". - if (isAlphabetic(path.codeUnitAt(0))) { - if (path.codeUnitAt(1) != chars.COLON) return null; - if (!isSeparator(path.codeUnitAt(2))) return null; - return path.substring(0, 3); - } - - // Try roots like "\\server\share". - if (!path.startsWith('\\\\')) return null; - - var start = 2; - // The server is one or more non-"\" characters. - while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { - start++; - } - if (start == 2 || start == path.length) return null; - - // The share is one or more non-"\" characters. - start += 1; - if (path.codeUnitAt(start) == chars.BACKSLASH) return null; - start += 1; - while (start < path.length && path.codeUnitAt(start) != chars.BACKSLASH) { - start++; - } - - return path.substring(0, start); - } -} \ No newline at end of file +} diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 27691d85..d75377e5 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -37,6 +37,7 @@ main() { expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); expect(context.rootPrefix('file://'), 'file://'); expect(context.rootPrefix('/'), '/'); + expect(context.rootPrefix('foo/bar://'), ''); }); test('dirname', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 7c16e319..72eefc3f 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -41,6 +41,9 @@ main() { expect(context.rootPrefix('C:\\'), r'C:\'); expect(context.rootPrefix('C:/'), 'C:/'); expect(context.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); + expect(context.rootPrefix(r'\\server\share'), r'\\server\share'); + expect(context.rootPrefix(r'\\server\'), r'\\server\'); + expect(context.rootPrefix(r'\\server'), r'\\server'); expect(context.rootPrefix(r'\a\b'), r'\'); expect(context.rootPrefix(r'/a/b'), r'/'); expect(context.rootPrefix(r'\'), r'\'); From d6f3fb3d8c22e99f0b3bb14cbaa9a463fee6a92e Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Mon, 18 Aug 2014 06:32:49 +0000 Subject: [PATCH 052/183] Add benchmark to 'path' package, of the most common methods. BUG= R=nweiz@google.com, rnystrom@google.com Review URL: https://codereview.chromium.org//443643002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@39321 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/path/benchmark/benchmark.dart | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 pkgs/path/benchmark/benchmark.dart diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart new file mode 100644 index 00000000..60a2b531 --- /dev/null +++ b/pkgs/path/benchmark/benchmark.dart @@ -0,0 +1,68 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import '../lib/path.dart' as path; + +void runBenchmark(String name, Function func, List files) { + // Warmup. + for (int i = 0; i < 10000; i++) { + for (var p in files) { + func(p); + } + } + var count = 100000; + var sw = new Stopwatch()..start(); + for (int i = 0; i < count; i++) { + for (var p in files) { + func(p); + } + } + print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})"); +} + +main(args) { + for (var style in [path.Style.posix, path.Style.url, path.Style.windows]) { + var context = new path.Context(style: style); + var files = COMMON_PATHS.toList()..addAll(STYLE_PATHS[style]); + + void benchmark(name, func) { + name = style.name + '-' + name; + if (args.isEmpty || args.any((arg) => name.contains(arg))) { + runBenchmark(name, func, files); + } + } + + benchmark('basename', context.basename); + benchmark('basenameWithoutExtension', context.basenameWithoutExtension); + benchmark('dirname', context.dirname); + benchmark('extension', context.extension); + benchmark('rootPrefix', context.rootPrefix); + benchmark('isAbsolute', context.isAbsolute); + benchmark('isRelative', context.isRelative); + benchmark('isRootRelative', context.isRootRelative); + benchmark('normalize', context.normalize); + benchmark('relative', context.relative); + benchmark('toUri', context.toUri); + benchmark('prettyUri', context.prettyUri); + } +} + +const COMMON_PATHS = const [ + '.', + '..', + 'out/ReleaseIA32/packages', +]; + +final STYLE_PATHS = { + path.Style.posix: [ + '/home/user/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart', + ], + path.Style.url: [ + 'https://example.server.org/443643002/path?top=yes#fragment', + ], + path.Style.windows: [ + r'C:\User\me\', + r'\\server\share\my\folders\some\file.data', + ], +}; From ea54cec54f4e0d088bac31bd9d1b77e8ec9479d2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:20:35 -0800 Subject: [PATCH 053/183] Add gitignore, status, and codereview files. --- pkgs/path/.gitignore | 14 ++++++++++++++ pkgs/path/.status | 9 +++++++++ pkgs/path/codereview.settings | 3 +++ 3 files changed, 26 insertions(+) create mode 100644 pkgs/path/.gitignore create mode 100644 pkgs/path/.status create mode 100644 pkgs/path/codereview.settings diff --git a/pkgs/path/.gitignore b/pkgs/path/.gitignore new file mode 100644 index 00000000..388eff0b --- /dev/null +++ b/pkgs/path/.gitignore @@ -0,0 +1,14 @@ +# Don’t commit the following directories created by pub. +.buildlog +.pub/ +build/ +packages + +# Or the files created by dart2js. +*.dart.js +*.js_ +*.js.deps +*.js.map + +# Include when developing application packages. +pubspec.lock \ No newline at end of file diff --git a/pkgs/path/.status b/pkgs/path/.status new file mode 100644 index 00000000..5a7089bb --- /dev/null +++ b/pkgs/path/.status @@ -0,0 +1,9 @@ +# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +[ $browser ] +path/test/io_test: Fail, OK # Uses dart:io. + +[ $runtime == vm ] +path/test/browser_test: Fail, OK # Uses dart:html diff --git a/pkgs/path/codereview.settings b/pkgs/path/codereview.settings new file mode 100644 index 00000000..eef39556 --- /dev/null +++ b/pkgs/path/codereview.settings @@ -0,0 +1,3 @@ +CODE_REVIEW_SERVER: http://codereview.chromium.org/ +VIEW_VC: https://github.com/dart-lang/path/commit/ +CC_LIST: reviews@dartlang.org \ No newline at end of file From 6615bf30d6d722f16fea6818db0394f593dcdf49 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:20:45 -0800 Subject: [PATCH 054/183] Update the pubspec's homepage link. --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 2491882e..c4c6ff27 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -5,7 +5,7 @@ description: > A string-based path manipulation library. All of the path operations you know and love, with solid support on both Windows and POSIX (Linux and Mac OS X) machines. -homepage: http://www.dartlang.org +homepage: http://github.com/dart-lang/path dev_dependencies: unittest: ">=0.9.0 <0.12.0" environment: From 378d225a955853f8304218e40a8d467bc1770954 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 Dec 2014 16:51:04 -0800 Subject: [PATCH 055/183] Fix the status file to match the package bots' expectations. --- pkgs/path/.status | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.status b/pkgs/path/.status index 5a7089bb..c302eb4e 100644 --- a/pkgs/path/.status +++ b/pkgs/path/.status @@ -2,8 +2,15 @@ # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. +# Skip non-test files ending with "_test". +*/packages/*: Skip + +# Only run tests from the build directory, since we don't care about the +# difference between transformed an untransformed code. +test/*: Skip + [ $browser ] -path/test/io_test: Fail, OK # Uses dart:io. +*/io_test: Fail, OK # Uses dart:io. [ $runtime == vm ] -path/test/browser_test: Fail, OK # Uses dart:html +*/browser_test: Fail, OK # Uses dart:html From aa2b34f6a7eecb1fd5eb8e76d6f1a165e4443dc3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Dec 2014 17:41:52 -0800 Subject: [PATCH 056/183] Properly skip tests in packages directories. --- pkgs/path/.status | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/path/.status b/pkgs/path/.status index c302eb4e..c4ea50b1 100644 --- a/pkgs/path/.status +++ b/pkgs/path/.status @@ -3,7 +3,12 @@ # BSD-style license that can be found in the LICENSE file. # Skip non-test files ending with "_test". +packages/*: Skip */packages/*: Skip +*/*/packages/*: Skip +*/*/*/packages/*: Skip +*/*/*/*packages/*: Skip +*/*/*/*/*packages/*: Skip # Only run tests from the build directory, since we don't care about the # difference between transformed an untransformed code. From 2edd6ca5f943258676ca563c131754406791730f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 18 Dec 2014 17:54:28 -0800 Subject: [PATCH 057/183] Remove initial */s in .status. --- pkgs/path/.status | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.status b/pkgs/path/.status index c4ea50b1..bfc58fc1 100644 --- a/pkgs/path/.status +++ b/pkgs/path/.status @@ -15,7 +15,7 @@ packages/*: Skip test/*: Skip [ $browser ] -*/io_test: Fail, OK # Uses dart:io. +build/test/io_test: Fail, OK # Uses dart:io. [ $runtime == vm ] -*/browser_test: Fail, OK # Uses dart:html +build/test/browser_test: Fail, OK # Uses dart:html From 880d54364cfbb3afd05a436cf1d88c511e49d16c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 19 Dec 2014 12:56:53 -0800 Subject: [PATCH 058/183] Actually release the performance changes to path. R=rnystrom@google.com Review URL: https://codereview.chromium.org//790173004 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 59f38931..1cc7127c 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.1 + +* Add a number of performance improvements. + ## 1.3.0 * Expose a top-level `context` field that provides access to a `Context` object diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index c4c6ff27..c9f0ec61 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.0 +version: 1.3.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From ab87f4c3f52a6c140cf110fb90b366b8442e34f9 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 13 Jan 2015 13:25:34 -0800 Subject: [PATCH 059/183] fixed relative root tests R=nweiz@google.com Review URL: https://codereview.chromium.org//837563005 --- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 10 +++++----- pkgs/path/test/url_test.dart | 13 ++++++------- pkgs/path/test/windows_test.dart | 10 +++++----- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index c9f0ec61..3455ce38 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.1 +version: 1.3.2-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 5b6d1315..e6aaba4e 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -421,11 +421,11 @@ main() { test('from a relative root', () { var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); - expect(context.isWithin('.', 'a/b/c'), isTrue); - expect(context.isWithin('.', '../a/b/c'), isFalse); - expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(context.isWithin('/', '/baz/bang'), isTrue); - expect(context.isWithin('.', '/baz/bang'), isFalse); + expect(r.isWithin('.', 'a/b/c'), isTrue); + expect(r.isWithin('.', '../a/b/c'), isFalse); + expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(r.isWithin('/', '/baz/bang'), isTrue); + expect(r.isWithin('.', '/baz/bang'), isFalse); }); }); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index d75377e5..3f7e6feb 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -639,13 +639,12 @@ main() { test('from a relative root', () { var r = new path.Context(style: path.Style.url, current: 'foo/bar'); - expect(context.isWithin('.', 'a/b/c'), isTrue); - expect(context.isWithin('.', '../a/b/c'), isFalse); - expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(context.isWithin( - 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), - isTrue); - expect(context.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); + expect(r.isWithin('.', 'a/b/c'), isTrue); + expect(r.isWithin('.', '../a/b/c'), isFalse); + expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(r.isWithin( + 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), isTrue); + expect(r.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); }); }); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 72eefc3f..1f5dc387 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -539,11 +539,11 @@ main() { test('from a relative root', () { var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); - expect(context.isWithin('.', r'a\b\c'), isTrue); - expect(context.isWithin('.', r'..\a\b\c'), isFalse); - expect(context.isWithin('.', r'..\..\a\foo\b\c'), isFalse); - expect(context.isWithin(r'C:\', r'C:\baz\bang'), isTrue); - expect(context.isWithin('.', r'C:\baz\bang'), isFalse); + expect(r.isWithin('.', r'a\b\c'), isTrue); + expect(r.isWithin('.', r'..\a\b\c'), isFalse); + expect(r.isWithin('.', r'..\..\a\foo\b\c'), isFalse); + expect(r.isWithin(r'C:\', r'C:\baz\bang'), isTrue); + expect(r.isWithin('.', r'C:\baz\bang'), isFalse); }); }); From c9f8d96b176a71076c8d3b3334f78b26345aaf41 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 13 Jan 2015 13:40:13 -0800 Subject: [PATCH 060/183] reformatted, remove unused fields and imports, updated readme and fixed codereview.settings R=nweiz@google.com Review URL: https://codereview.chromium.org//831093005 --- pkgs/path/README.md | 14 ++-- pkgs/path/benchmark/benchmark.dart | 6 +- pkgs/path/codereview.settings | 2 +- pkgs/path/lib/path.dart | 8 +-- pkgs/path/lib/src/context.dart | 44 +++++++----- pkgs/path/lib/src/parsed_path.dart | 16 ++--- pkgs/path/lib/src/style/url.dart | 5 +- pkgs/path/lib/src/style/windows.dart | 10 +-- pkgs/path/lib/src/utils.dart | 2 +- pkgs/path/test/browser_test.dart | 6 +- pkgs/path/test/io_test.dart | 4 +- pkgs/path/test/posix_test.dart | 24 +++---- pkgs/path/test/relative_test.dart | 21 +++--- pkgs/path/test/url_test.dart | 103 ++++++++++++--------------- pkgs/path/test/windows_test.dart | 59 ++++++++------- 15 files changed, 158 insertions(+), 166 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 2e3eec1b..5a172cdf 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -14,13 +14,17 @@ construct a `path.Context` for that style. The path library was designed to be imported with a prefix, though you don't have to if you don't want to: - import 'package:path/path.dart' as path; +```dart +import 'package:path/path.dart' as path; +``` The most common way to use the library is through the top-level functions. These manipulate path strings based on your current working directory and the path style (POSIX, Windows, or URLs) of the host platform. For example: - path.join("directory", "file.txt"); +```dart +path.join("directory", "file.txt"); +``` This calls the top-level [join] function to join "directory" and "file.txt" using the current platform's directory separator. @@ -29,8 +33,10 @@ If you want to work with paths for a specific platform regardless of the underlying platform that the program is running on, you can create a [Context] and give it an explicit [Style]: - var context = new path.Context(style: Style.windows); - context.join("directory", "file.txt"); +```dart +var context = new path.Context(style: Style.windows); +context.join("directory", "file.txt"); +``` This will join "directory" and "file.txt" using the Windows path separator, even when the program is run on a POSIX machine. diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 60a2b531..419eee0f 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -48,11 +48,7 @@ main(args) { } } -const COMMON_PATHS = const [ - '.', - '..', - 'out/ReleaseIA32/packages', -]; +const COMMON_PATHS = const ['.', '..', 'out/ReleaseIA32/packages']; final STYLE_PATHS = { path.Style.posix: [ diff --git a/pkgs/path/codereview.settings b/pkgs/path/codereview.settings index eef39556..1ff4094d 100644 --- a/pkgs/path/codereview.settings +++ b/pkgs/path/codereview.settings @@ -1,3 +1,3 @@ -CODE_REVIEW_SERVER: http://codereview.chromium.org/ +CODE_REVIEW_SERVER: https://codereview.chromium.org/ VIEW_VC: https://github.com/dart-lang/path/commit/ CC_LIST: reviews@dartlang.org \ No newline at end of file diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index be6e95a7..cfbaf11f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -99,8 +99,8 @@ String get separator => context.separator; /// /// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' String absolute(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) => - context.absolute(part1, part2, part3, part4, part5, part6, part7); + String part5, String part6, String part7]) => + context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// @@ -221,8 +221,8 @@ bool isRootRelative(String path) => context.isRootRelative(path); /// /// path.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) => - context.join(part1, part2, part3, part4, part5, part6, part7, part8); + String part5, String part6, String part7, String part8]) => + context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index e9237a2b..a3efd95a 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -44,7 +44,9 @@ class Context { } /// Create a [Context] to be used internally within path. - Context._internal() : style = Style.platform, _current = null; + Context._internal() + : style = Style.platform, + _current = null; Context._(this.style, this._current); @@ -70,7 +72,7 @@ class Context { /// /// If [current] isn't absolute, this won't return an absolute path. String absolute(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) { + String part5, String part6, String part7]) { return join(current, part1, part2, part3, part4, part5, part6, part7); } @@ -94,7 +96,7 @@ class Context { /// /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => - _parse(path).basenameWithoutExtension; + _parse(path).basenameWithoutExtension; /// Gets the part of [path] before the last separator. /// @@ -194,7 +196,7 @@ class Context { /// context.join('path', '/to', 'foo'); // -> '/to/foo' /// String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) { + String part5, String part6, String part7, String part8]) { var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; _validateArgList("join", parts); return joinAll(parts.where((part) => part != null)); @@ -274,8 +276,7 @@ class Context { List split(String path) { var parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.where((part) => !part.isEmpty) - .toList(); + parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); if (parsed.root != null) parsed.parts.insert(0, parsed.root); return parsed.parts; } @@ -354,15 +355,16 @@ class Context { // one. In Windows, drive letters are case-insenstive and we allow // calculation of relative paths, even if a path has not been normalized. if (fromParsed.root != pathParsed.root && - ((fromParsed.root == null || pathParsed.root == null) || - fromParsed.root.toLowerCase().replaceAll('/', '\\') != - pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { return pathParsed.toString(); } // Strip off their common prefix. - while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && - fromParsed.parts[0] == pathParsed.parts[0]) { + while (fromParsed.parts.length > 0 && + pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { fromParsed.parts.removeAt(0); fromParsed.separators.removeAt(1); pathParsed.parts.removeAt(0); @@ -375,11 +377,11 @@ class Context { if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { throw new PathException('Unable to find a path to "$path" from "$from".'); } - pathParsed.parts.insertAll(0, - new List.filled(fromParsed.parts.length, '..')); + pathParsed.parts.insertAll( + 0, new List.filled(fromParsed.parts.length, '..')); pathParsed.separators[0] = ''; - pathParsed.separators.insertAll(1, - new List.filled(fromParsed.parts.length, style.separator)); + pathParsed.separators.insertAll( + 1, new List.filled(fromParsed.parts.length, style.separator)); // Corner case: the paths completely collapsed. if (pathParsed.parts.length == 0) return '.'; @@ -388,7 +390,10 @@ class Context { // Don't add a final '/.' in that case. if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { pathParsed.parts.removeLast(); - pathParsed.separators..removeLast()..removeLast()..add(''); + pathParsed.separators + ..removeLast() + ..removeLast() + ..add(''); } // Make it relative. @@ -415,7 +420,8 @@ class Context { } var parts = this.split(relative); - return this.isRelative(relative) && parts.first != '..' && + return this.isRelative(relative) && + parts.first != '..' && parts.first != '.'; } @@ -518,7 +524,6 @@ class Context { var path = normalize(fromUri(uri)); var rel = relative(path); - var components = split(rel); // Only return a relative path if it's actually shorter than the absolute // path. This avoids ugly things like long "../" chains to get to the root @@ -544,7 +549,8 @@ _validateArgList(String method, List args) { // Show the arguments. var message = new StringBuffer(); message.write("$method("); - message.write(args.take(numArgs) + message.write(args + .take(numArgs) .map((arg) => arg == null ? "null" : '"$arg"') .join(", ")); message.write("): part ${i - 1} was null, but part $i was not."); diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index a7b0afd3..b853eb1a 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -41,8 +41,6 @@ class ParsedPath { bool get isAbsolute => root != null; factory ParsedPath.parse(String path, InternalStyle style) { - var before = path; - // Remove the root prefix, if any. var root = style.getRoot(path); var isRootRelative = style.isRootRelative(path); @@ -78,8 +76,8 @@ class ParsedPath { return new ParsedPath._(style, root, isRootRelative, parts, separators); } - ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, - this.separators); + ParsedPath._( + this.style, this.root, this.isRootRelative, this.parts, this.separators); String get basename { var copy = this.clone(); @@ -134,9 +132,9 @@ class ParsedPath { // Canonicalize separators. var newSeparators = new List.generate( newParts.length, (_) => style.separator, growable: true); - newSeparators.insert(0, - isAbsolute && newParts.length > 0 && style.needsSeparator(root) ? - style.separator : ''); + newSeparators.insert(0, isAbsolute && + newParts.length > 0 && + style.needsSeparator(root) ? style.separator : ''); parts = newParts; separators = newSeparators; @@ -180,8 +178,6 @@ class ParsedPath { return [file.substring(0, lastDot), file.substring(lastDot)]; } - ParsedPath clone() => new ParsedPath._( - style, root, isRootRelative, + ParsedPath clone() => new ParsedPath._(style, root, isRootRelative, new List.from(parts), new List.from(separators)); } - diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index d5d0fdb8..255f22a6 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -6,7 +6,6 @@ library path.style.url; import '../characters.dart' as chars; import '../internal_style.dart'; -import '../utils.dart'; /// The style for URL paths. class UrlStyle extends InternalStyle { @@ -19,8 +18,8 @@ class UrlStyle extends InternalStyle { // Deprecated properties. final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp( - r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final needsSeparatorPattern = + new RegExp(r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); final relativeRootPattern = new RegExp(r"^/"); diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 16e14d59..f38efa52 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -38,7 +38,7 @@ class WindowsStyle extends InternalStyle { if (path.isEmpty) return 0; if (path.codeUnitAt(0) == chars.SLASH) return 1; if (path.codeUnitAt(0) == chars.BACKSLASH) { - if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; + if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; // The path is a network share. Search for up to two '\'s, as they are // the server and share - and part of the root part. var index = path.indexOf('\\', 2); @@ -101,8 +101,8 @@ class WindowsStyle extends InternalStyle { parsed.parts.add(""); } - return new Uri(scheme: 'file', host: rootParts.first, - pathSegments: parsed.parts); + return new Uri( + scheme: 'file', host: rootParts.first, pathSegments: parsed.parts); } else { // Drive-letter paths become "file:///C:/path/to/file". @@ -116,8 +116,8 @@ class WindowsStyle extends InternalStyle { // Get rid of the trailing "\" in "C:\" because the URI constructor will // add a separator on its own. - parsed.parts.insert(0, - parsed.root.replaceAll("/", "").replaceAll("\\", "")); + parsed.parts.insert( + 0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); return new Uri(scheme: 'file', pathSegments: parsed.parts); } diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index 0636261c..e3207495 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -10,7 +10,7 @@ import 'characters.dart' as chars; /// lowercase). bool isAlphabetic(int char) => (char >= chars.UPPER_A && char <= chars.UPPER_Z) || - (char >= chars.LOWER_A && char <= chars.LOWER_Z); + (char >= chars.LOWER_A && char <= chars.LOWER_Z); /// Returns whether [char] is the code for an ASCII digit. bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index d8584a46..0b56c7ac 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -15,7 +15,7 @@ main() { test('uses the window location if root and style are omitted', () { var context = new path.Context(); expect(context.current, - Uri.parse(window.location.href).resolve('.').toString()); + Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { @@ -34,7 +34,7 @@ main() { }); test('current', () { - expect(path.current, - Uri.parse(window.location.href).resolve('.').toString()); + expect( + path.current, Uri.parse(window.location.href).resolve('.').toString()); }); } diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 56639205..e8555222 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -41,8 +41,8 @@ main() { var dir = io.Directory.current.path; try { expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); - expect(path.absolute('foo/bar'), - equals(path.context.join(dir, 'foo/bar'))); + expect( + path.absolute('foo/bar'), equals(path.context.join(dir, 'foo/bar'))); io.Directory.current = path.dirname(dir); expect(path.normalize(path.absolute('foo/bar')), diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index e6aaba4e..25e34199 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -10,8 +10,8 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var context = new path.Context( - style: path.Style.posix, current: '/root/path'); + var context = + new path.Context(style: path.Style.posix, current: '/root/path'); test('separator', () { expect(context.separator, '/'); @@ -184,7 +184,7 @@ main() { test('join does not modify internal ., .., or trailing separators', () { expect(context.join('a/', 'b/c/'), 'a/b/c/'); expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), - 'a/b/./c/..//d/.././..//e/f//'); + 'a/b/./c/..//d/.././..//e/f//'); expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); @@ -245,7 +245,7 @@ main() { expect(context.normalize(r'C:\'), r'C:\'); expect(context.normalize(r'\\'), r'\\'); expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), - 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); + 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { @@ -392,12 +392,12 @@ main() { }); test('with a root parameter and a relative root', () { - var r = new path.Context( - style: path.Style.posix, current: 'relative/root'); + var r = + new path.Context(style: path.Style.posix, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); - expect(r.relative('/foo/bar/baz', from: 'foo/bar'), - equals('/foo/bar/baz')); + expect( + r.relative('/foo/bar/baz', from: 'foo/bar'), equals('/foo/bar/baz')); expect(r.relative('..', from: 'foo/bar'), equals('../../..')); }); @@ -449,8 +449,8 @@ main() { test('ignores parts before an absolute path', () { expect(context.absolute('a', '/b', '/c', 'd'), '/c/d'); - expect(context.absolute('a', r'c:\b', 'c', 'd'), - r'/root/path/a/c:\b/c/d'); + expect( + context.absolute('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d'); expect(context.absolute('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); }); }); @@ -477,8 +477,8 @@ main() { group('fromUri', () { test('with a URI', () { expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); - expect(context.fromUri(Uri.parse('file:///path/to/foo/')), - '/path/to/foo/'); + expect( + context.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); expect(context.fromUri(Uri.parse('file:///')), '/'); expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); expect(context.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 58163d25..4523c8d7 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -13,16 +13,16 @@ void main() { test("test relative", () { relativeTest(new path.Context(style: path.Style.posix, current: '.'), '/'); relativeTest(new path.Context(style: path.Style.posix, current: '/'), '/'); - relativeTest(new path.Context(style: path.Style.windows, current: r'd:\'), - r'c:\'); - relativeTest(new path.Context(style: path.Style.windows, current: '.'), - r'c:\'); + relativeTest( + new path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); + relativeTest( + new path.Context(style: path.Style.windows, current: '.'), r'c:\'); relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), - 'http://myserver/'); + 'http://myserver/'); relativeTest(new path.Context(style: path.Style.url, current: '.'), - 'http://myserver/'); - relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), - '/'); + 'http://myserver/'); + relativeTest( + new path.Context(style: path.Style.url, current: 'file:///'), '/'); relativeTest(new path.Context(style: path.Style.url, current: '.'), '/'); }); } @@ -78,10 +78,9 @@ void relativeTest(path.Context context, String prefix) { // Should always throw - no relative path can be constructed. if (isRelative) { expect(() => context.relative('.', from: '..'), throwsPathException); - expect(() => context.relative('a/b', from: '../../d'), - throwsPathException); + expect(() => context.relative('a/b', from: '../../d'), throwsPathException); expect(() => context.relative('a/b', from: '${prefix}a/b'), - throwsPathException); + throwsPathException); // An absolute path relative from a relative path returns the absolute path. expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 3f7e6feb..aca161b4 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -6,8 +6,8 @@ import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; main() { - var context = new path.Context(style: path.Style.url, - current: 'http://dartlang.org/root/path'); + var context = new path.Context( + style: path.Style.url, current: 'http://dartlang.org/root/path'); test('separator', () { expect(context.separator, '/'); @@ -28,8 +28,8 @@ main() { expect(context.rootPrefix(''), ''); expect(context.rootPrefix('a'), ''); expect(context.rootPrefix('a/b'), ''); - expect(context.rootPrefix('http://dartlang.org/a/c'), - 'http://dartlang.org'); + expect( + context.rootPrefix('http://dartlang.org/a/c'), 'http://dartlang.org'); expect(context.rootPrefix('file:///a/c'), 'file://'); expect(context.rootPrefix('/a/c'), '/'); expect(context.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); @@ -246,7 +246,7 @@ main() { test('Join does not modify internal ., .., or trailing separators', () { expect(context.join('a/', 'b/c/'), 'a/b/c/'); expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), - 'a/b/./c/..//d/.././..//e/f//'); + 'a/b/./c/..//d/.././..//e/f//'); expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); @@ -265,9 +265,12 @@ main() { expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); expect(context.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(context.joinAll( - ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), - 'http://dartlang.org/c/d'); + expect(context.joinAll([ + 'a', + 'http://google.com/b', + 'http://dartlang.org/c', + 'd' + ]), 'http://dartlang.org/c/d'); expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); @@ -334,7 +337,7 @@ main() { expect(context.normalize(r'C:\'), r'C:\'); expect(context.normalize(r'\\'), r'\\'); expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), - 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); + 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { @@ -351,8 +354,8 @@ main() { expect(context.normalize('http://dartlang.org/.'), 'http://dartlang.org'); expect(context.normalize('file:///.'), 'file://'); expect(context.normalize('/.'), '/'); - expect(context.normalize('http://dartlang.org/./'), - 'http://dartlang.org'); + expect( + context.normalize('http://dartlang.org/./'), 'http://dartlang.org'); expect(context.normalize('file:///./'), 'file://'); expect(context.normalize('/./'), '/'); expect(context.normalize('./.'), '.'); @@ -368,8 +371,8 @@ main() { expect(context.normalize('../'), '..'); expect(context.normalize('../../..'), '../../..'); expect(context.normalize('../../../'), '../../..'); - expect(context.normalize('http://dartlang.org/..'), - 'http://dartlang.org'); + expect( + context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); expect(context.normalize('file:///..'), 'file://'); expect(context.normalize('/..'), '/'); expect(context.normalize('http://dartlang.org/../../..'), @@ -395,10 +398,10 @@ main() { test('does not walk before root on absolute paths', () { expect(context.normalize('..'), '..'); expect(context.normalize('../'), '..'); - expect(context.normalize('http://dartlang.org/..'), - 'http://dartlang.org'); + expect( + context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); expect(context.normalize('http://dartlang.org/../a'), - 'http://dartlang.org/a'); + 'http://dartlang.org/a'); expect(context.normalize('file:///..'), 'file://'); expect(context.normalize('file:///../a'), 'file:///a'); expect(context.normalize('/..'), '/'); @@ -441,8 +444,8 @@ main() { expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(context.relative('http://dartlang.org/root/a/b.txt'), - '../a/b.txt'); + expect( + context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); @@ -455,15 +458,14 @@ main() { 'a/b.txt'); expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(context.relative('http://dartlang.org/root/a/b.txt'), - '../a/b.txt'); + expect( + context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path with different hostname/protocol', () { expect(context.relative(r'http://google.com/a/b'), r'http://google.com/a/b'); - expect(context.relative(r'file:///a/b'), - r'file:///a/b'); + expect(context.relative(r'file:///a/b'), r'file:///a/b'); }); test('given relative path', () { @@ -480,11 +482,9 @@ main() { // Regression test('from root-only path', () { expect(context.relative('http://dartlang.org', - from: 'http://dartlang.org'), - '.'); + from: 'http://dartlang.org'), '.'); expect(context.relative('http://dartlang.org/root/path', - from: 'http://dartlang.org'), - 'root/path'); + from: 'http://dartlang.org'), 'root/path'); }); }); @@ -552,15 +552,12 @@ main() { context.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(context.relative('http://dartlang.org/foo/bar/baz', - from: 'file:///foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); - expect( - context.relative('/foo/bar/baz', from: 'file:///foo/bar'), + expect(context.relative('/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect( - context.relative('file:///foo/bar/baz', from: '/foo/bar'), + expect(context.relative('file:///foo/bar/baz', from: '/foo/bar'), equals('file:///foo/bar/baz')); expect(context.relative('..', from: '/foo/bar'), equals('../../root')); @@ -570,8 +567,8 @@ main() { equals('http://dartlang.org/root')); expect(context.relative('..', from: '/foo/bar'), equals('../../root')); - expect(context.relative('http://dartlang.org/foo/bar/baz', - from: 'foo/bar'), + expect( + context.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); expect(context.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); @@ -584,15 +581,12 @@ main() { test('with a root parameter and a relative root', () { var r = new path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect( - r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + expect(r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('/foo/bar/baz')); - expect( - r.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + expect(r.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('http://dartlang.org/foo/bar/baz', - from: 'file:///foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('http://dartlang.org/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); @@ -600,8 +594,8 @@ main() { equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); - expect(r.relative('/foo/bar/baz', from: 'foo/bar'), - equals('/foo/bar/baz')); + expect( + r.relative('/foo/bar/baz', from: 'foo/bar'), equals('/foo/bar/baz')); expect(r.relative('..', from: 'foo/bar'), equals('../../..')); }); @@ -623,11 +617,9 @@ main() { expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); expect(context.isWithin( - 'http://dartlang.org', 'http://dartlang.org/foo/bar'), - isTrue); + 'http://dartlang.org', 'http://dartlang.org/foo/bar'), isTrue); expect(context.isWithin( - 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), - isFalse); + 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), isFalse); expect(context.isWithin('http://dartlang.org', '/foo/bar'), isTrue); expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); expect(context.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); @@ -642,8 +634,8 @@ main() { expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(r.isWithin( - 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), isTrue); + expect(r.isWithin('http://dartlang.org/', 'http://dartlang.org/baz/bang'), + isTrue); expect(r.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); }); }); @@ -667,8 +659,8 @@ main() { test('does not add separator if a part ends in one', () { expect(context.absolute('a/', 'b', 'c/', 'd'), 'http://dartlang.org/root/path/a/b/c/d'); - expect(context.absolute(r'a\', 'b'), - r'http://dartlang.org/root/path/a\/b'); + expect( + context.absolute(r'a\', 'b'), r'http://dartlang.org/root/path/a\/b'); }); test('ignores parts before an absolute path', () { @@ -709,8 +701,8 @@ main() { expect(context.fromUri(Uri.parse('file:///path/to/foo')), 'file:///path/to/foo'); expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(context.fromUri( - Uri.parse('http://dartlang.org/path/to/foo%23bar')), + expect( + context.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), 'http://dartlang.org/path/to/foo%23bar'); // Since the resulting "path" is also a URL, special characters should // remain percent-encoded in the result. @@ -729,8 +721,8 @@ main() { Uri.parse('http://dartlang.org/path/to/foo')); expect(context.toUri('http://dartlang.org/path/to/foo/'), Uri.parse('http://dartlang.org/path/to/foo/')); - expect(context.toUri('file:///path/to/foo'), - Uri.parse('file:///path/to/foo')); + expect( + context.toUri('file:///path/to/foo'), Uri.parse('file:///path/to/foo')); expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); expect(context.toUri('http://dartlang.org/path/to/foo%23bar'), Uri.parse('http://dartlang.org/path/to/foo%23bar')); @@ -755,8 +747,7 @@ main() { 'http://dartlang.org/other/path/a/b'); expect(context.prettyUri('http://pub.dartlang.org/root/path'), 'http://pub.dartlang.org/root/path'); - expect(context.prettyUri('http://dartlang.org/root/other'), - '../other'); + expect(context.prettyUri('http://dartlang.org/root/other'), '../other'); }); test('with a relative URI', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 1f5dc387..a174305a 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -10,8 +10,8 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var context = new path.Context(style: path.Style.windows, - current: r'C:\root\path'); + var context = + new path.Context(style: path.Style.windows, current: r'C:\root\path'); group('separator', () { expect(context.separator, '\\'); @@ -247,7 +247,7 @@ main() { test('join does not modify internal ., .., or trailing separators', () { expect(context.join('a/', 'b/c/'), 'a/b/c/'); expect(context.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), - r'a\b\./c\..\\d\..\.\..\\e\f\\'); + r'a\b\./c\..\\d\..\.\..\\e\f\\'); expect(context.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); expect(context.join(r'a', 'b${context.separator}'), r'a\b\'); }); @@ -301,8 +301,8 @@ main() { equals([r'\\server\share', 'foo', 'bar', 'baz'])); expect(context.split(r'\\server\share'), equals([r'\\server\share'])); - expect(context.split(r'\foo\bar\baz'), - equals([r'\', 'foo', 'bar', 'baz'])); + expect( + context.split(r'\foo\bar\baz'), equals([r'\', 'foo', 'bar', 'baz'])); expect(context.split(r'\'), equals([r'\'])); }); }); @@ -321,7 +321,7 @@ main() { expect(context.normalize(r'C:\'), r'C:\'); expect(context.normalize(r'\\server\share'), r'\\server\share'); expect(context.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), - 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); + 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); }); test('collapses redundant separators', () { @@ -350,8 +350,8 @@ main() { expect(context.normalize(r'..\..\..'), r'..\..\..'); expect(context.normalize(r'../..\..\'), r'..\..\..'); expect(context.normalize(r'\\server\share\..'), r'\\server\share'); - expect(context.normalize(r'\\server\share\..\../..\a'), - r'\\server\share\a'); + expect( + context.normalize(r'\\server\share\..\../..\a'), r'\\server\share\a'); expect(context.normalize(r'c:\..'), r'c:\'); expect(context.normalize(r'A:/..\..\..'), r'A:\'); expect(context.normalize(r'b:\..\..\..\a'), r'b:\a'); @@ -483,16 +483,16 @@ main() { }); test('from a root with extension', () { - var r = new path.Context( - style: path.Style.windows, current: r'C:\dir.ext'); + var r = + new path.Context(style: path.Style.windows, current: r'C:\dir.ext'); expect(r.relative(r'C:\dir.ext\file'), 'file'); }); test('with a root parameter', () { expect(context.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); - expect(context.relative('..', from: r'C:\foo\bar'), - equals(r'..\..\root')); + expect( + context.relative('..', from: r'C:\foo\bar'), equals(r'..\..\root')); expect(context.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); expect(context.relative(r'C:\foo\bar\baz', from: r'foo\bar'), equals(r'..\..\..\..\foo\bar\baz')); @@ -553,8 +553,8 @@ main() { expect(context.absolute('a', 'b'), r'C:\root\path\a\b'); expect(context.absolute('a', 'b', 'c'), r'C:\root\path\a\b\c'); expect(context.absolute('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); - expect(context.absolute('a', 'b', 'c', 'd', 'e'), - r'C:\root\path\a\b\c\d\e'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e'), r'C:\root\path\a\b\c\d\e'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), r'C:\root\path\a\b\c\d\e\f'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), @@ -600,18 +600,18 @@ main() { expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), r'\\server\share\path\to\foo'); expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); - expect(context.fromUri(Uri.parse('file://server/share')), - r'\\server\share'); + expect( + context.fromUri(Uri.parse('file://server/share')), r'\\server\share'); expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); expect(context.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); - expect(context.fromUri(Uri.parse('///C:/path/to/foo')), - r'C:\path\to\foo'); + expect( + context.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); expect(context.fromUri(Uri.parse('//server/share/path/to/foo')), r'\\server\share\path\to\foo'); expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), r'C:\path\to\foo#bar'); - expect(context.fromUri( - Uri.parse('file://server/share/path/to/foo%23bar')), + expect( + context.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), r'\\server\share\path\to\foo#bar'); expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); @@ -625,14 +625,14 @@ main() { }); test('toUri', () { - expect(context.toUri(r'C:\path\to\foo'), - Uri.parse('file:///C:/path/to/foo')); + expect( + context.toUri(r'C:\path\to\foo'), Uri.parse('file:///C:/path/to/foo')); expect(context.toUri(r'C:\path\to\foo\'), Uri.parse('file:///C:/path/to/foo/')); expect(context.toUri(r'C:\'), Uri.parse('file:///C:/')); expect(context.toUri(r'\\server\share'), Uri.parse('file://server/share')); - expect(context.toUri(r'\\server\share\'), - Uri.parse('file://server/share/')); + expect( + context.toUri(r'\\server\share\'), Uri.parse('file://server/share/')); expect(context.toUri(r'foo\bar'), Uri.parse('foo/bar')); expect(context.toUri(r'C:\path\to\foo#bar'), Uri.parse('file:///C:/path/to/foo%23bar')); @@ -648,12 +648,11 @@ main() { test('with a file: URI', () { expect(context.prettyUri('file:///C:/root/path/a/b'), r'a\b'); expect(context.prettyUri('file:///C:/root/path/a/../b'), r'b'); - expect(context.prettyUri('file:///C:/other/path/a/b'), - r'C:\other\path\a\b'); - expect(context.prettyUri('file:///D:/root/path/a/b'), - r'D:\root\path\a\b'); - expect(context.prettyUri('file:///C:/root/other'), - r'..\other'); + expect( + context.prettyUri('file:///C:/other/path/a/b'), r'C:\other\path\a\b'); + expect( + context.prettyUri('file:///D:/root/path/a/b'), r'D:\root\path\a\b'); + expect(context.prettyUri('file:///C:/root/other'), r'..\other'); }); test('with an http: URI', () { From 441de9299e411567dd503f3e0213343c4c14e174 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 29 Jan 2015 12:47:26 -0800 Subject: [PATCH 061/183] path: Fix some analyzer hints. R=rnystrom@google.com Review URL: https://codereview.chromium.org//883133002 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 1cc7127c..b36faedc 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.2 + +* Fix some analyzer hints. + ## 1.3.1 * Add a number of performance improvements. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 3455ce38..4974049d 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.2-dev +version: 1.3.2 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 0cada32e3524c57465bcbaefe5d703be9cb33d1f Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Wed, 18 Feb 2015 09:40:49 -0800 Subject: [PATCH 062/183] Don't use 'current' if 'from' is not relative. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//937683002 --- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/lib/src/context.dart | 8 +++++++- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/relative_test.dart | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index b36faedc..e5f7b440 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.3.3 + +* Performance improvement in `Context.relative` - don't call `current` if `from` + is not relative. + ## 1.3.2 * Fix some analyzer hints. diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index a3efd95a..c2fe48d0 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -324,7 +324,13 @@ class Context { /// "/", no path can be determined. In this case, a [PathException] will be /// thrown. String relative(String path, {String from}) { - from = from == null ? current : this.join(current, from); + // Avoid calling [current] since it is slow and calling join() when + // [from] is absolute does nothing. + if (from == null) { + from = current; + } else if (this.isRelative(from) || this.isRootRelative(from)) { + from = this.join(current, from); + } // We can't determine the path from a relative path to an absolute path. if (this.isRelative(from) && this.isAbsolute(path)) { diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 4974049d..b4df1b6f 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.2 +version: 1.3.3 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 4523c8d7..57a90463 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -31,7 +31,7 @@ void relativeTest(path.Context context, String prefix) { var isRelative = (context.current == '.'); // Cases where the arguments are absolute paths. expectRelative(result, pathArg, fromArg) { - expect(context.normalize(result), context.relative(pathArg, from: fromArg)); + expect(context.relative(pathArg, from: fromArg), context.normalize(result)); } expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); From 96b4f884215af55f26821590498c12990888e2f5 Mon Sep 17 00:00:00 2001 From: Vijay Menon Date: Fri, 27 Mar 2015 15:30:09 -0700 Subject: [PATCH 063/183] DDC fixes for path R=rnystrom@google.com Review URL: https://codereview.chromium.org//1038063002 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/src/context.dart | 15 ++++++++++++--- pkgs/path/lib/src/parsed_path.dart | 8 ++++---- pkgs/path/pubspec.yaml | 2 +- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index e5f7b440..4eb8ec95 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.4 + +* Fix dev_compiler warnings. + ## 1.3.3 * Performance improvement in `Context.relative` - don't call `current` if `from` diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index c2fe48d0..db055a19 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -40,12 +40,12 @@ class Context { "allowed."); } - return new Context._(style, current); + return new Context._(style as InternalStyle, current); } /// Create a [Context] to be used internally within path. Context._internal() - : style = Style.platform, + : style = Style.platform as InternalStyle, _current = null; Context._(this.style, this._current); @@ -197,7 +197,16 @@ class Context { /// String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) { - var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + var parts = [ + part1, + part2, + part3, + part4, + part5, + part6, + part7, + part8 + ]; _validateArgList("join", parts); return joinAll(parts.where((part) => part != null)); } diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index b853eb1a..d37e2d39 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -47,8 +47,8 @@ class ParsedPath { if (root != null) path = path.substring(root.length); // Split the parts on path separators. - var parts = []; - var separators = []; + var parts = []; + var separators = []; var start = 0; @@ -102,7 +102,7 @@ class ParsedPath { void normalize() { // Handle '.', '..', and empty parts. var leadingDoubles = 0; - var newParts = []; + var newParts = []; for (var part in parts) { if (part == '.' || part == '') { // Do nothing. Ignore it. @@ -130,7 +130,7 @@ class ParsedPath { } // Canonicalize separators. - var newSeparators = new List.generate( + var newSeparators = new List.generate( newParts.length, (_) => style.separator, growable: true); newSeparators.insert(0, isAbsolute && newParts.length > 0 && diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index b4df1b6f..9c837e91 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.3 +version: 1.3.4 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 34f084362dc8d9e382966c5805b6d63b5d7846cd Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 9 Apr 2015 13:10:07 -0700 Subject: [PATCH 064/183] Add type annotations to top-level and static fields. R=nweiz@google.com Review URL: https://codereview.chromium.org//1064273002 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/path.dart | 6 +++--- pkgs/path/lib/src/style.dart | 8 ++++---- pkgs/path/pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 4eb8ec95..ea38c190 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.5 + +* Added type annotations to top-level and static fields. + ## 1.3.4 * Fix dev_compiler warnings. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index cfbaf11f..af9efe54 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -54,13 +54,13 @@ export 'src/path_exception.dart'; export 'src/style.dart'; /// A default context for manipulating POSIX paths. -final posix = new Context(style: Style.posix); +final Context posix = new Context(style: Style.posix); /// A default context for manipulating Windows paths. -final windows = new Context(style: Style.windows); +final Context windows = new Context(style: Style.windows); /// A default context for manipulating URLs. -final url = new Context(style: Style.url); +final Context url = new Context(style: Style.url); /// The system path context. /// diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index a94ec68c..e0e0e01b 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -13,14 +13,14 @@ import 'style/windows.dart'; abstract class Style { /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths /// start with "/". Used by UNIX, Linux, Mac OS X, and others. - static final posix = new PosixStyle(); + static final Style posix = new PosixStyle(); /// Windows paths use "\" (backslash) as separators. Absolute paths start with /// a drive letter followed by a colon (example, "C:") or two backslashes /// ("\\") for UNC paths. // TODO(rnystrom): The UNC root prefix should include the drive name too, not // just the "\\". - static final windows = new WindowsStyle(); + static final Style windows = new WindowsStyle(); /// URLs aren't filesystem paths, but they're supported to make it easier to /// manipulate URL paths in the browser. @@ -28,13 +28,13 @@ abstract class Style { /// URLs use "/" (forward slash) as separators. Absolute paths either start /// with a protocol and optional hostname (e.g. `http://dartlang.org`, /// `file://`) or with "/". - static final url = new UrlStyle(); + static final Style url = new UrlStyle(); /// The style of the host platform. /// /// When running on the command line, this will be [windows] or [posix] based /// on the host operating system. On a browser, this will be [url]. - static final platform = _getPlatformStyle(); + static final Style platform = _getPlatformStyle(); /// Gets the type of the host platform. static Style _getPlatformStyle() { diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 9c837e91..835129ac 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.4 +version: 1.3.5 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From ebe9af732f7b4768ba3afe542fe7eb690e75de5f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 9 Jul 2015 13:49:03 -0700 Subject: [PATCH 065/183] Fix path.toUri for relative paths. It wasn't preserving trailing slashes, which was a problem when passing packageRoot URLs to dart2js. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1225373003 . --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/src/internal_style.dart | 10 ++++++++-- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 1 + pkgs/path/test/url_test.dart | 1 + pkgs/path/test/windows_test.dart | 1 + 6 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index ea38c190..457be806 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.6 + +* Ensure that `path.toUri` preserves trailing slashes for relative paths. + ## 1.3.5 * Added type annotations to top-level and static fields. diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index db2d3462..549f95ba 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -57,8 +57,14 @@ abstract class InternalStyle extends Style { String pathFromUri(Uri uri); /// Returns the URI that represents the relative path made of [parts]. - Uri relativePathToUri(String path) => - new Uri(pathSegments: context.split(path)); + Uri relativePathToUri(String path) { + var segments = context.split(path); + + // Ensure that a trailing slash in the path produces a trailing slash in the + // URL. + if (isSeparator(path.codeUnitAt(path.length - 1))) segments.add(''); + return new Uri(pathSegments: segments); + } /// Returns the URI that represents [path], which is assumed to be absolute. Uri absolutePathToUri(String path); diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 835129ac..6ba3a153 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.5 +version: 1.3.6 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 25e34199..2368d999 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -499,6 +499,7 @@ main() { test('toUri', () { expect(context.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); expect(context.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); + expect(context.toUri('path/to/foo/'), Uri.parse('path/to/foo/')); expect(context.toUri('/'), Uri.parse('file:///')); expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); expect(context.toUri('/path/to/foo#bar'), diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index aca161b4..854e19ab 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -721,6 +721,7 @@ main() { Uri.parse('http://dartlang.org/path/to/foo')); expect(context.toUri('http://dartlang.org/path/to/foo/'), Uri.parse('http://dartlang.org/path/to/foo/')); + expect(context.toUri('path/to/foo/'), Uri.parse('path/to/foo/')); expect( context.toUri('file:///path/to/foo'), Uri.parse('file:///path/to/foo')); expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index a174305a..69428378 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -629,6 +629,7 @@ main() { context.toUri(r'C:\path\to\foo'), Uri.parse('file:///C:/path/to/foo')); expect(context.toUri(r'C:\path\to\foo\'), Uri.parse('file:///C:/path/to/foo/')); + expect(context.toUri(r'path\to\foo\'), Uri.parse('path/to/foo/')); expect(context.toUri(r'C:\'), Uri.parse('file:///C:/')); expect(context.toUri(r'\\server\share'), Uri.parse('file://server/share')); expect( From e992887804d4446d484fdaf0c63707b3ad6e9ec7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 16 Jul 2015 13:27:47 -0700 Subject: [PATCH 066/183] Upgrade to the new test runner. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1235823009 . --- pkgs/path/.gitignore | 1 + pkgs/path/.status | 21 --------------------- pkgs/path/.test_config | 3 +++ pkgs/path/pubspec.yaml | 4 ++-- pkgs/path/test/browser_test.dart | 7 +++---- pkgs/path/test/io_test.dart | 4 +++- pkgs/path/test/path_test.dart | 2 +- pkgs/path/test/posix_test.dart | 2 +- pkgs/path/test/relative_test.dart | 2 +- pkgs/path/test/url_test.dart | 2 +- pkgs/path/test/utils.dart | 2 +- pkgs/path/test/windows_test.dart | 4 ++-- 12 files changed, 19 insertions(+), 35 deletions(-) delete mode 100644 pkgs/path/.status create mode 100644 pkgs/path/.test_config diff --git a/pkgs/path/.gitignore b/pkgs/path/.gitignore index 388eff0b..7dbf0350 100644 --- a/pkgs/path/.gitignore +++ b/pkgs/path/.gitignore @@ -3,6 +3,7 @@ .pub/ build/ packages +.packages # Or the files created by dart2js. *.dart.js diff --git a/pkgs/path/.status b/pkgs/path/.status deleted file mode 100644 index bfc58fc1..00000000 --- a/pkgs/path/.status +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -# for details. All rights reserved. Use of this source code is governed by a -# BSD-style license that can be found in the LICENSE file. - -# Skip non-test files ending with "_test". -packages/*: Skip -*/packages/*: Skip -*/*/packages/*: Skip -*/*/*/packages/*: Skip -*/*/*/*packages/*: Skip -*/*/*/*/*packages/*: Skip - -# Only run tests from the build directory, since we don't care about the -# difference between transformed an untransformed code. -test/*: Skip - -[ $browser ] -build/test/io_test: Fail, OK # Uses dart:io. - -[ $runtime == vm ] -build/test/browser_test: Fail, OK # Uses dart:html diff --git a/pkgs/path/.test_config b/pkgs/path/.test_config new file mode 100644 index 00000000..412fc5c5 --- /dev/null +++ b/pkgs/path/.test_config @@ -0,0 +1,3 @@ +{ + "test_package": true +} \ No newline at end of file diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 6ba3a153..e6698448 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.6 +version: 1.3.7-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -7,6 +7,6 @@ description: > machines. homepage: http://github.com/dart-lang/path dev_dependencies: - unittest: ">=0.9.0 <0.12.0" + test: ">=0.12.0 <0.13.0" environment: sdk: ">=1.0.0 <2.0.0" diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 0b56c7ac..b3291469 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -2,15 +2,14 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +@TestOn('browser') + import 'dart:html'; -import 'package:unittest/unittest.dart'; -import 'package:unittest/html_config.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; main() { - useHtmlConfiguration(); - group('new Context()', () { test('uses the window location if root and style are omitted', () { var context = new path.Context(); diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index e8555222..f15f82dd 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -2,9 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +@TestOn('vm') + import 'dart:io' as io; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; main() { diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index 19397cf8..b45f8dca 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; main() { diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 2368d999..d6d7a90e 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -4,7 +4,7 @@ library path.test.posix_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; import 'utils.dart'; diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 57a90463..3cf02dcb 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -4,7 +4,7 @@ // // Test "relative" on all styles of path.Context, on all platforms. -import "package:unittest/unittest.dart"; +import "package:test/test.dart"; import "package:path/path.dart" as path; import "utils.dart"; diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 854e19ab..1188129f 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; main() { diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 1730798f..7d917a0b 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -4,7 +4,7 @@ library path.test.utils; -import "package:unittest/unittest.dart"; +import "package:test/test.dart"; import "package:path/path.dart" as path; /// A matcher for a closure that throws a [path.PathException]. diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 69428378..a32f1fec 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -4,7 +4,7 @@ library path.test.windows_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:path/path.dart' as path; import 'utils.dart'; @@ -13,7 +13,7 @@ main() { var context = new path.Context(style: path.Style.windows, current: r'C:\root\path'); - group('separator', () { + test('separator', () { expect(context.separator, '\\'); }); From f25964c4d4f2452ddbb069901beb8ef3455c9fad Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 17 Nov 2015 16:03:56 -0800 Subject: [PATCH 067/183] Optimize absolute() and normalize(). This short-circuits absolute() if the path is already absolute. Detecting absolute paths is very fast, whereas join() is relatively slow. For normalize(), this runs a single-pass check over a path to see whether it needs to be normalized at all. This check isn't free, but it's a lot less costly than parsing and reserializing the path, and it covers the common case. See dart-lang/globdart-lang/path#24962 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1455003002 . --- pkgs/path/CHANGELOG.md | 4 ++ pkgs/path/benchmark/benchmark.dart | 1 + pkgs/path/lib/src/context.dart | 78 ++++++++++++++++++++++++++++++ pkgs/path/pubspec.yaml | 2 +- 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 457be806..1dfc09a9 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.7 + +* Improve the performance of `absolute()` and `normalize()`. + ## 1.3.6 * Ensure that `path.toUri` preserves trailing slashes for relative paths. diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 419eee0f..03871d0c 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -33,6 +33,7 @@ main(args) { } } + benchmark('absolute', context.absolute); benchmark('basename', context.basename); benchmark('basenameWithoutExtension', context.basenameWithoutExtension); benchmark('dirname', context.dirname); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index db055a19..44bdde99 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -4,6 +4,7 @@ library path.context; +import 'characters.dart' as chars; import 'internal_style.dart'; import 'style.dart'; import 'parsed_path.dart'; @@ -73,6 +74,15 @@ class Context { /// If [current] isn't absolute, this won't return an absolute path. String absolute(String part1, [String part2, String part3, String part4, String part5, String part6, String part7]) { + _validateArgList( + "absolute", [part1, part2, part3, part4, part5, part6, part7]); + + // If there's a single absolute path, just return it. This is a lot faster + // for the common case of `p.absolute(path)`. + if (part2 == null && isAbsolute(part1) && !isRootRelative(part1)) { + return part1; + } + return join(current, part1, part2, part3, part4, part5, part6, part7); } @@ -295,11 +305,79 @@ class Context { /// /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) { + if (!_needsNormalization(path)) return path; + var parsed = _parse(path); parsed.normalize(); return parsed.toString(); } + /// Returns whether [path] needs to be normalized. + bool _needsNormalization(String path) { + var start = 0; + var codeUnits = path.codeUnits; + var previousPrevious; + var previous; + + // Skip past the root before we start looking for snippets that need + // normalization. We want to normalize "//", but not when it's part of + // "http://". + var root = style.rootLength(path); + if (root != 0) { + start = root; + previous = chars.SLASH; + + // On Windows, the root still needs to be normalized if it contains a + // forward slash. + if (style == Style.windows) { + for (var i = 0; i < root; i++) { + if (codeUnits[i] == chars.SLASH) return true; + } + } + } + + for (var i = start; i < codeUnits.length; i++) { + var codeUnit = codeUnits[i]; + if (style.isSeparator(codeUnit)) { + // Forward slashes in Windows paths are normalized to backslashes. + if (style == Style.windows && codeUnit == chars.SLASH) return true; + + // Multiple separators are normalized to single separators. + if (previous != null && style.isSeparator(previous)) return true; + + // Single dots and double dots are normalized to directory traversals. + // + // This can return false positives for ".../", but that's unlikely + // enough that it's probably not going to cause performance issues. + if (previous == chars.PERIOD && + (previousPrevious == null || + previousPrevious == chars.PERIOD || + style.isSeparator(previousPrevious))) { + return true; + } + } + + previousPrevious = previous; + previous = codeUnit; + } + + // Empty paths are normalized to ".". + if (previous == null) return true; + + // Trailing separators are removed. + if (style.isSeparator(previous)) return true; + + // Single dots and double dots are normalized to directory traversals. + if (previous == chars.PERIOD && + (previousPrevious == null || + previousPrevious == chars.SLASH || + previousPrevious == chars.PERIOD)) { + return true; + } + + return false; + } + /// Attempts to convert [path] to an equivalent relative path relative to /// [root]. /// diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index e6698448..bc7ab988 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.7-dev +version: 1.3.7 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From e4511876a8fbfceff239d13d8ac79f5bc2dda8dc Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 30 Nov 2015 14:53:09 -0800 Subject: [PATCH 068/183] Cache the current working directory. Recomputing this isn't terribly expensive, but it happens a lot so it's nice to avoid. Also avoid calling it in relative() when the path is already relative. This brings current from about 0.070 iterations/us to about 0.097, and relative from about 0.080 to about 1.589. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1473563003 . --- pkgs/path/benchmark/benchmark.dart | 4 ++++ pkgs/path/lib/path.dart | 25 ++++++++++++++++++++++--- pkgs/path/lib/src/context.dart | 3 +++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 03871d0c..8dc2690a 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -47,6 +47,10 @@ main(args) { benchmark('toUri', context.toUri); benchmark('prettyUri', context.prettyUri); } + + if (args.isEmpty || args.any((arg) => arg == 'current')) { + runBenchmark('current', (_) => path.current, [null]); + } } const COMMON_PATHS = const ['.', '..', 'out/ReleaseIA32/packages']; diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index af9efe54..93fe67e4 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -79,17 +79,36 @@ Style get style => context.style; /// In the browser, this means the current URL, without the last file segment. String get current { var uri = Uri.base; + + // Converting the base URI to a file path is pretty slow, and the base URI + // rarely changes in practice, so we cache the result here. + if (uri == _currentUriBase) return _current; + _currentUriBase = uri; + if (Style.platform == Style.url) { - return uri.resolve('.').toString(); + _current = uri.resolve('.').toString(); + return _current; } else { var path = uri.toFilePath(); // Remove trailing '/' or '\'. - int lastIndex = path.length - 1; + var lastIndex = path.length - 1; assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); - return path.substring(0, lastIndex); + _current = path.substring(0, lastIndex); + return _current; } } +/// The last value returned by [Uri.base]. +/// +/// This is used to cache the current working directory. +Uri _currentUriBase; + +/// The last known value of the current working directory. +/// +/// This is cached because [current] is called frequently but rarely actually +/// changes. +String _current; + /// Gets the path separator for the current platform. This is `\` on Windows /// and `/` on other platforms (including the browser). String get separator => context.separator; diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 44bdde99..a05ffda6 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -411,6 +411,9 @@ class Context { /// "/", no path can be determined. In this case, a [PathException] will be /// thrown. String relative(String path, {String from}) { + // Avoid expensive computation if the path is already relative. + if (from == null && this.isRelative(path)) return path; + // Avoid calling [current] since it is slow and calling join() when // [from] is absolute does nothing. if (from == null) { From 18539f8387fae5e39ee71ac633bf48b8ddd009e4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 30 Nov 2015 20:09:17 -0800 Subject: [PATCH 069/183] Make sure we normalize relative()'s return value. I'm not sure why relative() does this, but it's encoded in the tests so we may break people if we change it. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1491503002 . --- pkgs/path/lib/src/context.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index a05ffda6..5331e370 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -412,7 +412,7 @@ class Context { /// thrown. String relative(String path, {String from}) { // Avoid expensive computation if the path is already relative. - if (from == null && this.isRelative(path)) return path; + if (from == null && this.isRelative(path)) return this.normalize(path); // Avoid calling [current] since it is slow and calling join() when // [from] is absolute does nothing. From 51b50cd9daa5e334e5fe203649aa3862e3f0c9ca Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 30 Nov 2015 20:18:51 -0800 Subject: [PATCH 070/183] Improve the performance of isWithin(). This adds a single-pass fast path function that only bails when it encounters "." or ".." components in one path but not the other. This brings isWithin() from about 0.008 iterations/us to about 0.0180 by the repo's benchmark. It makes it about 11.5x faster on data provided by @scheglov (see below), which consists of absolute paths that mostly do not contain one another. Data: https://github.com/dart-lang/path/issues/7#issuecomment-157856146 Closes dart-lang/path#7 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1468343002 . --- pkgs/path/CHANGELOG.md | 11 ++ pkgs/path/benchmark/benchmark.dart | 33 +++- pkgs/path/lib/src/context.dart | 268 ++++++++++++++++++++++++++++- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 13 ++ pkgs/path/test/url_test.dart | 25 +++ pkgs/path/test/windows_test.dart | 24 +++ 7 files changed, 367 insertions(+), 9 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 1dfc09a9..aee24241 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,14 @@ +## 1.3.8 + +* Improve the performance of `isWithin()` when the paths don't contain + asymmetrical `.` or `..` components. + +* Improve the performance of `relative()` when `from` is `null` and the path is + already relative. + +* Improve the performance of `current` when the current directory hasn't + changed. + ## 1.3.7 * Improve the performance of `absolute()` and `normalize()`. diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 8dc2690a..4285e653 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -21,18 +21,47 @@ void runBenchmark(String name, Function func, List files) { print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})"); } +void runBenchmarkTwoArgs(String name, Function func, List files) { + // Warmup. + for (int i = 0; i < 1000; i++) { + for (var file1 in files) { + for (var file2 in files) { + func(file1, file2); + } + } + } + + var count = 10000; + var sw = new Stopwatch()..start(); + for (int i = 0; i < count; i++) { + for (var file1 in files) { + for (var file2 in files) { + func(file1, file2); + } + } + } + print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})"); +} + main(args) { for (var style in [path.Style.posix, path.Style.url, path.Style.windows]) { var context = new path.Context(style: style); var files = COMMON_PATHS.toList()..addAll(STYLE_PATHS[style]); - void benchmark(name, func) { + benchmark(name, func) { name = style.name + '-' + name; if (args.isEmpty || args.any((arg) => name.contains(arg))) { runBenchmark(name, func, files); } } + benchmarkTwoArgs(name, func) { + name = style.name + '-' + name + '-two'; + if (args.isEmpty || args.any((arg) => name.contains(arg))) { + runBenchmarkTwoArgs(name, func, files); + } + } + benchmark('absolute', context.absolute); benchmark('basename', context.basename); benchmark('basenameWithoutExtension', context.basenameWithoutExtension); @@ -44,8 +73,10 @@ main(args) { benchmark('isRootRelative', context.isRootRelative); benchmark('normalize', context.normalize); benchmark('relative', context.relative); + benchmarkTwoArgs('relative', context.relative); benchmark('toUri', context.toUri); benchmark('prettyUri', context.prettyUri); + benchmarkTwoArgs('isWithin', context.isWithin); } if (args.isEmpty || args.any((arg) => arg == 'current')) { diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 5331e370..b19aa71e 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -414,13 +414,7 @@ class Context { // Avoid expensive computation if the path is already relative. if (from == null && this.isRelative(path)) return this.normalize(path); - // Avoid calling [current] since it is slow and calling join() when - // [from] is absolute does nothing. - if (from == null) { - from = current; - } else if (this.isRelative(from) || this.isRootRelative(from)) { - from = this.join(current, from); - } + from = from == null ? current : absolute(from); // We can't determine the path from a relative path to an absolute path. if (this.isRelative(from) && this.isAbsolute(path)) { @@ -506,6 +500,31 @@ class Context { /// path.isWithin('/root/path', '/root/other'); // -> false /// path.isWithin('/root/path', '/root/path'); // -> false bool isWithin(String parent, String child) { + // Make both paths the same level of relative. We're only able to do the + // quick comparison if both paths are in the same format, and making a path + // absolute is faster than making it relative. + var parentIsAbsolute = isAbsolute(parent); + var childIsAbsolute = isAbsolute(child); + if (parentIsAbsolute && !childIsAbsolute) { + child = absolute(child); + if (style.isRootRelative(parent)) parent = absolute(parent); + } else if (childIsAbsolute && !parentIsAbsolute) { + parent = absolute(parent); + if (style.isRootRelative(child)) child = absolute(child); + } else if (childIsAbsolute && parentIsAbsolute) { + var childIsRootRelative = style.isRootRelative(child); + var parentIsRootRelative = style.isRootRelative(parent); + + if (childIsRootRelative && !parentIsRootRelative) { + child = absolute(child); + } else if (parentIsRootRelative && !childIsRootRelative) { + parent = absolute(parent); + } + } + + var fastResult = _isWithinFast(parent, child); + if (fastResult != null) return fastResult; + var relative; try { relative = this.relative(child, from: parent); @@ -521,6 +540,214 @@ class Context { parts.first != '.'; } + /// An optimized implementation of [isWithin] that doesn't handle a few + /// complex cases. + bool _isWithinFast(String parent, String child) { + // Normally we just bail when we see "." path components, but we can handle + // a single dot easily enough. + if (parent == '.') parent = ''; + + var parentRootLength = style.rootLength(parent); + var childRootLength = style.rootLength(child); + + // If the roots aren't the same length, we know both paths are absolute or + // both are root-relative, and thus that the roots are meaningfully + // different. + // + // isWithin("C:/bar", "//foo/bar/baz") //=> false + // isWithin("http://example.com/", "http://google.com/bar") //=> false + if (parentRootLength != childRootLength) return false; + + var parentCodeUnits = parent.codeUnits; + var childCodeUnits = child.codeUnits; + + // Make sure that the roots are textually the same as well. + // + // isWithin("C:/bar", "D:/bar/baz") //=> false + // isWithin("http://example.com/", "http://example.org/bar") //=> false + for (var i = 0; i < parentRootLength; i++) { + var parentCodeUnit = parentCodeUnits[i]; + var childCodeUnit = childCodeUnits[i]; + if (parentCodeUnit == childCodeUnit) continue; + + // If both code units are separators, that's fine too. + // + // isWithin("C:/", r"C:\foo") //=> true + if (!style.isSeparator(parentCodeUnit) || + !style.isSeparator(childCodeUnit)) { + return false; + } + } + + // Start by considering the last code unit as a separator, since + // semantically we're starting at a new path component even if we're + // comparing relative paths. + var lastCodeUnit = chars.SLASH; + + // Iterate through both paths as long as they're semantically identical. + var parentIndex = parentRootLength; + var childIndex = childRootLength; + while (parentIndex < parent.length && childIndex < child.length) { + var parentCodeUnit = parentCodeUnits[parentIndex]; + var childCodeUnit = childCodeUnits[childIndex]; + if (parentCodeUnit == childCodeUnit) { + lastCodeUnit = parentCodeUnit; + parentIndex++; + childIndex++; + continue; + } + + // Different separators are considered identical. + var parentIsSeparator = style.isSeparator(parentCodeUnit); + var childIsSeparator = style.isSeparator(childCodeUnit); + if (parentIsSeparator && childIsSeparator) { + lastCodeUnit = parentCodeUnit; + parentIndex++; + childIndex++; + continue; + } + + // Ignore multiple separators in a row. + if (parentIsSeparator && style.isSeparator(lastCodeUnit)) { + parentIndex++; + continue; + } else if (childIsSeparator && style.isSeparator(lastCodeUnit)) { + childIndex++; + continue; + } + + // If a dot comes after a separator or another dot, it may be a + // directory traversal operator. Otherwise, it's just a normal + // non-matching character. + // + // isWithin("foo/./bar", "foo/bar/baz") //=> true + // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false + // + // We could stay on the fast path for "/./", but that adds a lot of + // complexity and isn't likely to come up much in practice. + if ((parentCodeUnit == chars.PERIOD || childCodeUnit == chars.PERIOD) && + (style.isSeparator(lastCodeUnit) || lastCodeUnit == chars.PERIOD)) { + return null; + } + + // If we're here, we've hit two non-matching, non-significant characters. + // As long as the remainders of the two paths don't have any unresolved + // ".." components, we can be confident that [child] is not within + // [parent]. + var childDirection = _pathDirection(childCodeUnits, childIndex); + if (childDirection != _PathDirection.belowRoot) return null; + var parentDirection = _pathDirection(parentCodeUnits, parentIndex); + if (parentDirection != _PathDirection.belowRoot) return null; + + return false; + } + + // If the child is shorter than the parent, it's probably not within the + // parent. The only exception is if the parent has some weird ".." stuff + // going on, in which case we do the slow check. + // + // isWithin("foo/bar/baz", "foo/bar") //=> false + // isWithin("foo/bar/baz/../..", "foo/bar") //=> true + if (childIndex == child.length) { + var direction = _pathDirection(parentCodeUnits, parentIndex); + return direction == _PathDirection.aboveRoot ? null : false; + } + + // We've reached the end of the parent path, which means it's time to make a + // decision. Before we do, though, we'll check the rest of the child to see + // what that tells us. + var direction = _pathDirection(childCodeUnits, childIndex); + + // If there are no more components in the child, then it's the same as + // the parent, not within it. + // + // isWithin("foo/bar", "foo/bar") //=> false + // isWithin("foo/bar", "foo/bar//") //=> false + if (direction == _PathDirection.atRoot) return false; + + // If there are unresolved ".." components in the child, no decision we make + // will be valid. We'll abort and do the slow check instead. + // + // isWithin("foo/bar", "foo/bar/..") //=> false + // isWithin("foo/bar", "foo/bar/baz/bang/../../..") //=> false + // isWithin("foo/bar", "foo/bar/baz/bang/../../../bar/baz") //=> true + if (direction == _PathDirection.aboveRoot) return null; + + // The child is within the parent if and only if we're on a separator + // boundary. + // + // isWithin("foo/bar", "foo/bar/baz") //=> true + // isWithin("foo/bar/", "foo/bar/baz") //=> true + // isWithin("foo/bar", "foo/barbaz") //=> false + return style.isSeparator(childCodeUnits[childIndex]) || + style.isSeparator(lastCodeUnit); + } + + // Returns a [_PathDirection] describing the path represented by [codeUnits] + // after [index]. + // + // This ignores leading separators. + // + // pathDirection("foo") //=> below root + // pathDirection("foo/bar/../baz") //=> below root + // pathDirection("//foo/bar/baz") //=> below root + // pathDirection("/") //=> at root + // pathDirection("foo/..") //=> at root + // pathDirection("foo/../baz") //=> reaches root + // pathDirection("foo/../..") //=> above root + // pathDirection("foo/../../foo/bar/baz") //=> above root + _PathDirection _pathDirection(List codeUnits, int index) { + var depth = 0; + var reachedRoot = false; + var i = index; + while (i < codeUnits.length) { + // Ignore initial separators or doubled separators. + while (i < codeUnits.length && style.isSeparator(codeUnits[i])) { + i++; + } + + // If we're at the end, stop. + if (i == codeUnits.length) break; + + // Move through the path component to the next separator. + var start = i; + while (i < codeUnits.length && !style.isSeparator(codeUnits[i])) { + i++; + } + + // See if the path component is ".", "..", or a name. + if (i - start == 1 && codeUnits[start] == chars.PERIOD) { + // Don't change the depth. + } else if (i - start == 2 && + codeUnits[start] == chars.PERIOD && + codeUnits[start + 1] == chars.PERIOD) { + // ".." backs out a directory. + depth--; + + // If we work back beyond the root, stop. + if (depth < 0) break; + + // Record that we reached the root so we don't return + // [_PathDirection.belowRoot]. + if (depth == 0) reachedRoot = true; + } else { + // Step inside a directory. + depth++; + } + + // If we're at the end, stop. + if (i == codeUnits.length) break; + + // Move past the separator. + i++; + } + + if (depth < 0) return _PathDirection.aboveRoot; + if (depth == 0) return _PathDirection.atRoot; + if (reachedRoot) return _PathDirection.reachesRoot; + return _PathDirection.belowRoot; + } + /// Removes a trailing extension from the last part of [path]. /// /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' @@ -653,3 +880,30 @@ _validateArgList(String method, List args) { throw new ArgumentError(message.toString()); } } + +/// An enum of possible return values for [Context._pathDirection]. +class _PathDirection { + /// The path contains enough ".." components that at some point it reaches + /// above its original root. + /// + /// Note that this applies even if the path ends beneath its original root. It + /// takes precendence over any other return values that may apple. + static const aboveRoot = const _PathDirection("above root"); + + /// The path contains enough ".." components that it ends at its original + /// root. + static const atRoot = const _PathDirection("at root"); + + /// The path contains enough ".." components that at some point it reaches its + /// original root, but it ends beneath that root. + static const reachesRoot = const _PathDirection("reaches root"); + + /// The path never reaches to or above its original root. + static const belowRoot = const _PathDirection("below root"); + + final String name; + + const _PathDirection(this.name); + + String toString() => name; +} diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index bc7ab988..69359bbf 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.7 +version: 1.3.8 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index d6d7a90e..afd6b94f 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -419,6 +419,19 @@ main() { expect(context.isWithin('baz', '/root/path/bang/baz'), isFalse); }); + test('complex cases', () { + expect(context.isWithin('foo/./bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo//bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/qux/../bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/bar/baz/../..'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar///'), isFalse); + expect(context.isWithin('foo/.bar', 'foo/.bar/baz'), isTrue); + expect(context.isWithin('foo/./bar', 'foo/.bar/baz'), isFalse); + expect(context.isWithin('foo/..bar', 'foo/..bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/bar/baz/..'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz/../qux'), isTrue); + }); + test('from a relative root', () { var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 1188129f..c81893a5 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -629,6 +629,31 @@ main() { isFalse); }); + test('complex cases', () { + expect(context.isWithin('foo/./bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo//bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/qux/../bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/bar/baz/../..'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar///'), isFalse); + expect(context.isWithin('foo/.bar', 'foo/.bar/baz'), isTrue); + expect(context.isWithin('foo/./bar', 'foo/.bar/baz'), isFalse); + expect(context.isWithin('foo/..bar', 'foo/..bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/bar/baz/..'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz/../qux'), isTrue); + expect(context.isWithin('http://example.org/', 'http://example.com/foo'), + isFalse); + expect(context.isWithin('http://example.org/', 'http://dartlang.org/foo'), + isFalse); + }); + + test('with root-relative paths', () { + expect(context.isWithin('/foo', 'http://dartlang.org/foo/bar'), isTrue); + expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); + expect(context.isWithin('/root', 'foo/bar'), isTrue); + expect(context.isWithin('foo', '/root/path/foo/bar'), isTrue); + expect(context.isWithin('/foo', '/foo/bar'), isTrue); + }); + test('from a relative root', () { var r = new path.Context(style: path.Style.url, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index a32f1fec..717043d1 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -537,6 +537,30 @@ main() { expect(context.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); }); + test('complex cases', () { + expect(context.isWithin(r'foo\.\bar', r'foo\bar\baz'), isTrue); + expect(context.isWithin(r'foo\\bar', r'foo\bar\baz'), isTrue); + expect(context.isWithin(r'foo\qux\..\bar', r'foo\bar\baz'), isTrue); + expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..\..'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar\\\'), isFalse); + expect(context.isWithin(r'foo\.bar', r'foo\.bar\baz'), isTrue); + expect(context.isWithin(r'foo\.\bar', r'foo\.bar\baz'), isFalse); + expect(context.isWithin(r'foo\..bar', r'foo\..bar\baz'), isTrue); + expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar\baz\..\qux'), isTrue); + expect(context.isWithin(r'C:\', 'C:/foo'), isTrue); + expect(context.isWithin(r'C:\', r'D:\foo'), isFalse); + expect(context.isWithin(r'C:\', r'\\foo\bar'), isFalse); + }); + + test('with root-relative paths', () { + expect(context.isWithin(r'\foo', r'C:\foo\bar'), isTrue); + expect(context.isWithin(r'C:\foo', r'\foo\bar'), isTrue); + expect(context.isWithin(r'\root', r'foo\bar'), isTrue); + expect(context.isWithin(r'foo', r'\root\path\foo\bar'), isTrue); + expect(context.isWithin(r'\foo', r'\foo\bar'), isTrue); + }); + test('from a relative root', () { var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); expect(r.isWithin('.', r'a\b\c'), isTrue); From 2d9fac03439c3b00ba0c1d14d48979de1e12a9aa Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 2 Dec 2015 14:08:33 -0800 Subject: [PATCH 071/183] Further improve isWithin() performance. On @scheglov's benchmark data, which include many path containing `.pub` or `.pub-cache`, this results in a further ~8x improvement over the improvements in 51b50cd9daa5e334e5fe203649aa3862e3f0c9ca. On the repo benchmark, this results in a further ~1.2x improvement. Closes dart-lang/path#7 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1498613002 . --- pkgs/path/CHANGELOG.md | 5 +++ pkgs/path/lib/src/context.dart | 69 ++++++++++++++++++++++++++++------ pkgs/path/pubspec.yaml | 2 +- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index aee24241..44090f64 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.3.9 + +* Further improve the performance of `isWithin()` when paths contain `/.` + sequences that aren't `/../`. + ## 1.3.8 * Improve the performance of `isWithin()` when the paths don't contain diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index b19aa71e..d10a29f1 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -616,18 +616,63 @@ class Context { continue; } - // If a dot comes after a separator or another dot, it may be a - // directory traversal operator. Otherwise, it's just a normal - // non-matching character. - // - // isWithin("foo/./bar", "foo/bar/baz") //=> true - // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false - // - // We could stay on the fast path for "/./", but that adds a lot of - // complexity and isn't likely to come up much in practice. - if ((parentCodeUnit == chars.PERIOD || childCodeUnit == chars.PERIOD) && - (style.isSeparator(lastCodeUnit) || lastCodeUnit == chars.PERIOD)) { - return null; + if (parentCodeUnit == chars.PERIOD) { + // If a dot comes after a separator, it may be a directory traversal + // operator. To check that, we need to know if it's followed by either + // "/" or "./". Otherwise, it's just a normal non-matching character. + // + // isWithin("foo/./bar", "foo/bar/baz") //=> true + // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false + if (style.isSeparator(lastCodeUnit)) { + parentIndex++; + + // We've hit "/." at the end of the parent path, which we can ignore, + // since the paths were equivalent up to this point. + if (parentIndex == parent.length) break; + parentCodeUnit = parentCodeUnits[parentIndex]; + + // We've hit "/./", which we can ignore. + if (style.isSeparator(parentCodeUnit)) { + parentIndex++; + continue; + } + + // We've hit "/..", which may be a directory traversal operator that + // we can't handle on the fast track. + if (parentCodeUnit == chars.PERIOD) { + parentIndex++; + if (parentIndex == parent.length || + style.isSeparator(parentCodeUnits[parentIndex])) { + return null; + } + } + } + + // If this isn't a directory traversal, fall through so we hit the + // normal handling for mismatched paths. + } + + // This is the same logic as above, but for the child path instead of the + // parent. + if (childCodeUnit == chars.PERIOD) { + if (style.isSeparator(lastCodeUnit)) { + childIndex++; + if (childIndex == child.length) break; + childCodeUnit = childCodeUnits[childIndex]; + + if (style.isSeparator(childCodeUnit)) { + childIndex++; + continue; + } + + if (childCodeUnit == chars.PERIOD) { + childIndex++; + if (childIndex == child.length || + style.isSeparator(childCodeUnits[childIndex])) { + return null; + } + } + } } // If we're here, we've hit two non-matching, non-significant characters. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 69359bbf..fb21d20a 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.8 +version: 1.3.9 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From c72bd3d5263edb4df4221b078629230631c51bdf Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Fri, 18 Dec 2015 14:15:21 -0800 Subject: [PATCH 072/183] Give the benchmark some love. - Get rid of a bunch of duplicate code. - Fix crash in relative(from:) test. - Add some normal looking paths to try to get more real-world results. - Other tweaks. R=nweiz@google.com Review URL: https://codereview.chromium.org//1507143002 . --- pkgs/path/benchmark/benchmark.dart | 142 +++++++++++++++-------------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 4285e653..183b921c 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -2,64 +2,59 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import '../lib/path.dart' as path; +import 'package:path/path.dart' as p; -void runBenchmark(String name, Function func, List files) { - // Warmup. - for (int i = 0; i < 10000; i++) { - for (var p in files) { - func(p); - } - } - var count = 100000; - var sw = new Stopwatch()..start(); - for (int i = 0; i < count; i++) { - for (var p in files) { - func(p); - } - } - print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})"); -} +/// Some hopefully real-world representative platform-independent paths. +const genericPaths = const [ + '.', + '..', + 'out/ReleaseIA32/packages', + 'lib', + 'lib/src/', + 'lib/src/style/url.dart', + 'test/./not/.././normalized', + 'benchmark/really/long/path/with/many/components.dart', +]; -void runBenchmarkTwoArgs(String name, Function func, List files) { - // Warmup. - for (int i = 0; i < 1000; i++) { - for (var file1 in files) { - for (var file2 in files) { - func(file1, file2); - } - } - } +/// Some platform-specific paths. +final platformPaths = { + p.Style.posix: [ + '/', + '/home/user/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart', + ], + p.Style.url: ['https://example.server.org/443643002/path?top=yes#fragment',], + p.Style.windows: [ + r'C:\User\me\', + r'\\server\share\my\folders\some\file.data', + ], +}; - var count = 10000; - var sw = new Stopwatch()..start(); - for (int i = 0; i < count; i++) { - for (var file1 in files) { - for (var file2 in files) { - func(file1, file2); - } - } - } - print("$name: ${count / sw.elapsedMicroseconds} iter/us (${sw.elapsed})"); -} +/// The command line arguments passed to this script. +List arguments; -main(args) { - for (var style in [path.Style.posix, path.Style.url, path.Style.windows]) { - var context = new path.Context(style: style); - var files = COMMON_PATHS.toList()..addAll(STYLE_PATHS[style]); +void main(List args) { + arguments = args; - benchmark(name, func) { - name = style.name + '-' + name; - if (args.isEmpty || args.any((arg) => name.contains(arg))) { - runBenchmark(name, func, files); - } + for (var style in [p.Style.posix, p.Style.url, p.Style.windows]) { + var context = new p.Context(style: style); + var files = genericPaths.toList()..addAll(platformPaths[style]); + + benchmark(name, function) { + runBenchmark("${style.name}-$name", 100000, () { + for (var file in files) { + function(file); + } + }); } - benchmarkTwoArgs(name, func) { - name = style.name + '-' + name + '-two'; - if (args.isEmpty || args.any((arg) => name.contains(arg))) { - runBenchmarkTwoArgs(name, func, files); - } + benchmarkPairs(name, function) { + runBenchmark("${style.name}-$name", 1000, () { + for (var file1 in files) { + for (var file2 in files) { + function(file1, file2); + } + } + }); } benchmark('absolute', context.absolute); @@ -73,28 +68,37 @@ main(args) { benchmark('isRootRelative', context.isRootRelative); benchmark('normalize', context.normalize); benchmark('relative', context.relative); - benchmarkTwoArgs('relative', context.relative); + benchmarkPairs('relative from', (file, from) { + try { + return context.relative(file, from: from); + } on p.PathException { + // Do nothing. + } + }); benchmark('toUri', context.toUri); benchmark('prettyUri', context.prettyUri); - benchmarkTwoArgs('isWithin', context.isWithin); + benchmarkPairs('isWithin', context.isWithin); } - if (args.isEmpty || args.any((arg) => arg == 'current')) { - runBenchmark('current', (_) => path.current, [null]); - } + runBenchmark('current', 100000, () => p.current); } -const COMMON_PATHS = const ['.', '..', 'out/ReleaseIA32/packages']; +void runBenchmark(String name, int count, Function function) { + // If names are passed on the command-line, they select which benchmarks are + // run. + if (arguments.isNotEmpty && !arguments.contains(name)) return; -final STYLE_PATHS = { - path.Style.posix: [ - '/home/user/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart', - ], - path.Style.url: [ - 'https://example.server.org/443643002/path?top=yes#fragment', - ], - path.Style.windows: [ - r'C:\User\me\', - r'\\server\share\my\folders\some\file.data', - ], -}; + // Warmup. + for (var i = 0; i < 10000; i++) { + function(); + } + + var stopwatch = new Stopwatch()..start(); + for (var i = 0; i < count; i++) { + function(); + } + + var rate = + (count / stopwatch.elapsedMicroseconds).toStringAsFixed(5).padLeft(9); + print("${name.padLeft(32)}: $rate iter/us (${stopwatch.elapsed})"); +} From 097753437b127ce78cf9cd8637162bab2619e3ff Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Fri, 18 Dec 2015 14:16:45 -0800 Subject: [PATCH 073/183] One more simple optimization in isWithin. This makes Konstantine's use case about 11% faster. R=nweiz@google.com Review URL: https://codereview.chromium.org//1509803002 . --- pkgs/path/CHANGELOG.md | 4 +++ pkgs/path/lib/src/context.dart | 47 ++++++++++++++++------------------ pkgs/path/pubspec.yaml | 2 +- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 44090f64..a404f70f 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.10 + +* Further improve the performance of `isWithin()`. + ## 1.3.9 * Further improve the performance of `isWithin()` when paths contain `/.` diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index d10a29f1..a6a1cb28 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -558,16 +558,13 @@ class Context { // isWithin("http://example.com/", "http://google.com/bar") //=> false if (parentRootLength != childRootLength) return false; - var parentCodeUnits = parent.codeUnits; - var childCodeUnits = child.codeUnits; - // Make sure that the roots are textually the same as well. // // isWithin("C:/bar", "D:/bar/baz") //=> false // isWithin("http://example.com/", "http://example.org/bar") //=> false for (var i = 0; i < parentRootLength; i++) { - var parentCodeUnit = parentCodeUnits[i]; - var childCodeUnit = childCodeUnits[i]; + var parentCodeUnit = parent.codeUnitAt(i); + var childCodeUnit = child.codeUnitAt(i); if (parentCodeUnit == childCodeUnit) continue; // If both code units are separators, that's fine too. @@ -588,8 +585,8 @@ class Context { var parentIndex = parentRootLength; var childIndex = childRootLength; while (parentIndex < parent.length && childIndex < child.length) { - var parentCodeUnit = parentCodeUnits[parentIndex]; - var childCodeUnit = childCodeUnits[childIndex]; + var parentCodeUnit = parent.codeUnitAt(parentIndex); + var childCodeUnit = child.codeUnitAt(childIndex); if (parentCodeUnit == childCodeUnit) { lastCodeUnit = parentCodeUnit; parentIndex++; @@ -629,7 +626,7 @@ class Context { // We've hit "/." at the end of the parent path, which we can ignore, // since the paths were equivalent up to this point. if (parentIndex == parent.length) break; - parentCodeUnit = parentCodeUnits[parentIndex]; + parentCodeUnit = parent.codeUnitAt(parentIndex); // We've hit "/./", which we can ignore. if (style.isSeparator(parentCodeUnit)) { @@ -642,7 +639,7 @@ class Context { if (parentCodeUnit == chars.PERIOD) { parentIndex++; if (parentIndex == parent.length || - style.isSeparator(parentCodeUnits[parentIndex])) { + style.isSeparator(parent.codeUnitAt(parentIndex))) { return null; } } @@ -658,7 +655,7 @@ class Context { if (style.isSeparator(lastCodeUnit)) { childIndex++; if (childIndex == child.length) break; - childCodeUnit = childCodeUnits[childIndex]; + childCodeUnit = child.codeUnitAt(childIndex); if (style.isSeparator(childCodeUnit)) { childIndex++; @@ -668,7 +665,7 @@ class Context { if (childCodeUnit == chars.PERIOD) { childIndex++; if (childIndex == child.length || - style.isSeparator(childCodeUnits[childIndex])) { + style.isSeparator(child.codeUnitAt(childIndex))) { return null; } } @@ -679,9 +676,9 @@ class Context { // As long as the remainders of the two paths don't have any unresolved // ".." components, we can be confident that [child] is not within // [parent]. - var childDirection = _pathDirection(childCodeUnits, childIndex); + var childDirection = _pathDirection(child, childIndex); if (childDirection != _PathDirection.belowRoot) return null; - var parentDirection = _pathDirection(parentCodeUnits, parentIndex); + var parentDirection = _pathDirection(parent, parentIndex); if (parentDirection != _PathDirection.belowRoot) return null; return false; @@ -694,14 +691,14 @@ class Context { // isWithin("foo/bar/baz", "foo/bar") //=> false // isWithin("foo/bar/baz/../..", "foo/bar") //=> true if (childIndex == child.length) { - var direction = _pathDirection(parentCodeUnits, parentIndex); + var direction = _pathDirection(parent, parentIndex); return direction == _PathDirection.aboveRoot ? null : false; } // We've reached the end of the parent path, which means it's time to make a // decision. Before we do, though, we'll check the rest of the child to see // what that tells us. - var direction = _pathDirection(childCodeUnits, childIndex); + var direction = _pathDirection(child, childIndex); // If there are no more components in the child, then it's the same as // the parent, not within it. @@ -724,7 +721,7 @@ class Context { // isWithin("foo/bar", "foo/bar/baz") //=> true // isWithin("foo/bar/", "foo/bar/baz") //=> true // isWithin("foo/bar", "foo/barbaz") //=> false - return style.isSeparator(childCodeUnits[childIndex]) || + return style.isSeparator(child.codeUnitAt(childIndex)) || style.isSeparator(lastCodeUnit); } @@ -741,31 +738,31 @@ class Context { // pathDirection("foo/../baz") //=> reaches root // pathDirection("foo/../..") //=> above root // pathDirection("foo/../../foo/bar/baz") //=> above root - _PathDirection _pathDirection(List codeUnits, int index) { + _PathDirection _pathDirection(String path, int index) { var depth = 0; var reachedRoot = false; var i = index; - while (i < codeUnits.length) { + while (i < path.length) { // Ignore initial separators or doubled separators. - while (i < codeUnits.length && style.isSeparator(codeUnits[i])) { + while (i < path.length && style.isSeparator(path.codeUnitAt(i))) { i++; } // If we're at the end, stop. - if (i == codeUnits.length) break; + if (i == path.length) break; // Move through the path component to the next separator. var start = i; - while (i < codeUnits.length && !style.isSeparator(codeUnits[i])) { + while (i < path.length && !style.isSeparator(path.codeUnitAt(i))) { i++; } // See if the path component is ".", "..", or a name. - if (i - start == 1 && codeUnits[start] == chars.PERIOD) { + if (i - start == 1 && path.codeUnitAt(start) == chars.PERIOD) { // Don't change the depth. } else if (i - start == 2 && - codeUnits[start] == chars.PERIOD && - codeUnits[start + 1] == chars.PERIOD) { + path.codeUnitAt(start) == chars.PERIOD && + path.codeUnitAt(start + 1) == chars.PERIOD) { // ".." backs out a directory. depth--; @@ -781,7 +778,7 @@ class Context { } // If we're at the end, stop. - if (i == codeUnits.length) break; + if (i == path.length) break; // Move past the separator. i++; diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index fb21d20a..31bf4925 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.9 +version: 1.3.10-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 23ea083b8e3f409b32479ff39ca9119eeabe4344 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 12 Jan 2016 17:22:08 -0800 Subject: [PATCH 074/183] Get rid of all the library tags. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1584623002 . --- pkgs/path/lib/path.dart | 2 -- pkgs/path/lib/src/characters.dart | 2 -- pkgs/path/lib/src/context.dart | 2 -- pkgs/path/lib/src/internal_style.dart | 2 -- pkgs/path/lib/src/parsed_path.dart | 2 -- pkgs/path/lib/src/path_exception.dart | 2 -- pkgs/path/lib/src/style.dart | 2 -- pkgs/path/lib/src/style/posix.dart | 2 -- pkgs/path/lib/src/style/url.dart | 2 -- pkgs/path/lib/src/style/windows.dart | 2 -- pkgs/path/lib/src/utils.dart | 2 -- pkgs/path/test/posix_test.dart | 2 -- pkgs/path/test/utils.dart | 2 -- pkgs/path/test/windows_test.dart | 2 -- 14 files changed, 28 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 93fe67e4..25236ea9 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -44,8 +44,6 @@ /// /// This will join "directory" and "file.txt" using the Windows path separator, /// even when the program is run on a POSIX machine. -library path; - import 'src/context.dart'; import 'src/style.dart'; diff --git a/pkgs/path/lib/src/characters.dart b/pkgs/path/lib/src/characters.dart index ff196a67..7dddb2f1 100644 --- a/pkgs/path/lib/src/characters.dart +++ b/pkgs/path/lib/src/characters.dart @@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. /// This library contains character-code definitions. -library path.characters; - const PLUS = 0x2b; const MINUS = 0x2d; const PERIOD = 0x2e; diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index a6a1cb28..8819706e 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.context; - import 'characters.dart' as chars; import 'internal_style.dart'; import 'style.dart'; diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 549f95ba..84bc67e2 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.internal_style; - import 'context.dart'; import 'style.dart'; diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index d37e2d39..a3d68c77 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.parsed_path; - import 'internal_style.dart'; import 'style.dart'; diff --git a/pkgs/path/lib/src/path_exception.dart b/pkgs/path/lib/src/path_exception.dart index 49bd268e..55f5be30 100644 --- a/pkgs/path/lib/src/path_exception.dart +++ b/pkgs/path/lib/src/path_exception.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.path_exception; - /// An exception class that's thrown when a path operation is unable to be /// computed accurately. class PathException implements Exception { diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index e0e0e01b..342a1028 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.style; - import 'context.dart'; import 'style/posix.dart'; import 'style/url.dart'; diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 74aeb4c5..6f183fc3 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.style.posix; - import '../characters.dart' as chars; import '../parsed_path.dart'; import '../internal_style.dart'; diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 255f22a6..659275cd 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.style.url; - import '../characters.dart' as chars; import '../internal_style.dart'; diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index f38efa52..8223486f 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.style.windows; - import '../characters.dart' as chars; import '../internal_style.dart'; import '../parsed_path.dart'; diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index e3207495..64e471e3 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.utils; - import 'characters.dart' as chars; /// Returns whether [char] is the code for an ASCII letter (uppercase or diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index afd6b94f..7c5aaf48 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.test.posix_test; - import 'package:test/test.dart'; import 'package:path/path.dart' as path; diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 7d917a0b..ae3ea465 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.test.utils; - import "package:test/test.dart"; import "package:path/path.dart" as path; diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 717043d1..8b83c036 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library path.test.windows_test; - import 'package:test/test.dart'; import 'package:path/path.dart' as path; From 175368df838251551dd643ddba710c5d2e122025 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 31 Mar 2016 17:24:45 -0700 Subject: [PATCH 075/183] Declare the package strong-mode clean. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1849573002 . --- pkgs/path/.analysis_options | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pkgs/path/.analysis_options diff --git a/pkgs/path/.analysis_options b/pkgs/path/.analysis_options new file mode 100644 index 00000000..a10d4c5a --- /dev/null +++ b/pkgs/path/.analysis_options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true From 6b84118a00eab5782c7429289f09a43e99570dc2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 5 Oct 2016 16:41:02 -0700 Subject: [PATCH 076/183] Make Windows path manipulation case-insensitive. (dart-lang/path#15) See dart-lang/path#14 --- pkgs/path/CHANGELOG.md | 2 ++ pkgs/path/lib/src/context.dart | 33 +++++++-------------------- pkgs/path/lib/src/internal_style.dart | 10 ++++++++ pkgs/path/lib/src/style/windows.dart | 31 +++++++++++++++++++++++++ pkgs/path/test/posix_test.dart | 5 ++++ pkgs/path/test/url_test.dart | 9 ++++++++ pkgs/path/test/windows_test.dart | 12 ++++++++++ 7 files changed, 77 insertions(+), 25 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index a404f70f..3c9fb6bf 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,5 +1,7 @@ ## 1.3.10 +* Properly compare Windows paths case-insensitively. + * Further improve the performance of `isWithin()`. ## 1.3.9 diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 8819706e..3e84caef 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -444,15 +444,14 @@ class Context { // calculation of relative paths, even if a path has not been normalized. if (fromParsed.root != pathParsed.root && ((fromParsed.root == null || pathParsed.root == null) || - fromParsed.root.toLowerCase().replaceAll('/', '\\') != - pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + !style.pathsEqual(fromParsed.root, pathParsed.root))) { return pathParsed.toString(); } // Strip off their common prefix. while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && - fromParsed.parts[0] == pathParsed.parts[0]) { + style.pathsEqual(fromParsed.parts[0], pathParsed.parts[0])) { fromParsed.parts.removeAt(0); fromParsed.separators.removeAt(1); pathParsed.parts.removeAt(0); @@ -563,15 +562,7 @@ class Context { for (var i = 0; i < parentRootLength; i++) { var parentCodeUnit = parent.codeUnitAt(i); var childCodeUnit = child.codeUnitAt(i); - if (parentCodeUnit == childCodeUnit) continue; - - // If both code units are separators, that's fine too. - // - // isWithin("C:/", r"C:\foo") //=> true - if (!style.isSeparator(parentCodeUnit) || - !style.isSeparator(childCodeUnit)) { - return false; - } + if (!style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) return false; } // Start by considering the last code unit as a separator, since @@ -585,17 +576,7 @@ class Context { while (parentIndex < parent.length && childIndex < child.length) { var parentCodeUnit = parent.codeUnitAt(parentIndex); var childCodeUnit = child.codeUnitAt(childIndex); - if (parentCodeUnit == childCodeUnit) { - lastCodeUnit = parentCodeUnit; - parentIndex++; - childIndex++; - continue; - } - - // Different separators are considered identical. - var parentIsSeparator = style.isSeparator(parentCodeUnit); - var childIsSeparator = style.isSeparator(childCodeUnit); - if (parentIsSeparator && childIsSeparator) { + if (style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) { lastCodeUnit = parentCodeUnit; parentIndex++; childIndex++; @@ -603,10 +584,12 @@ class Context { } // Ignore multiple separators in a row. - if (parentIsSeparator && style.isSeparator(lastCodeUnit)) { + if (style.isSeparator(parentCodeUnit) && + style.isSeparator(lastCodeUnit)) { parentIndex++; continue; - } else if (childIsSeparator && style.isSeparator(lastCodeUnit)) { + } else if (style.isSeparator(childCodeUnit) && + style.isSeparator(lastCodeUnit)) { childIndex++; continue; } diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 84bc67e2..1450d531 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -66,4 +66,14 @@ abstract class InternalStyle extends Style { /// Returns the URI that represents [path], which is assumed to be absolute. Uri absolutePathToUri(String path); + + /// Returns whether [codeUnit1] and [codeUnit2] are considered equivalent for + /// this style. + bool codeUnitsEqual(int codeUnit1, int codeUnit2) => codeUnit1 == codeUnit2; + + /// Returns whether [path1] and [path2] are equivalent. + /// + /// This only needs to handle character-by-character comparison; it can assume + /// the paths are normalized and contain no `..` components. + bool pathsEqual(String path1, String path2) => path1 == path2; } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 8223486f..0d89764b 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -7,6 +7,10 @@ import '../internal_style.dart'; import '../parsed_path.dart'; import '../utils.dart'; +// `0b100000` can be bitwise-ORed with uppercase ASCII letters to get their +// lowercase equivalents. +const _asciiCaseBit = 0x20; + /// The style for Windows paths. class WindowsStyle extends InternalStyle { WindowsStyle(); @@ -120,4 +124,31 @@ class WindowsStyle extends InternalStyle { return new Uri(scheme: 'file', pathSegments: parsed.parts); } } + + bool codeUnitsEqual(int codeUnit1, int codeUnit2) { + if (codeUnit1 == codeUnit2) return true; + + /// Forward slashes and backslashes are equivalent on Windows. + if (codeUnit1 == chars.SLASH) return codeUnit2 == chars.BACKSLASH; + if (codeUnit1 == chars.BACKSLASH) return codeUnit2 == chars.SLASH; + + // If this check fails, the code units are definitely different. If it + // succeeds *and* either codeUnit is an ASCII letter, they're equivalent. + if (codeUnit1 ^ codeUnit2 != _asciiCaseBit) return false; + + // Now we just need to verify that one of the code units is an ASCII letter. + var upperCase1 = codeUnit1 | _asciiCaseBit; + return upperCase1 >= chars.LOWER_A && upperCase1 <= chars.LOWER_Z; + } + + bool pathsEqual(String path1, String path2) { + if (identical(path1, path2)) return true; + if (path1.length != path2.length) return false; + for (var i = 0; i < path1.length; i++) { + if (!codeUnitsEqual(path1.codeUnitAt(i), path2.codeUnitAt(i))) { + return false; + } + } + return true; + } } diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 7c5aaf48..7b55dd3b 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -348,6 +348,11 @@ main() { expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); + test('is case-sensitive', () { + expect(context.relative('/RoOt'), '../../RoOt'); + expect(context.relative('/rOoT/pAtH/a'), '../../rOoT/pAtH/a'); + }); + // Regression test('from root-only path', () { expect(context.relative('/', from: '/'), '.'); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index c81893a5..4adfb481 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -479,6 +479,15 @@ main() { expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); + test('is case-sensitive', () { + expect(context.relative('HtTp://dartlang.org/root'), + 'HtTp://dartlang.org/root'); + expect(context.relative('http://DaRtLaNg.OrG/root'), + 'http://DaRtLaNg.OrG/root'); + expect(context.relative('/RoOt'), '../../RoOt'); + expect(context.relative('/rOoT/pAtH/a'), '../../rOoT/pAtH/a'); + }); + // Regression test('from root-only path', () { expect(context.relative('http://dartlang.org', diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 8b83c036..8e72bb11 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -425,6 +425,12 @@ main() { expect(context.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); }); + test('is case-insensitive', () { + expect(context.relative(r'c:\'), r'..\..'); + expect(context.relative(r'c:\RoOt'), r'..'); + expect(context.relative(r'c:\rOoT\pAtH\a'), r'a'); + }); + // Regression test('from root-only path', () { expect(context.relative(r'C:\', from: r'C:\'), '.'); @@ -567,6 +573,12 @@ main() { expect(r.isWithin(r'C:\', r'C:\baz\bang'), isTrue); expect(r.isWithin('.', r'C:\baz\bang'), isFalse); }); + + test('is case-insensitive', () { + expect(context.isWithin(r'FoO', r'fOo\bar'), isTrue); + expect(context.isWithin(r'C:\', r'c:\foo'), isTrue); + expect(context.isWithin(r'fOo\qux\..\BaR', r'FoO\bAr\baz'), isTrue); + }); }); group('absolute', () { From 4e7239bbe01ad7c89542bf15041022960ccc1686 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 6 Oct 2016 17:17:49 -0700 Subject: [PATCH 077/183] Add new equality and canonicalization functions. (dart-lang/path#16) Closes dart-lang/path#14 --- pkgs/path/CHANGELOG.md | 5 +- pkgs/path/lib/path.dart | 32 +++ pkgs/path/lib/src/context.dart | 308 ++++++++++++++++++++------ pkgs/path/lib/src/internal_style.dart | 4 + pkgs/path/lib/src/parsed_path.dart | 5 +- pkgs/path/lib/src/style/windows.dart | 9 + pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 44 ++++ pkgs/path/test/url_test.dart | 58 +++++ pkgs/path/test/utils.dart | 27 ++- pkgs/path/test/windows_test.dart | 55 +++++ 11 files changed, 478 insertions(+), 71 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 3c9fb6bf..2818f7be 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,4 +1,7 @@ -## 1.3.10 +## 1.4.0 + +* Add `equals()`, `hash()` and `canonicalize()` top-level functions and + `Context` methods. These make it easier to treat paths as map keys. * Properly compare Windows paths case-insensitively. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 25236ea9..59cc5208 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -281,9 +281,27 @@ String joinAll(Iterable parts) => context.joinAll(parts); /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] List split(String path) => context.split(path); +/// Canonicalizes [path]. +/// +/// This is guaranteed to return the same path for two different input paths +/// if and only if both input paths point to the same location. Unlike +/// [normalize], it returns absolute paths when possible and canonicalizes +/// ASCII case on Windows. +/// +/// Note that this does not resolve symlinks. +/// +/// If you want a map that uses path keys, it's probably more efficient to +/// pass [equals] and [hash] to [new HashMap] than it is to canonicalize every +/// key. +String canonicalize(String path) => context.canonicalize(path); + /// Normalizes [path], simplifying it by handling `..`, and `.`, and /// removing redundant path separators whenever possible. /// +/// Note that this is *not* guaranteed to return the same result for two +/// equivalent input paths. For that, see [canonicalize]. Or, if you're using +/// paths as map keys, pass [equals] and [hash] to [new HashMap]. +/// /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) => context.normalize(path); @@ -324,6 +342,20 @@ String relative(String path, {String from}) => /// path.isWithin('/root/path', '/root/path') // -> false bool isWithin(String parent, String child) => context.isWithin(parent, child); +/// Returns `true` if [path1] points to the same location as [path2], and +/// `false` otherwise. +/// +/// The [hash] function returns a hash code that matches these equality +/// semantics. +bool equals(String path1, String path2) => context.equals(path1, path2); + +/// Returns a hash code for [path] such that, if [equals] returns `true` for two +/// paths, their hash codes are the same. +/// +/// Note that the same path may have different hash codes on different platforms +/// or with different [current] directories. +int hash(String path) => context.hash(path); + /// Removes a trailing extension from the last part of [path]. /// /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 3e84caef..af97f908 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -2,6 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:math' as math; + import 'characters.dart' as chars; import 'internal_style.dart'; import 'style.dart'; @@ -298,9 +300,34 @@ class Context { return parsed.parts; } + /// Canonicalizes [path]. + /// + /// This is guaranteed to return the same path for two different input paths + /// if and only if both input paths point to the same location. Unlike + /// [normalize], it returns absolute paths when possible and canonicalizes + /// ASCII case on Windows. + /// + /// Note that this does not resolve symlinks. + /// + /// If you want a map that uses path keys, it's probably more efficient to + /// pass [equals] and [hash] to [new HashMap] than it is to canonicalize every + /// key. + String canonicalize(String path) { + path = absolute(path); + if (style != Style.windows && !_needsNormalization(path)) return path; + + var parsed = _parse(path); + parsed.normalize(canonicalize: true); + return parsed.toString(); + } + /// Normalizes [path], simplifying it by handling `..`, and `.`, and /// removing redundant path separators whenever possible. /// + /// Note that this is *not* guaranteed to return the same result for two + /// equivalent input paths. For that, see [canonicalize]. Or, if you're using + /// paths as map keys, pass [equals] and [hash] to [new HashMap]. + /// /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) { if (!_needsNormalization(path)) return path; @@ -496,7 +523,22 @@ class Context { /// path.isWithin('/root/path', '/root/path/a'); // -> true /// path.isWithin('/root/path', '/root/other'); // -> false /// path.isWithin('/root/path', '/root/path'); // -> false - bool isWithin(String parent, String child) { + bool isWithin(String parent, String child) => + _isWithinOrEquals(parent, child) == _PathRelation.within; + + /// Returns `true` if [path1] points to the same location as [path2], and + /// `false` otherwise. + /// + /// The [hash] function returns a hash code that matches these equality + /// semantics. + bool equals(String path1, String path2) => + _isWithinOrEquals(path1, path2) == _PathRelation.equal; + + /// Compares two paths and returns an enum value indicating their relationship + /// to one another. + /// + /// This never returns [_PathRelation.inconclusive]. + _PathRelation _isWithinOrEquals(String parent, String child) { // Make both paths the same level of relative. We're only able to do the // quick comparison if both paths are in the same format, and making a path // absolute is faster than making it relative. @@ -519,8 +561,8 @@ class Context { } } - var fastResult = _isWithinFast(parent, child); - if (fastResult != null) return fastResult; + var result = _isWithinOrEqualsFast(parent, child); + if (result != _PathRelation.inconclusive) return result; var relative; try { @@ -528,18 +570,22 @@ class Context { } on PathException catch (_) { // If no relative path from [parent] to [child] is found, [child] // definitely isn't a child of [parent]. - return false; + return _PathRelation.different; } - var parts = this.split(relative); - return this.isRelative(relative) && - parts.first != '..' && - parts.first != '.'; + if (!this.isRelative(relative)) return _PathRelation.different; + if (relative == '.') return _PathRelation.equal; + if (relative == '..') return _PathRelation.different; + return (relative.length >= 3 && + relative.startsWith('..') && + style.isSeparator(relative.codeUnitAt(2))) + ? _PathRelation.different + : _PathRelation.within; } - /// An optimized implementation of [isWithin] that doesn't handle a few - /// complex cases. - bool _isWithinFast(String parent, String child) { + /// An optimized implementation of [_isWithinOrEquals] that doesn't handle a + /// few complex cases. + _PathRelation _isWithinOrEqualsFast(String parent, String child) { // Normally we just bail when we see "." path components, but we can handle // a single dot easily enough. if (parent == '.') parent = ''; @@ -553,7 +599,7 @@ class Context { // // isWithin("C:/bar", "//foo/bar/baz") //=> false // isWithin("http://example.com/", "http://google.com/bar") //=> false - if (parentRootLength != childRootLength) return false; + if (parentRootLength != childRootLength) return _PathRelation.different; // Make sure that the roots are textually the same as well. // @@ -562,7 +608,9 @@ class Context { for (var i = 0; i < parentRootLength; i++) { var parentCodeUnit = parent.codeUnitAt(i); var childCodeUnit = child.codeUnitAt(i); - if (!style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) return false; + if (!style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) { + return _PathRelation.different; + } } // Start by considering the last code unit as a separator, since @@ -570,6 +618,9 @@ class Context { // comparing relative paths. var lastCodeUnit = chars.SLASH; + /// The index of the last separator in [parent]. + int lastParentSeparator; + // Iterate through both paths as long as they're semantically identical. var parentIndex = parentRootLength; var childIndex = childRootLength; @@ -577,6 +628,10 @@ class Context { var parentCodeUnit = parent.codeUnitAt(parentIndex); var childCodeUnit = child.codeUnitAt(childIndex); if (style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) { + if (style.isSeparator(parentCodeUnit)) { + lastParentSeparator = parentIndex; + } + lastCodeUnit = parentCodeUnit; parentIndex++; childIndex++; @@ -586,6 +641,7 @@ class Context { // Ignore multiple separators in a row. if (style.isSeparator(parentCodeUnit) && style.isSeparator(lastCodeUnit)) { + lastParentSeparator = parentIndex; parentIndex++; continue; } else if (style.isSeparator(childCodeUnit) && @@ -594,35 +650,34 @@ class Context { continue; } - if (parentCodeUnit == chars.PERIOD) { - // If a dot comes after a separator, it may be a directory traversal - // operator. To check that, we need to know if it's followed by either - // "/" or "./". Otherwise, it's just a normal non-matching character. - // - // isWithin("foo/./bar", "foo/bar/baz") //=> true - // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false - if (style.isSeparator(lastCodeUnit)) { - parentIndex++; + // If a dot comes after a separator, it may be a directory traversal + // operator. To check that, we need to know if it's followed by either + // "/" or "./". Otherwise, it's just a normal non-matching character. + // + // isWithin("foo/./bar", "foo/bar/baz") //=> true + // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false + if (parentCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) { + parentIndex++; - // We've hit "/." at the end of the parent path, which we can ignore, - // since the paths were equivalent up to this point. - if (parentIndex == parent.length) break; - parentCodeUnit = parent.codeUnitAt(parentIndex); + // We've hit "/." at the end of the parent path, which we can ignore, + // since the paths were equivalent up to this point. + if (parentIndex == parent.length) break; + parentCodeUnit = parent.codeUnitAt(parentIndex); - // We've hit "/./", which we can ignore. - if (style.isSeparator(parentCodeUnit)) { - parentIndex++; - continue; - } + // We've hit "/./", which we can ignore. + if (style.isSeparator(parentCodeUnit)) { + lastParentSeparator = parentIndex; + parentIndex++; + continue; + } - // We've hit "/..", which may be a directory traversal operator that - // we can't handle on the fast track. - if (parentCodeUnit == chars.PERIOD) { - parentIndex++; - if (parentIndex == parent.length || - style.isSeparator(parent.codeUnitAt(parentIndex))) { - return null; - } + // We've hit "/..", which may be a directory traversal operator that + // we can't handle on the fast track. + if (parentCodeUnit == chars.PERIOD) { + parentIndex++; + if (parentIndex == parent.length || + style.isSeparator(parent.codeUnitAt(parentIndex))) { + return _PathRelation.inconclusive; } } @@ -632,23 +687,21 @@ class Context { // This is the same logic as above, but for the child path instead of the // parent. - if (childCodeUnit == chars.PERIOD) { - if (style.isSeparator(lastCodeUnit)) { - childIndex++; - if (childIndex == child.length) break; - childCodeUnit = child.codeUnitAt(childIndex); + if (childCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) { + childIndex++; + if (childIndex == child.length) break; + childCodeUnit = child.codeUnitAt(childIndex); - if (style.isSeparator(childCodeUnit)) { - childIndex++; - continue; - } + if (style.isSeparator(childCodeUnit)) { + childIndex++; + continue; + } - if (childCodeUnit == chars.PERIOD) { - childIndex++; - if (childIndex == child.length || - style.isSeparator(child.codeUnitAt(childIndex))) { - return null; - } + if (childCodeUnit == chars.PERIOD) { + childIndex++; + if (childIndex == child.length || + style.isSeparator(child.codeUnitAt(childIndex))) { + return _PathRelation.inconclusive; } } } @@ -658,11 +711,16 @@ class Context { // ".." components, we can be confident that [child] is not within // [parent]. var childDirection = _pathDirection(child, childIndex); - if (childDirection != _PathDirection.belowRoot) return null; + if (childDirection != _PathDirection.belowRoot) { + return _PathRelation.inconclusive; + } + var parentDirection = _pathDirection(parent, parentIndex); - if (parentDirection != _PathDirection.belowRoot) return null; + if (parentDirection != _PathDirection.belowRoot) { + return _PathRelation.inconclusive; + } - return false; + return _PathRelation.different; } // If the child is shorter than the parent, it's probably not within the @@ -672,8 +730,19 @@ class Context { // isWithin("foo/bar/baz", "foo/bar") //=> false // isWithin("foo/bar/baz/../..", "foo/bar") //=> true if (childIndex == child.length) { - var direction = _pathDirection(parent, parentIndex); - return direction == _PathDirection.aboveRoot ? null : false; + if (parentIndex == parent.length || + style.isSeparator(parent.codeUnitAt(parentIndex))) { + lastParentSeparator = parentIndex; + } else { + lastParentSeparator ??= math.max(0, parentRootLength - 1); + } + + var direction = _pathDirection(parent, + lastParentSeparator ?? parentRootLength - 1); + if (direction == _PathDirection.atRoot) return _PathRelation.equal; + return direction == _PathDirection.aboveRoot + ? _PathRelation.inconclusive + : _PathRelation.different; } // We've reached the end of the parent path, which means it's time to make a @@ -682,11 +751,13 @@ class Context { var direction = _pathDirection(child, childIndex); // If there are no more components in the child, then it's the same as - // the parent, not within it. + // the parent. // // isWithin("foo/bar", "foo/bar") //=> false // isWithin("foo/bar", "foo/bar//") //=> false - if (direction == _PathDirection.atRoot) return false; + // equals("foo/bar", "foo/bar") //=> true + // equals("foo/bar", "foo/bar//") //=> true + if (direction == _PathDirection.atRoot) return _PathRelation.equal; // If there are unresolved ".." components in the child, no decision we make // will be valid. We'll abort and do the slow check instead. @@ -694,7 +765,9 @@ class Context { // isWithin("foo/bar", "foo/bar/..") //=> false // isWithin("foo/bar", "foo/bar/baz/bang/../../..") //=> false // isWithin("foo/bar", "foo/bar/baz/bang/../../../bar/baz") //=> true - if (direction == _PathDirection.aboveRoot) return null; + if (direction == _PathDirection.aboveRoot) { + return _PathRelation.inconclusive; + } // The child is within the parent if and only if we're on a separator // boundary. @@ -702,12 +775,14 @@ class Context { // isWithin("foo/bar", "foo/bar/baz") //=> true // isWithin("foo/bar/", "foo/bar/baz") //=> true // isWithin("foo/bar", "foo/barbaz") //=> false - return style.isSeparator(child.codeUnitAt(childIndex)) || - style.isSeparator(lastCodeUnit); + return (style.isSeparator(child.codeUnitAt(childIndex)) || + style.isSeparator(lastCodeUnit)) + ? _PathRelation.within + : _PathRelation.different; } // Returns a [_PathDirection] describing the path represented by [codeUnits] - // after [index]. + // starting at [index]. // // This ignores leading separators. // @@ -771,6 +846,80 @@ class Context { return _PathDirection.belowRoot; } + /// Returns a hash code for [path] that matches the semantics of [equals]. + /// + /// Note that the same path may have different hash codes in different + /// [Context]s. + int hash(String path) { + // Make [path] absolute to ensure that equivalent relative and absolute + // paths have the same hash code. + path = absolute(path); + + var result = _hashFast(path); + if (result != null) return result; + + var parsed = _parse(path); + parsed.normalize(); + return _hashFast(parsed.toString()); + } + + /// An optimized implementation of [hash] that doesn't handle internal `..` + /// components. + /// + /// This will handle `..` components that appear at the beginning of the path. + int _hashFast(String path) { + var hash = 4603; + var beginning = true; + var wasSeparator = true; + for (var i = 0; i < path.length; i++) { + var codeUnit = style.canonicalizeCodeUnit(path.codeUnitAt(i)); + + // Take advantage of the fact that collisions are allowed to ignore + // separators entirely. This lets us avoid worrying about cases like + // multiple trailing slashes. + if (style.isSeparator(codeUnit)) { + wasSeparator = true; + continue; + } + + if (codeUnit == chars.PERIOD && wasSeparator) { + // If a dot comes after a separator, it may be a directory traversal + // operator. To check that, we need to know if it's followed by either + // "/" or "./". Otherwise, it's just a normal character. + // + // hash("foo/./bar") == hash("foo/bar") + + // We've hit "/." at the end of the path, which we can ignore. + if (i + 1 == path.length) break; + + var next = path.codeUnitAt(i + 1); + + // We can just ignore "/./", since they don't affect the semantics of + // the path. + if (style.isSeparator(next)) continue; + + // If the path ends with "/.." or contains "/../", we need to + // canonicalize it before we can hash it. We make an exception for ".."s + // at the beginning of the path, since those may appear even in a + // canonicalized path. + if (!beginning && + next == chars.PERIOD && + (i + 2 == path.length || + style.isSeparator(path.codeUnitAt(i + 2)))) { + return null; + } + } + + // Make sure [hash] stays under 32 bits even after multiplication. + hash &= 0x3FFFFFF; + hash *= 33; + hash ^= codeUnit; + wasSeparator = false; + beginning = false; + } + return hash; + } + /// Removes a trailing extension from the last part of [path]. /// /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' @@ -930,3 +1079,32 @@ class _PathDirection { String toString() => name; } + +/// An enum of possible return values for [Context._isWithinOrEquals]. +class _PathRelation { + /// The first path is a proper parent of the second. + /// + /// For example, `foo` is a proper parent of `foo/bar`, but not of `foo`. + static const within = const _PathRelation("within"); + + /// The two paths are equivalent. + /// + /// For example, `foo//bar` is equivalent to `foo/bar`. + static const equal = const _PathRelation("equal"); + + /// The first path is neither a parent of nor equal to the second. + static const different = const _PathRelation("different"); + + /// We couldn't quickly determine any information about the paths' + /// relationship to each other. + /// + /// Only returned by [Context._isWithinOrEqualsFast]. + static const inconclusive = const _PathRelation("inconclusive"); + + final String name; + + const _PathRelation(this.name); + + String toString() => name; +} + diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 1450d531..1874afcb 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -76,4 +76,8 @@ abstract class InternalStyle extends Style { /// This only needs to handle character-by-character comparison; it can assume /// the paths are normalized and contain no `..` components. bool pathsEqual(String path1, String path2) => path1 == path2; + + int canonicalizeCodeUnit(int codeUnit) => codeUnit; + + String canonicalizePart(String part) => part; } diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index a3d68c77..619ffbf4 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -97,7 +97,7 @@ class ParsedPath { if (separators.length > 0) separators[separators.length - 1] = ''; } - void normalize() { + void normalize({bool canonicalize: false}) { // Handle '.', '..', and empty parts. var leadingDoubles = 0; var newParts = []; @@ -113,7 +113,7 @@ class ParsedPath { leadingDoubles++; } } else { - newParts.add(part); + newParts.add(canonicalize ? style.canonicalizePart(part) : part); } } @@ -139,6 +139,7 @@ class ParsedPath { // Normalize the Windows root if needed. if (root != null && style == Style.windows) { + if (canonicalize) root = root.toLowerCase(); root = root.replaceAll('/', '\\'); } removeTrailingSeparators(); diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 0d89764b..57989af2 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -151,4 +151,13 @@ class WindowsStyle extends InternalStyle { } return true; } + + int canonicalizeCodeUnit(int codeUnit) { + if (codeUnit == chars.SLASH) return chars.BACKSLASH; + if (codeUnit < chars.UPPER_A) return codeUnit; + if (codeUnit > chars.UPPER_Z) return codeUnit; + return codeUnit | _asciiCaseBit; + } + + String canonicalizePart(String part) => part.toLowerCase(); } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 31bf4925..a0475a19 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.3.10-dev +version: 1.4.0-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 7b55dd3b..4549ebdf 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -317,6 +317,12 @@ main() { expect(context.normalize(r'a/b\'), r'a/b\'); expect(context.normalize('a/b///'), 'a/b'); }); + + test('when canonicalizing', () { + expect(context.canonicalize('.'), '/root/path'); + expect(context.canonicalize('foo/bar'), '/root/path/foo/bar'); + expect(context.canonicalize('FoO'), '/root/path/FoO'); + }); }); group('relative', () { @@ -445,6 +451,44 @@ main() { }); }); + group('equals and hash', () { + test('simple cases', () { + expectEquals(context, 'foo/bar', 'foo/bar'); + expectNotEquals(context, 'foo/bar', 'foo/bar/baz'); + expectNotEquals(context, 'foo/bar', 'foo'); + expectNotEquals(context, 'foo/bar', 'foo/baz'); + expectEquals(context, 'foo/bar', '../path/foo/bar'); + expectEquals(context, '/', '/'); + expectEquals(context, '/', '../..'); + expectEquals(context, 'baz', '/root/path/baz'); + }); + + test('complex cases', () { + expectEquals(context, 'foo/./bar', 'foo/bar'); + expectEquals(context, 'foo//bar', 'foo/bar'); + expectEquals(context, 'foo/qux/../bar', 'foo/bar'); + expectNotEquals(context, 'foo/qux/../bar', 'foo/qux'); + expectNotEquals(context, 'foo/bar', 'foo/bar/baz/../..'); + expectEquals(context, 'foo/bar', 'foo/bar///'); + expectEquals(context, 'foo/.bar', 'foo/.bar'); + expectNotEquals(context, 'foo/./bar', 'foo/.bar'); + expectEquals(context, 'foo/..bar', 'foo/..bar'); + expectNotEquals(context, 'foo/../bar', 'foo/..bar'); + expectEquals(context, 'foo/bar', 'foo/bar/baz/..'); + expectNotEquals(context, 'FoO/bAr', 'foo/bar'); + }); + + test('from a relative root', () { + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + expectEquals(r, 'a/b', 'a/b'); + expectNotEquals(r, '.', 'foo/bar'); + expectNotEquals(r, '.', '../a/b'); + expectEquals(r, '.', '../bar'); + expectEquals(r, '/baz/bang', '/baz/bang'); + expectNotEquals(r, 'baz/bang', '/baz/bang'); + }); + }); + group('absolute', () { test('allows up to seven parts', () { expect(context.absolute('a'), '/root/path/a'); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 4adfb481..32c02da7 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -5,6 +5,8 @@ import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'utils.dart'; + main() { var context = new path.Context( style: path.Style.url, current: 'http://dartlang.org/root/path'); @@ -427,6 +429,16 @@ main() { expect(context.normalize(r'a/b\'), r'a/b\'); expect(context.normalize('a/b///'), 'a/b'); }); + + test('when canonicalizing', () { + expect(context.canonicalize('.'), 'http://dartlang.org/root/path'); + expect(context.canonicalize('foo/bar'), + 'http://dartlang.org/root/path/foo/bar'); + expect(context.canonicalize('FoO'), 'http://dartlang.org/root/path/FoO'); + expect(context.canonicalize('/foo'), 'http://dartlang.org/foo'); + expect(context.canonicalize('http://google.com/foo'), + 'http://google.com/foo'); + }); }); group('relative', () { @@ -674,6 +686,52 @@ main() { }); }); + group('equals and hash', () { + test('simple cases', () { + expectEquals(context, 'foo/bar', 'foo/bar'); + expectNotEquals(context, 'foo/bar', 'foo/bar/baz'); + expectNotEquals(context, 'foo/bar', 'foo'); + expectNotEquals(context, 'foo/bar', 'foo/baz'); + expectEquals(context, 'foo/bar', '../path/foo/bar'); + expectEquals(context, 'http://google.com', 'http://google.com'); + expectEquals(context, 'http://dartlang.org', '../..'); + expectEquals(context, 'baz', '/root/path/baz'); + }); + + test('complex cases', () { + expectEquals(context, 'foo/./bar', 'foo/bar'); + expectEquals(context, 'foo//bar', 'foo/bar'); + expectEquals(context, 'foo/qux/../bar', 'foo/bar'); + expectNotEquals(context, 'foo/qux/../bar', 'foo/qux'); + expectNotEquals(context, 'foo/bar', 'foo/bar/baz/../..'); + expectEquals(context, 'foo/bar', 'foo/bar///'); + expectEquals(context, 'foo/.bar', 'foo/.bar'); + expectNotEquals(context, 'foo/./bar', 'foo/.bar'); + expectEquals(context, 'foo/..bar', 'foo/..bar'); + expectNotEquals(context, 'foo/../bar', 'foo/..bar'); + expectEquals(context, 'foo/bar', 'foo/bar/baz/..'); + expectNotEquals(context, 'FoO/bAr', 'foo/bar'); + expectEquals(context, 'http://google.com', 'http://google.com/'); + expectEquals(context, 'http://dartlang.org/root', '..'); + }); + + test('with root-relative paths', () { + expectEquals(context, '/foo', 'http://dartlang.org/foo'); + expectNotEquals(context, '/foo', 'http://google.com/foo'); + expectEquals(context, '/root/path/foo/bar', 'foo/bar'); + }); + + test('from a relative root', () { + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + expectEquals(r, 'a/b', 'a/b'); + expectNotEquals(r, '.', 'foo/bar'); + expectNotEquals(r, '.', '../a/b'); + expectEquals(r, '.', '../bar'); + expectEquals(r, '/baz/bang', '/baz/bang'); + expectNotEquals(r, 'baz/bang', '/baz/bang'); + }); + }); + group('absolute', () { test('allows up to seven parts', () { expect(context.absolute('a'), 'http://dartlang.org/root/path/a'); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index ae3ea465..5e22ce1f 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -3,7 +3,30 @@ // BSD-style license that can be found in the LICENSE file. import "package:test/test.dart"; -import "package:path/path.dart" as path; +import "package:path/path.dart" as p; /// A matcher for a closure that throws a [path.PathException]. -final throwsPathException = throwsA(new isInstanceOf()); +final throwsPathException = throwsA(new isInstanceOf()); + +void expectEquals(p.Context context, String path1, String path2) { + expect(context.equals(path1, path2), isTrue, + reason: 'Expected "$path1" to equal "$path2".'); + expect(context.equals(path2, path1), isTrue, + reason: 'Expected "$path2" to equal "$path1".'); + expect(context.hash(path1), equals(context.hash(path2)), + reason: 'Expected "$path1" to hash the same as "$path2".'); +} + +void expectNotEquals(p.Context context, String path1, String path2, + {bool allowSameHash: false}) { + expect(context.equals(path1, path2), isFalse, + reason: 'Expected "$path1" not to equal "$path2".'); + expect(context.equals(path2, path1), isFalse, + reason: 'Expected "$path2" not to equal "$path1".'); + + // Hash collisions are allowed, but the test author should be explicitly aware + // when they occur. + if (allowSameHash) return; + expect(context.hash(path1), isNot(equals(context.hash(path2))), + reason: 'Expected "$path1" not to hash the same as "$path2".'); +} diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 8e72bb11..f4453a7c 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -378,6 +378,14 @@ main() { test('normalizes separators', () { expect(context.normalize(r'a/b\c'), r'a\b\c'); }); + + test('when canonicalizing', () { + expect(context.canonicalize('.'), r'c:\root\path'); + expect(context.canonicalize('foo/bar'), r'c:\root\path\foo\bar'); + expect(context.canonicalize('FoO'), r'c:\root\path\foo'); + expect(context.canonicalize('/foo'), r'c:\foo'); + expect(context.canonicalize('D:/foo'), r'd:\foo'); + }); }); group('relative', () { @@ -581,6 +589,53 @@ main() { }); }); + group('equals and hash', () { + test('simple cases', () { + expectEquals(context, r'foo\bar', r'foo\bar'); + expectNotEquals(context, r'foo\bar', r'foo\bar\baz'); + expectNotEquals(context, r'foo\bar', r'foo'); + expectNotEquals(context, r'foo\bar', r'foo\baz'); + expectEquals(context, r'foo\bar', r'..\path\foo\bar'); + expectEquals(context, r'D:\', r'D:\'); + expectEquals(context, r'C:\', r'..\..'); + expectEquals(context, r'baz', r'C:\root\path\baz'); + }); + + test('complex cases', () { + expectEquals(context, r'foo\.\bar', r'foo\bar'); + expectEquals(context, r'foo\\bar', r'foo\bar'); + expectEquals(context, r'foo\qux\..\bar', r'foo\bar'); + expectNotEquals(context, r'foo\qux\..\bar', r'foo\qux'); + expectNotEquals(context, r'foo\bar', r'foo\bar\baz\..\..'); + expectEquals(context, r'foo\bar', r'foo\bar\\\'); + expectEquals(context, r'foo\.bar', r'foo\.bar'); + expectNotEquals(context, r'foo\.\bar', r'foo\.bar'); + expectEquals(context, r'foo\..bar', r'foo\..bar'); + expectNotEquals(context, r'foo\..\bar', r'foo\..bar'); + expectEquals(context, r'foo\bar', r'foo\bar\baz\..'); + expectEquals(context, r'FoO\bAr', r'foo\bar'); + expectEquals(context, r'foo/\bar', r'foo\/bar'); + expectEquals(context, r'c:\', r'C:\'); + expectEquals(context, r'C:\root', r'..'); + }); + + test('with root-relative paths', () { + expectEquals(context, r'\foo', r'C:\foo'); + expectNotEquals(context, r'\foo', 'http://google.com/foo'); + expectEquals(context, r'C:\root\path\foo\bar', r'foo\bar'); + }); + + test('from a relative root', () { + var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + expectEquals(r, r'a\b', r'a\b'); + expectNotEquals(r, '.', r'foo\bar'); + expectNotEquals(r, '.', r'..\a\b'); + expectEquals(r, '.', r'..\bar'); + expectEquals(r, r'C:\baz\bang', r'C:\baz\bang'); + expectNotEquals(r, r'baz\bang', r'C:\baz\bang'); + }); + }); + group('absolute', () { test('allows up to seven parts', () { expect(context.absolute('a'), r'C:\root\path\a'); From 532575bd39e200d0861dc34f4ebd215b4d2c95c9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 10 Oct 2016 14:39:14 -0700 Subject: [PATCH 078/183] Add a caveat about URL comparison. (dart-lang/path#17) --- pkgs/path/lib/path.dart | 3 +++ pkgs/path/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 59cc5208..deb1b53b 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -58,6 +58,9 @@ final Context posix = new Context(style: Style.posix); final Context windows = new Context(style: Style.windows); /// A default context for manipulating URLs. +/// +/// URL path equality is undefined for paths that differ only in their +/// percent-encoding or only in the case of their host segment. final Context url = new Context(style: Style.url); /// The system path context. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index a0475a19..87980ea5 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.4.0-dev +version: 1.4.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 62aa7b79d6f3c4b9a1432a8a4407eb7646d5a42e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Dec 2016 14:13:38 -0800 Subject: [PATCH 079/183] Be more explicit about stability guarantees. --- pkgs/path/README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 5a172cdf..58034706 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -41,13 +41,34 @@ context.join("directory", "file.txt"); This will join "directory" and "file.txt" using the Windows path separator, even when the program is run on a POSIX machine. +## Stability + +The `path` package is used by many Dart packages, and as such it strives for a +very high degree of stability. For the same reason, though, releasing a new +major version would probably cause a lot of versioning pain, so some flexibility +is necessary. + +We try to guarantee that **operations with valid inputs and correct output will +not change**. Operations where one or more inputs are invalid according to the +semantics of the corresponding platform may produce different output over time. +Operations for which `path` produces incorrect output will also change so that +we can fix bugs. + +Also, the `path` package's URL handling is based on [the WHATWG URL spec][]. +This is a living standard, and some parts of it haven't yet been entirely +solidified by vendor support. The `path` package reserves the right to change +its URL behavior if the underlying specification changes, although if the change +is big enough to break many valid uses we may elect to treat it as a breaking +change anyway. + +[the WHATWG URL spec]: https://url.spec.whatwg.org/ + ## FAQ ### Where can I use this? -Pathos runs on the Dart VM and in the browser under both dart2js and Dartium. -Under dart2js, it currently returns "." as the current working directory, while -under Dartium it returns the current URL. +The `path` package runs on the Dart VM and in the browser under both dart2js and +Dartium. On the browser, `window.location.href` is used as the current path. ### Why doesn't this make paths first-class objects? From df4c64e342bfa79e73920101999ae89ee08c8ac1 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Dec 2016 14:50:30 -0800 Subject: [PATCH 080/183] Fix root-relative file URI handling. According to the WHATWG spec, "/foo" should be considered relative to the drive letter if one exists for file URIs. See https://url.spec.whatwg.org/#file-slash-state See dart-lang/path#18 --- pkgs/path/CHANGELOG.md | 8 ++++++++ pkgs/path/lib/src/context.dart | 4 +++- pkgs/path/lib/src/internal_style.dart | 7 ++++--- pkgs/path/lib/src/style/posix.dart | 2 +- pkgs/path/lib/src/style/url.dart | 14 +++++++++++--- pkgs/path/lib/src/style/windows.dart | 2 +- pkgs/path/lib/src/utils.dart | 10 ++++++++++ pkgs/path/test/url_test.dart | 22 +++++++++++++++++++++- 8 files changed, 59 insertions(+), 10 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 2818f7be..3a43eea1 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.4.1 + +* Root-relative URLs like `/foo` are now resolved relative to the drive letter + for `file` URLs that begin with a Windows-style drive letter. This matches the + [WHATWG URL specification][]. + +[WHATWG URL specification]: https://url.spec.whatwg.org/#file-slash-state + ## 1.4.0 * Add `equals()`, `hash()` and `canonicalize()` top-level functions and diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index af97f908..c5ebb69e 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -245,7 +245,9 @@ class Context { // If the new part is root-relative, it preserves the previous root but // replaces the path after it. var parsed = _parse(part); - parsed.root = this.rootPrefix(buffer.toString()); + var path = buffer.toString(); + parsed.root = path.substring( + 0, style.rootLength(path, withDrive: true)); if (style.needsSeparator(parsed.root)) { parsed.separators[0] = style.separator; } diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 1874afcb..21897d6e 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -33,10 +33,11 @@ abstract class InternalStyle extends Style { /// Returns the number of characters of the root part. /// - /// Returns 0 if the path is relative. + /// Returns 0 if the path is relative and 1 if the path is root-relative. /// - /// If the path is root-relative, the root length is 1. - int rootLength(String path); + /// If [withDrive] is `true`, this should include the drive letter for `file:` + /// URLs. Non-URL styles may ignore the parameter. + int rootLength(String path, {bool withDrive: false}); /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 6f183fc3..5044d437 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -28,7 +28,7 @@ class PosixStyle extends InternalStyle { bool needsSeparator(String path) => path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); - int rootLength(String path) { + int rootLength(String path, {bool withDrive: false}) { if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1; return 0; } diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 659275cd..f4bab642 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -4,6 +4,7 @@ import '../characters.dart' as chars; import '../internal_style.dart'; +import '../utils.dart'; /// The style for URL paths. class UrlStyle extends InternalStyle { @@ -36,16 +37,23 @@ class UrlStyle extends InternalStyle { return path.endsWith("://") && rootLength(path) == path.length; } - int rootLength(String path) { + int rootLength(String path, {bool withDrive: false}) { if (path.isEmpty) return 0; if (isSeparator(path.codeUnitAt(0))) return 1; + var index = path.indexOf("/"); if (index > 0 && path.startsWith('://', index - 1)) { // The root part is up until the next '/', or the full path. Skip // '://' and search for '/' after that. index = path.indexOf('/', index + 2); - if (index > 0) return index; - return path.length; + if (index <= 0) return path.length; + + // file: URLs sometimes consider Windows drive letters part of the root. + // See https://url.spec.whatwg.org/#file-slash-state. + if (!withDrive || path.length < index + 3) return index; + if (!path.startsWith('file://')) return index; + if (!isDriveLetter(path, index + 1)) return index; + return path.length == index + 3 ? index + 3 : index + 4; } return 0; } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 57989af2..ed54ab9c 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -36,7 +36,7 @@ class WindowsStyle extends InternalStyle { return !isSeparator(path.codeUnitAt(path.length - 1)); } - int rootLength(String path) { + int rootLength(String path, {bool withDrive: false}) { if (path.isEmpty) return 0; if (path.codeUnitAt(0) == chars.SLASH) return 1; if (path.codeUnitAt(0) == chars.BACKSLASH) { diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index 64e471e3..3d71e560 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -12,3 +12,13 @@ bool isAlphabetic(int char) => /// Returns whether [char] is the code for an ASCII digit. bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; + +/// Returns whether [path] has a URL-formatted Windows drive letter beginning at +/// [index]. +bool isDriveLetter(String path, int index) { + if (path.length < index + 2) return false; + if (!isAlphabetic(path.codeUnitAt(index))) return false; + if (path.codeUnitAt(index + 1) != chars.COLON) return false; + if (path.length == index + 2) return true; + return path.codeUnitAt(index + 2) == chars.SLASH; +} diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 32c02da7..e8f34139 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -245,13 +245,33 @@ main() { expect(() => context.join(null, 'a'), throwsArgumentError); }); - test('Join does not modify internal ., .., or trailing separators', () { + test('does not modify internal ., .., or trailing separators', () { expect(context.join('a/', 'b/c/'), 'a/b/c/'); expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), 'a/b/./c/..//d/.././..//e/f//'); expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); + + test('treats drive letters as part of the root for file: URLs', () { + expect(context.join('file:///c:/foo/bar', '/baz/qux'), + 'file:///c:/baz/qux'); + expect(context.join('file:///D:/foo/bar', '/baz/qux'), + 'file:///D:/baz/qux'); + expect(context.join('file:///c:/', '/baz/qux'), 'file:///c:/baz/qux'); + expect(context.join('file:///c:', '/baz/qux'), 'file:///c:/baz/qux'); + expect(context.join('file://host/c:/foo/bar', '/baz/qux'), + 'file://host/c:/baz/qux'); + }); + + test('treats drive letters as normal components for non-file: URLs', () { + expect(context.join('http://foo.com/c:/foo/bar', '/baz/qux'), + 'http://foo.com/baz/qux'); + expect(context.join('misfile:///c:/foo/bar', '/baz/qux'), + 'misfile:///baz/qux'); + expect(context.join('filer:///c:/foo/bar', '/baz/qux'), + 'filer:///baz/qux'); + }); }); group('joinAll', () { From aff49ac0165fa6afc5ac9b16ae53d83ac40822b2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Dec 2016 15:36:29 -0800 Subject: [PATCH 081/183] File URL "/foo" is root-relative for Windows. IE interprets the "/foo" file URL as though it were relative to the current drive on Windows. We now match that behavior in p.windows.fromUri(). See dart-lang/path#18 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/src/style/windows.dart | 9 +++++++-- pkgs/path/test/windows_test.dart | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 3a43eea1..f7dde679 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -6,6 +6,10 @@ [WHATWG URL specification]: https://url.spec.whatwg.org/#file-slash-state +* When a root-relative URLs like `/foo` is converted to a Windows path using + `fromUrl()`, it is now resolved relative to the drive letter. This matches + IE's behavior. + ## 1.4.0 * Add `equals()`, `hash()` and `canonicalize()` top-level functions and diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index ed54ab9c..31114dbb 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -78,8 +78,13 @@ class WindowsStyle extends InternalStyle { var path = uri.path; if (uri.host == '') { // Drive-letter paths look like "file:///C:/path/to/file". The - // replaceFirst removes the extra initial slash. - if (path.startsWith('/')) path = path.replaceFirst("/", ""); + // replaceFirst removes the extra initial slash. Otherwise, leave the + // slash to match IE's interpretation of "/foo" as a root-relative path. + if (path.length >= 3 && + path.startsWith('/') && + isDriveLetter(path, 1)) { + path = path.replaceFirst("/", ""); + } } else { // Network paths look like "file://hostname/path/to/file". path = '\\\\${uri.host}$path'; diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index f4453a7c..ad587744 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -704,6 +704,7 @@ main() { r'\\server\share\path\to\foo#bar'); expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); + expect(context.fromUri(Uri.parse('/foo')), r'\foo'); expect(() => context.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); From d86d9b626ed5f0acaf77867d15369dc70e380781 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 8 Dec 2016 13:16:28 -0800 Subject: [PATCH 082/183] Bump the version to 1.4.1. (dart-lang/path#21) --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 87980ea5..48e799b8 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.4.0 +version: 1.4.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know From 9de016f9e7526efffb079a934123a17e701897cb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 8 Jun 2017 15:16:50 -0700 Subject: [PATCH 083/183] Fix a normalization bug. (dart-lang/path#26) Closes dart-lang/path#24 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/src/context.dart | 2 +- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/windows_test.dart | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index f7dde679..67c849c9 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.2 + +* Normalize `c:\foo\.` to `c:\foo`. + ## 1.4.1 * Root-relative URLs like `/foo` are now resolved relative to the drive letter diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index c5ebb69e..a00ca29a 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -397,7 +397,7 @@ class Context { // Single dots and double dots are normalized to directory traversals. if (previous == chars.PERIOD && (previousPrevious == null || - previousPrevious == chars.SLASH || + style.isSeparator(previousPrevious) || previousPrevious == chars.PERIOD)) { return true; } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 48e799b8..ff101d68 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.4.1 +version: 1.4.2-dev author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index ad587744..364db343 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -330,6 +330,7 @@ main() { test('eliminates "." parts', () { expect(context.normalize(r'.\'), '.'); expect(context.normalize(r'c:\.'), r'c:\'); + expect(context.normalize(r'c:\foo\.'), r'c:\foo'); expect(context.normalize(r'B:\.\'), r'B:\'); expect(context.normalize(r'\\server\share\.'), r'\\server\share'); expect(context.normalize(r'.\.'), '.'); @@ -351,6 +352,7 @@ main() { expect( context.normalize(r'\\server\share\..\../..\a'), r'\\server\share\a'); expect(context.normalize(r'c:\..'), r'c:\'); + expect(context.normalize(r'c:\foo\..'), r'c:\'); expect(context.normalize(r'A:/..\..\..'), r'A:\'); expect(context.normalize(r'b:\..\..\..\a'), r'b:\a'); expect(context.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); From 24cdc70dad0d3dd32cebde63e0a7ba1f50d2a8f8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 8 Jun 2017 15:18:50 -0700 Subject: [PATCH 084/183] Treat "package:" URLs as absolute. (dart-lang/path#25) Closes dart-lang/path#22 --- pkgs/path/CHANGELOG.md | 2 ++ pkgs/path/lib/src/style/url.dart | 32 +++++++++++++++++---------- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/url_test.dart | 37 ++++++++++++++++++++++---------- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 67c849c9..b29d2ab6 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,5 +1,7 @@ ## 1.4.2 +* Treat `package:` URLs as absolute. + * Normalize `c:\foo\.` to `c:\foo`. ## 1.4.1 diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index f4bab642..2e29aa70 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -41,19 +41,29 @@ class UrlStyle extends InternalStyle { if (path.isEmpty) return 0; if (isSeparator(path.codeUnitAt(0))) return 1; + for (var i = 0; i < path.length; i++) { + var codeUnit = path.codeUnitAt(i); + if (isSeparator(codeUnit)) return 0; + if (codeUnit == chars.COLON) { + if (i == 0) return 0; + + // The root part is up until the next '/', or the full path. Skip ':' + // (and '//' if it exists) and search for '/' after that. + if (path.startsWith('//', i + 1)) i += 3; + var index = path.indexOf('/', i); + if (index <= 0) return path.length; + + // file: URLs sometimes consider Windows drive letters part of the root. + // See https://url.spec.whatwg.org/#file-slash-state. + if (!withDrive || path.length < index + 3) return index; + if (!path.startsWith('file://')) return index; + if (!isDriveLetter(path, index + 1)) return index; + return path.length == index + 3 ? index + 3 : index + 4; + } + } + var index = path.indexOf("/"); if (index > 0 && path.startsWith('://', index - 1)) { - // The root part is up until the next '/', or the full path. Skip - // '://' and search for '/' after that. - index = path.indexOf('/', index + 2); - if (index <= 0) return path.length; - - // file: URLs sometimes consider Windows drive letters part of the root. - // See https://url.spec.whatwg.org/#file-slash-state. - if (!withDrive || path.length < index + 3) return index; - if (!path.startsWith('file://')) return index; - if (!isDriveLetter(path, index + 1)) return index; - return path.length == index + 3 ? index + 3 : index + 4; } return 0; } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index ff101d68..2c90e1cf 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.4.2-dev +version: 1.4.2 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index e8f34139..b8f4e0fb 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -40,6 +40,8 @@ main() { expect(context.rootPrefix('file://'), 'file://'); expect(context.rootPrefix('/'), '/'); expect(context.rootPrefix('foo/bar://'), ''); + expect(context.rootPrefix('package:foo/bar.dart'), 'package:foo'); + expect(context.rootPrefix('foo/bar:baz/qux'), ''); }); test('dirname', () { @@ -136,8 +138,10 @@ main() { expect(context.isAbsolute('~'), false); expect(context.isAbsolute('.'), false); expect(context.isAbsolute('../a'), false); - expect(context.isAbsolute('C:/a'), false); - expect(context.isAbsolute(r'C:\a'), false); + expect(context.isAbsolute('C:/a'), true); + expect(context.isAbsolute(r'C:\a'), true); + expect(context.isAbsolute('package:foo/bar.dart'), true); + expect(context.isAbsolute('foo/bar:baz/qux'), false); expect(context.isAbsolute(r'\\a'), false); }); @@ -159,8 +163,10 @@ main() { expect(context.isRelative('~'), true); expect(context.isRelative('.'), true); expect(context.isRelative('../a'), true); - expect(context.isRelative('C:/a'), true); - expect(context.isRelative(r'C:\a'), true); + expect(context.isRelative('C:/a'), false); + expect(context.isRelative(r'C:\a'), false); + expect(context.isRelative(r'package:foo/bar.dart'), false); + expect(context.isRelative('foo/bar:baz/qux'), true); expect(context.isRelative(r'\\a'), true); }); @@ -184,6 +190,8 @@ main() { expect(context.isRootRelative('../a'), false); expect(context.isRootRelative('C:/a'), false); expect(context.isRootRelative(r'C:\a'), false); + expect(context.isRootRelative(r'package:foo/bar.dart'), false); + expect(context.isRootRelative('foo/bar:baz/qux'), false); expect(context.isRootRelative(r'\\a'), false); }); @@ -216,7 +224,9 @@ main() { 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); expect(context.join('a', '/b', '/c', 'd'), '/c/d'); - expect(context.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'c:\b/c/d'); + expect(context.join('a', 'package:foo/bar', 'c', 'd'), + r'package:foo/bar/c/d'); expect(context.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); }); @@ -225,6 +235,8 @@ main() { 'http://dartlang.org/b/c'); expect(context.join('file://', 'a', '/b', 'c'), 'file:///b/c'); expect(context.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); + expect(context.join('package:foo/bar.dart', '/baz.dart'), + 'package:foo/baz.dart'); }); test('ignores trailing nulls', () { @@ -294,7 +306,9 @@ main() { 'd' ]), 'http://dartlang.org/c/d'); expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); - expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b/c/d'); + expect(context.joinAll(['a', 'package:foo/bar', 'c', 'd']), + r'package:foo/bar/c/d'); expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); }); @@ -405,8 +419,9 @@ main() { 'http://dartlang.org/a'); expect(context.normalize('file:///../../../a'), 'file:///a'); expect(context.normalize('/../../../a'), '/a'); - expect(context.normalize('c:/..'), '.'); - expect(context.normalize('A:/../../..'), '../..'); + expect(context.normalize('c:/..'), 'c:'); + expect(context.normalize('package:foo/..'), 'package:foo'); + expect(context.normalize('A:/../../..'), 'A:'); expect(context.normalize('a/..'), '.'); expect(context.normalize('a/b/..'), 'a'); expect(context.normalize('a/../b'), 'b'); @@ -430,7 +445,8 @@ main() { expect(context.normalize('a/..'), '.'); expect(context.normalize('../a'), '../a'); expect(context.normalize('/../a'), '/a'); - expect(context.normalize('c:/../a'), 'a'); + expect(context.normalize('c:/../a'), 'c:/a'); + expect(context.normalize('package:foo/../a'), 'package:foo/a'); expect(context.normalize('/../a'), '/a'); expect(context.normalize('a/b/..'), 'a'); expect(context.normalize('../a/b/..'), '../a'); @@ -778,8 +794,7 @@ main() { test('ignores parts before an absolute path', () { expect(context.absolute('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); expect(context.absolute('a', '/b', 'file:///c', 'd'), 'file:///c/d'); - expect(context.absolute('a', r'c:\b', 'c', 'd'), - r'http://dartlang.org/root/path/a/c:\b/c/d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), r'c:\b/c/d'); expect(context.absolute('a', r'\\b', 'c', 'd'), r'http://dartlang.org/root/path/a/\\b/c/d'); }); From a8474269c4b0d2ca656a6b309d9abd01ccf24cd3 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev Date: Thu, 15 Jun 2017 02:01:39 +0400 Subject: [PATCH 085/183] Fix backslashes in README.md (dart-lang/path#29) --- pkgs/path/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 58034706..ee346a60 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -97,8 +97,8 @@ Given that, we've decided this library should simply treat paths as strings. We believe this library handles most of the corner cases of Windows paths (POSIX paths are generally pretty straightforward): - * It understands that *both* "/" and "\" are valid path separators, not just - "\". + * It understands that *both* "/" and "\\" are valid path separators, not just + "\\". * It can accurately tell if a path is absolute based on drive-letters or UNC prefix. From 21373d36489b57538777b27afe9a99cffde7b0c3 Mon Sep 17 00:00:00 2001 From: Alexey Knyazev Date: Thu, 6 Jul 2017 02:47:56 +0400 Subject: [PATCH 086/183] Strong mode cleanup (dart-lang/path#30) --- pkgs/path/benchmark/benchmark.dart | 6 ++--- pkgs/path/lib/src/context.dart | 37 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 183b921c..946693f3 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -39,7 +39,7 @@ void main(List args) { var context = new p.Context(style: style); var files = genericPaths.toList()..addAll(platformPaths[style]); - benchmark(name, function) { + benchmark(String name, Function function) { runBenchmark("${style.name}-$name", 100000, () { for (var file in files) { function(file); @@ -47,7 +47,7 @@ void main(List args) { }); } - benchmarkPairs(name, function) { + benchmarkPairs(String name, Function function) { runBenchmark("${style.name}-$name", 1000, () { for (var file1 in files) { for (var file2 in files) { @@ -68,7 +68,7 @@ void main(List args) { benchmark('isRootRelative', context.isRootRelative); benchmark('normalize', context.normalize); benchmark('relative', context.relative); - benchmarkPairs('relative from', (file, from) { + benchmarkPairs('relative from', (String file, String from) { try { return context.relative(file, from: from); } on p.PathException { diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index a00ca29a..108955b3 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -343,8 +343,8 @@ class Context { bool _needsNormalization(String path) { var start = 0; var codeUnits = path.codeUnits; - var previousPrevious; - var previous; + int previousPrevious; + int previous; // Skip past the root before we start looking for snippets that need // normalization. We want to normalize "//", but not when it's part of @@ -566,7 +566,7 @@ class Context { var result = _isWithinOrEqualsFast(parent, child); if (result != _PathRelation.inconclusive) return result; - var relative; + String relative; try { relative = this.relative(child, from: parent); } on PathException catch (_) { @@ -958,10 +958,7 @@ class Context { /// If [uri] is relative, a relative path will be returned. /// /// path.fromUri('path/to/foo'); // -> 'path/to/foo' - String fromUri(uri) { - if (uri is String) uri = Uri.parse(uri); - return style.pathFromUri(uri); - } + String fromUri(uri) => style.pathFromUri(_parseUri(uri)); /// Returns the URI that represents [path]. /// @@ -1013,13 +1010,16 @@ class Context { /// // -> r'a/b.dart' /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' String prettyUri(uri) { - if (uri is String) uri = Uri.parse(uri); - if (uri.scheme == 'file' && style == Style.url) return uri.toString(); - if (uri.scheme != 'file' && uri.scheme != '' && style != Style.url) { - return uri.toString(); + var typedUri = _parseUri(uri); + if (typedUri.scheme == 'file' && style == Style.url) { + return typedUri.toString(); + } else if (typedUri.scheme != 'file' && + typedUri.scheme != '' && + style != Style.url) { + return typedUri.toString(); } - var path = normalize(fromUri(uri)); + var path = normalize(fromUri(typedUri)); var rel = relative(path); // Only return a relative path if it's actually shorter than the absolute @@ -1031,14 +1031,23 @@ class Context { ParsedPath _parse(String path) => new ParsedPath.parse(path, style); } +/// Parses argument if it's a [String] or returns it intact if it's a [Uri]. +/// +/// Throws an [ArgumentError] otherwise. +Uri _parseUri(uri) { + if (uri is String) return Uri.parse(uri); + if (uri is Uri) return uri; + throw new ArgumentError.value(uri, 'uri', 'Value must be a String or a Uri'); +} + /// Validates that there are no non-null arguments following a null one and /// throws an appropriate [ArgumentError] on failure. -_validateArgList(String method, List args) { +void _validateArgList(String method, List args) { for (var i = 1; i < args.length; i++) { // Ignore nulls hanging off the end. if (args[i] == null || args[i - 1] != null) continue; - var numArgs; + int numArgs; for (numArgs = args.length; numArgs >= 1; numArgs--) { if (args[numArgs - 1] != null) break; } From f1d63d2f44f1be955ced81e79c1a349768065f77 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 19 Oct 2017 12:17:12 -0700 Subject: [PATCH 087/183] Rename .analysis_options to analysis_options.yaml (dart-lang/path#31) --- pkgs/path/{.analysis_options => analysis_options.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkgs/path/{.analysis_options => analysis_options.yaml} (100%) diff --git a/pkgs/path/.analysis_options b/pkgs/path/analysis_options.yaml similarity index 100% rename from pkgs/path/.analysis_options rename to pkgs/path/analysis_options.yaml From 6bc40fb22825dc6af9f45fea5f78a7303c92db76 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 6 Nov 2017 12:49:03 -0800 Subject: [PATCH 088/183] Suggest the prefix "p" in documentation We had been suggesting "path", even though it tends to conflict with variable names and thus isn't what most packages use. --- pkgs/path/README.md | 10 +-- pkgs/path/lib/path.dart | 135 +++++++++++++++++++--------------------- 2 files changed, 71 insertions(+), 74 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index ee346a60..a72ceec8 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -7,7 +7,9 @@ We've tried very hard to make this library do the "right" thing on whatever platform you run it on, including in the browser. When you use the top-level functions, it will assume the current platform's path style and work with that. If you want to explicitly work with paths of a specific style, you can -construct a `path.Context` for that style. +construct a [`p.Context`][Context] for that style. + +[Context]: https://www.dartdocs.org/documentation/path/latest/path/Context-class.html ## Using @@ -15,7 +17,7 @@ The path library was designed to be imported with a prefix, though you don't have to if you don't want to: ```dart -import 'package:path/path.dart' as path; +import 'package:path/path.dart' as p; ``` The most common way to use the library is through the top-level functions. @@ -23,7 +25,7 @@ These manipulate path strings based on your current working directory and the path style (POSIX, Windows, or URLs) of the host platform. For example: ```dart -path.join("directory", "file.txt"); +p.join("directory", "file.txt"); ``` This calls the top-level [join] function to join "directory" and @@ -34,7 +36,7 @@ underlying platform that the program is running on, you can create a [Context] and give it an explicit [Style]: ```dart -var context = new path.Context(style: Style.windows); +var context = new p.Context(style: Style.windows); context.join("directory", "file.txt"); ``` diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index deb1b53b..fda0c51f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -24,13 +24,13 @@ /// The path library was designed to be imported with a prefix, though you don't /// have to if you don't want to: /// -/// import 'package:path/path.dart' as path; +/// import 'package:path/path.dart' as p; /// /// The most common way to use the library is through the top-level functions. /// These manipulate path strings based on your current working directory and /// the path style (POSIX, Windows, or URLs) of the host platform. For example: /// -/// path.join("directory", "file.txt"); +/// p.join("directory", "file.txt"); /// /// This calls the top-level [join] function to join "directory" and "file.txt" /// using the current platform's directory separator. @@ -39,7 +39,7 @@ /// underlying platform that the program is running on, you can create a /// [Context] and give it an explicit [Style]: /// -/// var context = new path.Context(style: Style.windows); +/// var context = new p.Context(style: Style.windows); /// context.join("directory", "file.txt"); /// /// This will join "directory" and "file.txt" using the Windows path separator, @@ -117,66 +117,66 @@ String get separator => context.separator; /// Creates a new path by appending the given path parts to [current]. /// Equivalent to [join()] with [current] as the first argument. Example: /// -/// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +/// p.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' String absolute(String part1, [String part2, String part3, String part4, String part5, String part6, String part7]) => context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// -/// path.basename('path/to/foo.dart'); // -> 'foo.dart' -/// path.basename('path/to'); // -> 'to' +/// p.basename('path/to/foo.dart'); // -> 'foo.dart' +/// p.basename('path/to'); // -> 'to' /// /// Trailing separators are ignored. /// -/// path.basename('path/to/'); // -> 'to' +/// p.basename('path/to/'); // -> 'to' String basename(String path) => context.basename(path); /// Gets the part of [path] after the last separator, and without any trailing /// file extension. /// -/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' +/// p.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' /// /// Trailing separators are ignored. /// -/// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' +/// p.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => context.basenameWithoutExtension(path); /// Gets the part of [path] before the last separator. /// -/// path.dirname('path/to/foo.dart'); // -> 'path/to' -/// path.dirname('path/to'); // -> 'path' +/// p.dirname('path/to/foo.dart'); // -> 'path/to' +/// p.dirname('path/to'); // -> 'path' /// /// Trailing separators are ignored. /// -/// path.dirname('path/to/'); // -> 'path' +/// p.dirname('path/to/'); // -> 'path' /// /// If an absolute path contains no directories, only a root, then the root /// is returned. /// -/// path.dirname('/'); // -> '/' (posix) -/// path.dirname('c:\'); // -> 'c:\' (windows) +/// p.dirname('/'); // -> '/' (posix) +/// p.dirname('c:\'); // -> 'c:\' (windows) /// /// If a relative path has no directories, then '.' is returned. /// -/// path.dirname('foo'); // -> '.' -/// path.dirname(''); // -> '.' +/// p.dirname('foo'); // -> '.' +/// p.dirname(''); // -> '.' String dirname(String path) => context.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last /// `.` to the end (including the `.` itself). /// -/// path.extension('path/to/foo.dart'); // -> '.dart' -/// path.extension('path/to/foo'); // -> '' -/// path.extension('path.to/foo'); // -> '' -/// path.extension('path/to/foo.dart.js'); // -> '.js' +/// p.extension('path/to/foo.dart'); // -> '.dart' +/// p.extension('path/to/foo'); // -> '' +/// p.extension('path.to/foo'); // -> '' +/// p.extension('path/to/foo.dart.js'); // -> '.js' /// /// If the file name starts with a `.`, then that is not considered the /// extension: /// -/// path.extension('~/.bashrc'); // -> '' -/// path.extension('~/.notes.txt'); // -> '.txt' +/// p.extension('~/.bashrc'); // -> '' +/// p.extension('~/.notes.txt'); // -> '.txt' String extension(String path) => context.extension(path); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. @@ -184,16 +184,16 @@ String extension(String path) => context.extension(path); /// relative. /// /// // Unix -/// path.rootPrefix('path/to/foo'); // -> '' -/// path.rootPrefix('/path/to/foo'); // -> '/' +/// p.rootPrefix('path/to/foo'); // -> '' +/// p.rootPrefix('/path/to/foo'); // -> '/' /// /// // Windows -/// path.rootPrefix(r'path\to\foo'); // -> '' -/// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +/// p.rootPrefix(r'path\to\foo'); // -> '' +/// p.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' /// /// // URL -/// path.rootPrefix('path/to/foo'); // -> '' -/// path.rootPrefix('http://dartlang.org/path/to/foo'); +/// p.rootPrefix('path/to/foo'); // -> '' +/// p.rootPrefix('http://dartlang.org/path/to/foo'); /// // -> 'http://dartlang.org' String rootPrefix(String path) => context.rootPrefix(path); @@ -230,16 +230,16 @@ bool isRootRelative(String path) => context.isRootRelative(path); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: /// -/// path.join('path', 'to', 'foo'); // -> 'path/to/foo' +/// p.join('path', 'to', 'foo'); // -> 'path/to/foo' /// /// If any part ends in a path separator, then a redundant separator will not /// be added: /// -/// path.join('path/', 'to', 'foo'); // -> 'path/to/foo +/// p.join('path/', 'to', 'foo'); // -> 'path/to/foo /// /// If a part is an absolute path, then anything before that will be ignored: /// -/// path.join('path', '/to', 'foo'); // -> '/to/foo' +/// p.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) => context.join(part1, part2, part3, part4, part5, part6, part7, part8); @@ -247,16 +247,16 @@ String join(String part1, [String part2, String part3, String part4, /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: /// -/// path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' +/// p.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' /// /// If any part ends in a path separator, then a redundant separator will not /// be added: /// -/// path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo +/// p.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo /// /// If a part is an absolute path, then anything before that will be ignored: /// -/// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' +/// p.joinAll(['path', '/to', 'foo']); // -> '/to/foo' /// /// For a fixed number of parts, [join] is usually terser. String joinAll(Iterable parts) => context.joinAll(parts); @@ -264,23 +264,23 @@ String joinAll(Iterable parts) => context.joinAll(parts); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. /// -/// path.split('path/to/foo'); // -> ['path', 'to', 'foo'] +/// p.split('path/to/foo'); // -> ['path', 'to', 'foo'] /// /// The path will *not* be normalized before splitting. /// -/// path.split('path/../foo'); // -> ['path', '..', 'foo'] +/// p.split('path/../foo'); // -> ['path', '..', 'foo'] /// /// If [path] is absolute, the root directory will be the first element in the /// array. Example: /// /// // Unix -/// path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] +/// p.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] /// /// // Windows -/// path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +/// p.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] /// /// // Browser -/// path.split('http://dartlang.org/path/to/foo'); +/// p.split('http://dartlang.org/path/to/foo'); /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] List split(String path) => context.split(path); @@ -305,22 +305,21 @@ String canonicalize(String path) => context.canonicalize(path); /// equivalent input paths. For that, see [canonicalize]. Or, if you're using /// paths as map keys, pass [equals] and [hash] to [new HashMap]. /// -/// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' +/// p.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) => context.normalize(path); /// Attempts to convert [path] to an equivalent relative path from the current /// directory. /// /// // Given current directory is /root/path: -/// path.relative('/root/path/a/b.dart'); // -> 'a/b.dart' -/// path.relative('/root/other.dart'); // -> '../other.dart' +/// p.relative('/root/path/a/b.dart'); // -> 'a/b.dart' +/// p.relative('/root/other.dart'); // -> '../other.dart' /// /// If the [from] argument is passed, [path] is made relative to that instead. /// -/// path.relative('/root/path/a/b.dart', -/// from: '/root/path'); // -> 'a/b.dart' -/// path.relative('/root/other.dart', -/// from: '/root/path'); // -> '../other.dart' +/// p.relative('/root/path/a/b.dart', from: '/root/path'); // -> 'a/b.dart' +/// p.relative('/root/other.dart', from: '/root/path'); +/// // -> '../other.dart' /// /// If [path] and/or [from] are relative paths, they are assumed to be relative /// to the current directory. @@ -330,19 +329,19 @@ String normalize(String path) => context.normalize(path); /// in those cases. /// /// // Windows -/// path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' +/// p.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' /// /// // URL -/// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); +/// p.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); /// // -> 'http://dartlang.org' String relative(String path, {String from}) => context.relative(path, from: from); /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. /// -/// path.isWithin('/root/path', '/root/path/a'); // -> true -/// path.isWithin('/root/path', '/root/other'); // -> false -/// path.isWithin('/root/path', '/root/path') // -> false +/// p.isWithin('/root/path', '/root/path/a'); // -> true +/// p.isWithin('/root/path', '/root/other'); // -> false +/// p.isWithin('/root/path', '/root/path') // -> false bool isWithin(String parent, String child) => context.isWithin(parent, child); /// Returns `true` if [path1] points to the same location as [path2], and @@ -361,7 +360,7 @@ int hash(String path) => context.hash(path); /// Removes a trailing extension from the last part of [path]. /// -/// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' +/// p.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) => context.withoutExtension(path); /// Returns the path represented by [uri], which may be a [String] or a [Uri]. @@ -370,20 +369,18 @@ String withoutExtension(String path) => context.withoutExtension(path); /// style, this will just convert [uri] to a string. /// /// // POSIX -/// context.fromUri('file:///path/to/foo') -/// // -> '/path/to/foo' +/// p.fromUri('file:///path/to/foo') // -> '/path/to/foo' /// /// // Windows -/// context.fromUri('file:///C:/path/to/foo') -/// // -> r'C:\path\to\foo' +/// p.fromUri('file:///C:/path/to/foo') // -> r'C:\path\to\foo' /// /// // URL -/// context.fromUri('http://dartlang.org/path/to/foo') +/// p.fromUri('http://dartlang.org/path/to/foo') /// // -> 'http://dartlang.org/path/to/foo' /// /// If [uri] is relative, a relative path will be returned. /// -/// path.fromUri('path/to/foo'); // -> 'path/to/foo' +/// p.fromUri('path/to/foo'); // -> 'path/to/foo' String fromUri(uri) => context.fromUri(uri); /// Returns the URI that represents [path]. @@ -392,21 +389,20 @@ String fromUri(uri) => context.fromUri(uri); /// style, this will just convert [path] to a [Uri]. /// /// // POSIX -/// path.toUri('/path/to/foo') +/// p.toUri('/path/to/foo') /// // -> Uri.parse('file:///path/to/foo') /// /// // Windows -/// path.toUri(r'C:\path\to\foo') +/// p.toUri(r'C:\path\to\foo') /// // -> Uri.parse('file:///C:/path/to/foo') /// /// // URL -/// path.toUri('http://dartlang.org/path/to/foo') +/// p.toUri('http://dartlang.org/path/to/foo') /// // -> Uri.parse('http://dartlang.org/path/to/foo') /// /// If [path] is relative, a relative URI will be returned. /// -/// path.toUri('path/to/foo') -/// // -> Uri.parse('path/to/foo') +/// p.toUri('path/to/foo') // -> Uri.parse('path/to/foo') Uri toUri(String path) => context.toUri(path); /// Returns a terse, human-readable representation of [uri]. @@ -419,15 +415,14 @@ Uri toUri(String path) => context.toUri(path); /// or path-formatted. /// /// // POSIX at "/root/path" -/// path.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' -/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// p.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' +/// p.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' /// /// // Windows at "C:\root\path" -/// path.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' -/// path.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// p.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' +/// p.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' /// /// // URL at "http://dartlang.org/root/path" -/// path.prettyUri('http://dartlang.org/root/path/a/b.dart'); -/// // -> r'a/b.dart' -/// path.prettyUri('file:///root/path'); // -> 'file:///root/path' +/// p.prettyUri('http://dartlang.org/root/path/a/b.dart'); // -> r'a/b.dart' +/// p.prettyUri('file:///root/path'); // -> 'file:///root/path' String prettyUri(uri) => context.prettyUri(uri); From 4e5d6979e277cc3bd1a08b67eada979cc45fe2c2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 6 Nov 2017 13:02:18 -0800 Subject: [PATCH 089/183] Add changeExtension() Closes dart-lang/path#32 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/path.dart | 12 ++++++++++++ pkgs/path/lib/src/context.dart | 14 ++++++++++++++ pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 19 +++++++++++++++++++ pkgs/path/test/url_test.dart | 19 +++++++++++++++++++ pkgs/path/test/windows_test.dart | 19 +++++++++++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index b29d2ab6..b5fbeb9e 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.0 + +* Add a `setExtension()` top-level function and `Context` method. + ## 1.4.2 * Treat `package:` URLs as absolute. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index fda0c51f..d3c7512d 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -363,6 +363,18 @@ int hash(String path) => context.hash(path); /// p.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) => context.withoutExtension(path); +/// Returns [path] with the trailing extension set to [extension]. +/// +/// If [path] doesn't have a trailing extension, this just adds [extension] to +/// the end. +/// +/// p.setExtension('path/to/foo.dart', '.js') // -> 'path/to/foo.js' +/// p.setExtension('path/to/foo.dart.js', '.map') +/// // -> 'path/to/foo.dart.map' +/// p.setExtension('path/to/foo', '.js') // -> 'path/to/foo.js' +String setExtension(String path, String extension) => + context.setExtension(path, extension); + /// Returns the path represented by [uri], which may be a [String] or a [Uri]. /// /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 108955b3..8d022e09 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -938,6 +938,20 @@ class Context { return parsed.toString(); } + /// Returns [path] with the trailing extension set to [extension]. + /// + /// If [path] doesn't have a trailing extension, this just adds [extension] to + /// the end. + /// + /// context.setExtension('path/to/foo.dart', '.js') + /// // -> 'path/to/foo.js' + /// context.setExtension('path/to/foo.dart.js', '.map') + /// // -> 'path/to/foo.dart.map' + /// context.setExtension('path/to/foo', '.js') + /// // -> 'path/to/foo.js' + String setExtension(String path, String extension) => + withoutExtension(path) + extension; + /// Returns the path represented by [uri], which may be a [String] or a [Uri]. /// /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 2c90e1cf..5e94e09c 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.4.2 +version: 1.5.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 4549ebdf..df3e6c59 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -534,6 +534,25 @@ main() { expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); + test('setExtension', () { + expect(context.setExtension('', '.x'), '.x'); + expect(context.setExtension('a', '.x'), 'a.x'); + expect(context.setExtension('.a', '.x'), '.a.x'); + expect(context.setExtension('a.b', '.x'), 'a.x'); + expect(context.setExtension('a/b.c', '.x'), 'a/b.x'); + expect(context.setExtension('a/b.c.d', '.x'), 'a/b.c.x'); + expect(context.setExtension('a/', '.x'), 'a/.x'); + expect(context.setExtension('a/b/', '.x'), 'a/b/.x'); + expect(context.setExtension('a/.', '.x'), 'a/..x'); + expect(context.setExtension('a/.b', '.x'), 'a/.b.x'); + expect(context.setExtension('a.b/c', '.x'), 'a.b/c.x'); + expect(context.setExtension(r'a.b\c', '.x'), r'a.x'); + expect(context.setExtension(r'a/b\c', '.x'), r'a/b\c.x'); + expect(context.setExtension(r'a/b\c.d', '.x'), r'a/b\c.x'); + expect(context.setExtension('a/b.c/', '.x'), 'a/b/.x'); + expect(context.setExtension('a/b.c//', '.x'), 'a/b//.x'); + }); + group('fromUri', () { test('with a URI', () { expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index b8f4e0fb..8f912b7d 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -819,6 +819,25 @@ main() { expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); + test('withoutExtension', () { + expect(context.setExtension('', '.x'), '.x'); + expect(context.setExtension('a', '.x'), 'a.x'); + expect(context.setExtension('.a', '.x'), '.a.x'); + expect(context.setExtension('a.b', '.x'), 'a.x'); + expect(context.setExtension('a/b.c', '.x'), 'a/b.x'); + expect(context.setExtension('a/b.c.d', '.x'), 'a/b.c.x'); + expect(context.setExtension('a/', '.x'), 'a/.x'); + expect(context.setExtension('a/b/', '.x'), 'a/b/.x'); + expect(context.setExtension('a/.', '.x'), 'a/..x'); + expect(context.setExtension('a/.b', '.x'), 'a/.b.x'); + expect(context.setExtension('a.b/c', '.x'), 'a.b/c.x'); + expect(context.setExtension(r'a.b\c', '.x'), r'a.x'); + expect(context.setExtension(r'a/b\c', '.x'), r'a/b\c.x'); + expect(context.setExtension(r'a/b\c.d', '.x'), r'a/b\c.x'); + expect(context.setExtension('a/b.c/', '.x'), 'a/b/.x'); + expect(context.setExtension('a/b.c//', '.x'), 'a/b//.x'); + }); + group('fromUri', () { test('with a URI', () { expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 364db343..e24a3fcc 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -684,6 +684,25 @@ main() { expect(context.withoutExtension(r'a\b.c\'), r'a\b\'); }); + test('withoutExtension', () { + expect(context.setExtension('', '.x'), '.x'); + expect(context.setExtension('a', '.x'), 'a.x'); + expect(context.setExtension('.a', '.x'), '.a.x'); + expect(context.setExtension('a.b', '.x'), 'a.x'); + expect(context.setExtension(r'a\b.c', '.x'), r'a\b.x'); + expect(context.setExtension(r'a\b.c.d', '.x'), r'a\b.c.x'); + expect(context.setExtension(r'a\', '.x'), r'a\.x'); + expect(context.setExtension(r'a\b\', '.x'), r'a\b\.x'); + expect(context.setExtension(r'a\.', '.x'), r'a\..x'); + expect(context.setExtension(r'a\.b', '.x'), r'a\.b.x'); + expect(context.setExtension(r'a.b\c', '.x'), r'a.b\c.x'); + expect(context.setExtension(r'a/b.c/d', '.x'), r'a/b.c/d.x'); + expect(context.setExtension(r'a\b/c', '.x'), r'a\b/c.x'); + expect(context.setExtension(r'a\b/c.d', '.x'), r'a\b/c.x'); + expect(context.setExtension(r'a.b/c', '.x'), r'a.b/c.x'); + expect(context.setExtension(r'a\b.c\', '.x'), r'a\b\.x'); + }); + group('fromUri', () { test('with a URI', () { expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), From 63002aaf5d91ecb2078b8111eb7993b52331c7bc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 14 Nov 2017 20:00:34 -0800 Subject: [PATCH 090/183] Enable Travis-CI --- pkgs/path/.travis.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pkgs/path/.travis.yml diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml new file mode 100644 index 00000000..63f5e5ee --- /dev/null +++ b/pkgs/path/.travis.yml @@ -0,0 +1,22 @@ +language: dart + +dart: + - dev + - stable + +dart_task: + - test + - dartanalyzer + +matrix: + include: + - dart: dev + dart_task: dartfmt + +# Only building master means that we don't run two builds for each pull request. +branches: + only: [master] + +cache: + directories: + - $HOME/.pub-cache From 792e09204201a291a7d4895fa8b807746eb99522 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 14 Nov 2017 20:32:17 -0800 Subject: [PATCH 091/183] dartfmt --- pkgs/path/benchmark/benchmark.dart | 4 +- pkgs/path/lib/path.dart | 23 ++++++--- pkgs/path/lib/src/context.dart | 44 ++++++++++------- pkgs/path/lib/src/parsed_path.dart | 11 +++-- pkgs/path/lib/src/style/url.dart | 3 +- pkgs/path/lib/src/style/windows.dart | 8 ++- pkgs/path/lib/src/utils.dart | 2 +- pkgs/path/test/url_test.dart | 73 +++++++++++++++++----------- 8 files changed, 103 insertions(+), 65 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 946693f3..c741fa33 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -22,7 +22,9 @@ final platformPaths = { '/', '/home/user/dart/sdk/lib/indexed_db/dart2js/indexed_db_dart2js.dart', ], - p.Style.url: ['https://example.server.org/443643002/path?top=yes#fragment',], + p.Style.url: [ + 'https://example.server.org/443643002/path?top=yes#fragment', + ], p.Style.windows: [ r'C:\User\me\', r'\\server\share\my\folders\some\file.data', diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index d3c7512d..8e87c33f 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -118,9 +118,14 @@ String get separator => context.separator; /// Equivalent to [join()] with [current] as the first argument. Example: /// /// p.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' -String absolute(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) => - context.absolute(part1, part2, part3, part4, part5, part6, part7); +String absolute(String part1, + [String part2, + String part3, + String part4, + String part5, + String part6, + String part7]) => + context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// @@ -240,9 +245,15 @@ bool isRootRelative(String path) => context.isRootRelative(path); /// If a part is an absolute path, then anything before that will be ignored: /// /// p.join('path', '/to', 'foo'); // -> '/to/foo' -String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) => - context.join(part1, part2, part3, part4, part5, part6, part7, part8); +String join(String part1, + [String part2, + String part3, + String part4, + String part5, + String part6, + String part7, + String part8]) => + context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 8d022e09..badd2473 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -72,8 +72,13 @@ class Context { /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' /// /// If [current] isn't absolute, this won't return an absolute path. - String absolute(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) { + String absolute(String part1, + [String part2, + String part3, + String part4, + String part5, + String part6, + String part7]) { _validateArgList( "absolute", [part1, part2, part3, part4, part5, part6, part7]); @@ -205,8 +210,14 @@ class Context { /// /// context.join('path', '/to', 'foo'); // -> '/to/foo' /// - String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) { + String join(String part1, + [String part2, + String part3, + String part4, + String part5, + String part6, + String part7, + String part8]) { var parts = [ part1, part2, @@ -246,8 +257,8 @@ class Context { // replaces the path after it. var parsed = _parse(part); var path = buffer.toString(); - parsed.root = path.substring( - 0, style.rootLength(path, withDrive: true)); + parsed.root = + path.substring(0, style.rootLength(path, withDrive: true)); if (style.needsSeparator(parsed.root)) { parsed.separators[0] = style.separator; } @@ -378,8 +389,8 @@ class Context { // enough that it's probably not going to cause performance issues. if (previous == chars.PERIOD && (previousPrevious == null || - previousPrevious == chars.PERIOD || - style.isSeparator(previousPrevious))) { + previousPrevious == chars.PERIOD || + style.isSeparator(previousPrevious))) { return true; } } @@ -397,8 +408,8 @@ class Context { // Single dots and double dots are normalized to directory traversals. if (previous == chars.PERIOD && (previousPrevious == null || - style.isSeparator(previousPrevious) || - previousPrevious == chars.PERIOD)) { + style.isSeparator(previousPrevious) || + previousPrevious == chars.PERIOD)) { return true; } @@ -493,8 +504,8 @@ class Context { if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { throw new PathException('Unable to find a path to "$path" from "$from".'); } - pathParsed.parts.insertAll( - 0, new List.filled(fromParsed.parts.length, '..')); + pathParsed.parts + .insertAll(0, new List.filled(fromParsed.parts.length, '..')); pathParsed.separators[0] = ''; pathParsed.separators.insertAll( 1, new List.filled(fromParsed.parts.length, style.separator)); @@ -580,7 +591,7 @@ class Context { if (relative == '..') return _PathRelation.different; return (relative.length >= 3 && relative.startsWith('..') && - style.isSeparator(relative.codeUnitAt(2))) + style.isSeparator(relative.codeUnitAt(2))) ? _PathRelation.different : _PathRelation.within; } @@ -739,8 +750,8 @@ class Context { lastParentSeparator ??= math.max(0, parentRootLength - 1); } - var direction = _pathDirection(parent, - lastParentSeparator ?? parentRootLength - 1); + var direction = + _pathDirection(parent, lastParentSeparator ?? parentRootLength - 1); if (direction == _PathDirection.atRoot) return _PathRelation.equal; return direction == _PathDirection.aboveRoot ? _PathRelation.inconclusive @@ -907,7 +918,7 @@ class Context { if (!beginning && next == chars.PERIOD && (i + 2 == path.length || - style.isSeparator(path.codeUnitAt(i + 2)))) { + style.isSeparator(path.codeUnitAt(i + 2)))) { return null; } } @@ -1132,4 +1143,3 @@ class _PathRelation { String toString() => name; } - diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 619ffbf4..811e9f49 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -129,10 +129,13 @@ class ParsedPath { // Canonicalize separators. var newSeparators = new List.generate( - newParts.length, (_) => style.separator, growable: true); - newSeparators.insert(0, isAbsolute && - newParts.length > 0 && - style.needsSeparator(root) ? style.separator : ''); + newParts.length, (_) => style.separator, + growable: true); + newSeparators.insert( + 0, + isAbsolute && newParts.length > 0 && style.needsSeparator(root) + ? style.separator + : ''); parts = newParts; separators = newSeparators; diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 2e29aa70..9dc44422 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -63,8 +63,7 @@ class UrlStyle extends InternalStyle { } var index = path.indexOf("/"); - if (index > 0 && path.startsWith('://', index - 1)) { - } + if (index > 0 && path.startsWith('://', index - 1)) {} return 0; } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 31114dbb..c55d85e6 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -80,9 +80,7 @@ class WindowsStyle extends InternalStyle { // Drive-letter paths look like "file:///C:/path/to/file". The // replaceFirst removes the extra initial slash. Otherwise, leave the // slash to match IE's interpretation of "/foo" as a root-relative path. - if (path.length >= 3 && - path.startsWith('/') && - isDriveLetter(path, 1)) { + if (path.length >= 3 && path.startsWith('/') && isDriveLetter(path, 1)) { path = path.replaceFirst("/", ""); } } else { @@ -123,8 +121,8 @@ class WindowsStyle extends InternalStyle { // Get rid of the trailing "\" in "C:\" because the URI constructor will // add a separator on its own. - parsed.parts.insert( - 0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); + parsed.parts + .insert(0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); return new Uri(scheme: 'file', pathSegments: parsed.parts); } diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index 3d71e560..561ff632 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -8,7 +8,7 @@ import 'characters.dart' as chars; /// lowercase). bool isAlphabetic(int char) => (char >= chars.UPPER_A && char <= chars.UPPER_Z) || - (char >= chars.LOWER_A && char <= chars.LOWER_Z); + (char >= chars.LOWER_A && char <= chars.LOWER_Z); /// Returns whether [char] is the code for an ASCII digit. bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 8f912b7d..91e43394 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -220,7 +220,8 @@ main() { expect(context.join('a', '/', 'b', 'c'), '/b/c'); expect(context.join('a', '/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); - expect(context.join( + expect( + context.join( 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); expect(context.join('a', '/b', '/c', 'd'), '/c/d'); @@ -266,10 +267,10 @@ main() { }); test('treats drive letters as part of the root for file: URLs', () { - expect(context.join('file:///c:/foo/bar', '/baz/qux'), - 'file:///c:/baz/qux'); - expect(context.join('file:///D:/foo/bar', '/baz/qux'), - 'file:///D:/baz/qux'); + expect( + context.join('file:///c:/foo/bar', '/baz/qux'), 'file:///c:/baz/qux'); + expect( + context.join('file:///D:/foo/bar', '/baz/qux'), 'file:///D:/baz/qux'); expect(context.join('file:///c:/', '/baz/qux'), 'file:///c:/baz/qux'); expect(context.join('file:///c:', '/baz/qux'), 'file:///c:/baz/qux'); expect(context.join('file://host/c:/foo/bar', '/baz/qux'), @@ -281,8 +282,8 @@ main() { 'http://foo.com/baz/qux'); expect(context.join('misfile:///c:/foo/bar', '/baz/qux'), 'misfile:///baz/qux'); - expect(context.join('filer:///c:/foo/bar', '/baz/qux'), - 'filer:///baz/qux'); + expect( + context.join('filer:///c:/foo/bar', '/baz/qux'), 'filer:///baz/qux'); }); }); @@ -299,12 +300,10 @@ main() { expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); expect(context.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(context.joinAll([ - 'a', - 'http://google.com/b', - 'http://dartlang.org/c', - 'd' - ]), 'http://dartlang.org/c/d'); + expect( + context.joinAll( + ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), + 'http://dartlang.org/c/d'); expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b/c/d'); expect(context.joinAll(['a', 'package:foo/bar', 'c', 'd']), @@ -538,10 +537,14 @@ main() { // Regression test('from root-only path', () { - expect(context.relative('http://dartlang.org', - from: 'http://dartlang.org'), '.'); - expect(context.relative('http://dartlang.org/root/path', - from: 'http://dartlang.org'), 'root/path'); + expect( + context.relative('http://dartlang.org', + from: 'http://dartlang.org'), + '.'); + expect( + context.relative('http://dartlang.org/root/path', + from: 'http://dartlang.org'), + 'root/path'); }); }); @@ -608,10 +611,14 @@ main() { expect( context.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(context.relative('http://dartlang.org/foo/bar/baz', - from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect(context.relative('http://dartlang.org/foo/bar/baz', - from: 'http://dartlang.org/foo/bar'), equals('baz')); + expect( + context.relative('http://dartlang.org/foo/bar/baz', + from: 'file:///foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect( + context.relative('http://dartlang.org/foo/bar/baz', + from: 'http://dartlang.org/foo/bar'), + equals('baz')); expect(context.relative('/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect(context.relative('file:///foo/bar/baz', from: '/foo/bar'), @@ -642,10 +649,14 @@ main() { equals('/foo/bar/baz')); expect(r.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect(r.relative('http://dartlang.org/foo/bar/baz', - from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect(r.relative('http://dartlang.org/foo/bar/baz', - from: 'http://dartlang.org/foo/bar'), equals('baz')); + expect( + r.relative('http://dartlang.org/foo/bar/baz', + from: 'file:///foo/bar'), + equals('http://dartlang.org/foo/bar/baz')); + expect( + r.relative('http://dartlang.org/foo/bar/baz', + from: 'http://dartlang.org/foo/bar'), + equals('baz')); expect(r.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), equals('http://dartlang.org/foo/bar/baz')); @@ -673,10 +684,14 @@ main() { expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect(context.isWithin( - 'http://dartlang.org', 'http://dartlang.org/foo/bar'), isTrue); - expect(context.isWithin( - 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), isFalse); + expect( + context.isWithin( + 'http://dartlang.org', 'http://dartlang.org/foo/bar'), + isTrue); + expect( + context.isWithin( + 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), + isFalse); expect(context.isWithin('http://dartlang.org', '/foo/bar'), isTrue); expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); expect(context.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); From 532b3ef1c43120cc74a539f8e027c9089069095d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 15 Nov 2017 13:34:34 -0800 Subject: [PATCH 092/183] Remove unused block and local var --- pkgs/path/lib/src/style/url.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 9dc44422..6860b9ac 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -62,8 +62,6 @@ class UrlStyle extends InternalStyle { } } - var index = path.indexOf("/"); - if (index > 0 && path.startsWith('://', index - 1)) {} return 0; } From 436e58f9168e30a6b160539bb64f2c70bba502d0 Mon Sep 17 00:00:00 2001 From: jensjoha Date: Tue, 21 Nov 2017 22:35:10 +0100 Subject: [PATCH 093/183] Fix absolute working directory bugs on POSIX (dart-lang/path#36) Closes dart-lang/path#35 --- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/lib/path.dart | 5 +++-- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/io_test.dart | 21 +++++++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index b5fbeb9e..e23e3801 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.5.1 + +* Fix a number of bugs that occurred when the current working directory was `/` + on Linux or Mac OS. + ## 1.5.0 * Add a `setExtension()` top-level function and `Context` method. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 8e87c33f..98858598 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -91,10 +91,11 @@ String get current { return _current; } else { var path = uri.toFilePath(); - // Remove trailing '/' or '\'. + // Remove trailing '/' or '\' unless it is the only thing left + // (for instance the root on Linux). var lastIndex = path.length - 1; assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); - _current = path.substring(0, lastIndex); + _current = lastIndex == 0 ? path : path.substring(0, lastIndex); return _current; } } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 5e94e09c..30a8dff9 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.5.0 +version: 1.5.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index f15f82dd..82d8b097 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -55,4 +55,25 @@ main() { io.Directory.current = dir; } }); + + // Regression test for #35. This tests against the *actual* working directory + // rather than just a custom context because we do some processing in + // [path.current] that has clobbered the root in the past. + test('absolute works on root working directory', () { + var dir = path.current; + try { + io.Directory.current = path.rootPrefix(path.current); + + expect(path.relative(path.absolute('foo/bar'), from: path.current), + path.relative(path.absolute('foo/bar'))); + + expect(path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.join(path.current, '../foo/bar')))); + + expect(path.normalize(path.absolute('foo/bar')), + equals(path.normalize(path.join(path.current, '../foo/bar')))); + } finally { + io.Directory.current = dir; + } + }); } From 1b9e0284a73e7a5efc14a0785d1d5cf3dcd4661f Mon Sep 17 00:00:00 2001 From: BC Ko Date: Thu, 24 May 2018 02:40:29 -0700 Subject: [PATCH 094/183] Update .gitignore to new `dart_tool` pub cache dart-lang/sdkdart-lang/path#32030 --- pkgs/path/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/path/.gitignore b/pkgs/path/.gitignore index 7dbf0350..813a31ec 100644 --- a/pkgs/path/.gitignore +++ b/pkgs/path/.gitignore @@ -1,6 +1,7 @@ # Don’t commit the following directories created by pub. .buildlog .pub/ +.dart_tool/ build/ packages .packages @@ -12,4 +13,4 @@ packages *.js.map # Include when developing application packages. -pubspec.lock \ No newline at end of file +pubspec.lock From fe6f25627d0108dbae2112be2a276b3b38de46aa Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 4 Jun 2018 19:54:26 -0400 Subject: [PATCH 095/183] Add a PathMap class --- pkgs/path/CHANGELOG.md | 4 ++ pkgs/path/lib/path.dart | 1 + pkgs/path/lib/src/path_map.dart | 38 +++++++++++++++ pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/path_map_test.dart | 80 +++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 pkgs/path/lib/src/path_map.dart create mode 100644 pkgs/path/test/path_map_test.dart diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index e23e3801..3dd398c8 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.0 + +* Add a `PathMap` class that uses path equality for its keys. + ## 1.5.1 * Fix a number of bugs that occurred when the current working directory was `/` diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 98858598..7348a343 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -49,6 +49,7 @@ import 'src/style.dart'; export 'src/context.dart' hide createInternal; export 'src/path_exception.dart'; +export 'src/path_map.dart'; export 'src/style.dart'; /// A default context for manipulating POSIX paths. diff --git a/pkgs/path/lib/src/path_map.dart b/pkgs/path/lib/src/path_map.dart new file mode 100644 index 00000000..53205ad1 --- /dev/null +++ b/pkgs/path/lib/src/path_map.dart @@ -0,0 +1,38 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; + +import '../path.dart' as p; + +/// A map whose keys are paths, compared using [equals] and [hash]. +class PathMap extends MapView { + /// Creates an empty [PathMap] whose keys are compared using `context.equals` + /// and `context.hash`. + /// + /// The [context] defaults to the current path context. + PathMap({p.Context context}) : super(_create(context)); + + /// Creates a [PathMap] with the same keys and values as [other] whose keys + /// are compared using `context.equals` and `context.hash`. + /// + /// The [context] defaults to the current path context. If multiple keys in + /// [other] represent the same logical path, the last key's value will be + /// used. + PathMap.of(Map other, {p.Context context}) + : super(_create(context)..addAll(other)); + + /// Creates a map that uses [context] for equality and hashing. + static Map _create(p.Context context) { + context ??= p.context; + return new LinkedHashMap( + equals: (path1, path2) { + if (path1 == null) return path2 == null; + if (path2 == null) return false; + return context.equals(path1, path2); + }, + hashCode: (path) => path == null ? 0 : context.hash(path), + isValidKey: (path) => path is String || path == null); + } +} diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 30a8dff9..e3c477c4 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.5.1 +version: 1.6.0 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/path_map_test.dart b/pkgs/path/test/path_map_test.dart new file mode 100644 index 00000000..ce025db2 --- /dev/null +++ b/pkgs/path/test/path_map_test.dart @@ -0,0 +1,80 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:test/test.dart'; + +import 'package:path/path.dart'; + +void main() { + group("considers equal", () { + test("two identical paths", () { + var map = new PathMap(); + map[join("foo", "bar")] = 1; + map[join("foo", "bar")] = 2; + expect(map, hasLength(1)); + expect(map, containsPair(join("foo", "bar"), 2)); + }); + + test("two logically equivalent paths", () { + var map = new PathMap(); + map["foo"] = 1; + map[absolute("foo")] = 2; + expect(map, hasLength(1)); + expect(map, containsPair("foo", 2)); + expect(map, containsPair(absolute("foo"), 2)); + }); + + test("two nulls", () { + var map = new PathMap(); + map[null] = 1; + map[null] = 2; + expect(map, hasLength(1)); + expect(map, containsPair(null, 2)); + }); + }); + + group("considers unequal", () { + test("two distinct paths", () { + var map = new PathMap(); + map["foo"] = 1; + map["bar"] = 2; + expect(map, hasLength(2)); + expect(map, containsPair("foo", 1)); + expect(map, containsPair("bar", 2)); + }); + + test("a path and null", () { + var map = new PathMap(); + map["foo"] = 1; + map[null] = 2; + expect(map, hasLength(2)); + expect(map, containsPair("foo", 1)); + expect(map, containsPair(null, 2)); + }); + }); + + test("uses the custom context", () { + var map = new PathMap(context: windows); + map["FOO"] = 1; + map["foo"] = 2; + expect(map, hasLength(1)); + expect(map, containsPair("fOo", 2)); + }); + + group(".of()", () { + test("copies the existing map's keys", () { + var map = new PathMap.of({"foo": 1, "bar": 2}); + expect(map, hasLength(2)); + expect(map, containsPair("foo", 1)); + expect(map, containsPair("bar", 2)); + }); + + test("uses the second value in the case of duplicates", () { + var map = new PathMap.of({"foo": 1, absolute("foo"): 2}); + expect(map, hasLength(1)); + expect(map, containsPair("foo", 2)); + expect(map, containsPair(absolute("foo"), 2)); + }); + }); +} From 628ea1209c7b144c79aba19c35de73626ca23ee6 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 4 Jun 2018 20:34:21 -0400 Subject: [PATCH 096/183] Add a PathSet class --- pkgs/path/CHANGELOG.md | 2 + pkgs/path/lib/path.dart | 1 + pkgs/path/lib/src/path_set.dart | 83 +++++++++++++++++++++++++++++++ pkgs/path/test/path_set_test.dart | 81 ++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 pkgs/path/lib/src/path_set.dart create mode 100644 pkgs/path/test/path_set_test.dart diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 3dd398c8..61e17883 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -2,6 +2,8 @@ * Add a `PathMap` class that uses path equality for its keys. +* Add a `PathSet` class that uses path equality for its contents. + ## 1.5.1 * Fix a number of bugs that occurred when the current working directory was `/` diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 7348a343..ed70f9d5 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -50,6 +50,7 @@ import 'src/style.dart'; export 'src/context.dart' hide createInternal; export 'src/path_exception.dart'; export 'src/path_map.dart'; +export 'src/path_set.dart'; export 'src/style.dart'; /// A default context for manipulating POSIX paths. diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart new file mode 100644 index 00000000..98f08a92 --- /dev/null +++ b/pkgs/path/lib/src/path_set.dart @@ -0,0 +1,83 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; + +import '../path.dart' as p; + +/// A set containing paths, compared using [equals] and [hash]. +class PathSet extends IterableBase implements Set { + /// The set to which we forward implementation methods. + final Set _inner; + + /// Creates an empty [PathSet] whose contents are compared using + /// `context.equals` and `context.hash`. + /// + /// The [context] defaults to the current path context. + PathSet({p.Context context}) : _inner = _create(context); + + /// Creates a [PathSet] with the same contents as [other] whose elements are + /// compared using `context.equals` and `context.hash`. + /// + /// The [context] defaults to the current path context. If multiple elements + /// in [other] represent the same logical path, the first value will be + /// used. + PathSet.of(Iterable other, {p.Context context}) + : _inner = _create(context)..addAll(other); + + /// Creates a set that uses [context] for equality and hashing. + static Set _create(p.Context context) { + context ??= p.context; + return new LinkedHashSet( + equals: (path1, path2) { + if (path1 == null) return path2 == null; + if (path2 == null) return false; + return context.equals(path1, path2); + }, + hashCode: (path) => path == null ? 0 : context.hash(path), + isValidKey: (path) => path is String || path == null); + } + + // Normally we'd use DelegatingSetView from the collection package to + // implement these, but we want to avoid adding dependencies from path because + // it's so widely used that even brief version skew can be very painful. + + Iterator get iterator => _inner.iterator; + + int get length => _inner.length; + + bool add(String value) => _inner.add(value); + + void addAll(Iterable elements) => _inner.addAll(elements); + + Set cast() => _inner.cast(); + + void clear() => _inner.clear(); + + bool contains(Object other) => _inner.contains(other); + + bool containsAll(Iterable other) => _inner.containsAll(other); + + Set difference(Set other) => _inner.difference(other); + + Set intersection(Set other) => _inner.intersection(other); + + String lookup(Object element) => _inner.lookup(element); + + bool remove(Object value) => _inner.remove(value); + + void removeAll(Iterable elements) => _inner.removeAll(elements); + + void removeWhere(bool test(String element)) => _inner.removeWhere(test); + + void retainAll(Iterable elements) => _inner.retainAll(elements); + + Set retype() => _inner.retype(); + + void retainWhere(bool test(String element)) => _inner.retainWhere(test); + + Set union(Set other) => _inner.union(other); + + Set toSet() => _inner.toSet(); +} diff --git a/pkgs/path/test/path_set_test.dart b/pkgs/path/test/path_set_test.dart new file mode 100644 index 00000000..884c1840 --- /dev/null +++ b/pkgs/path/test/path_set_test.dart @@ -0,0 +1,81 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:test/test.dart'; + +import 'package:path/path.dart'; + +void main() { + group("considers equal", () { + test("two identical paths", () { + var set = new PathSet(); + expect(set.add(join("foo", "bar")), isTrue); + expect(set.add(join("foo", "bar")), isFalse); + expect(set, hasLength(1)); + expect(set, contains(join("foo", "bar"))); + }); + + test("two logically equivalent paths", () { + var set = new PathSet(); + expect(set.add("foo"), isTrue); + expect(set.add(absolute("foo")), isFalse); + expect(set, hasLength(1)); + expect(set, contains("foo")); + expect(set, contains(absolute("foo"))); + }); + + test("two nulls", () { + var set = new PathSet(); + expect(set.add(null), isTrue); + expect(set.add(null), isFalse); + expect(set, hasLength(1)); + expect(set, contains(null)); + }); + }); + + group("considers unequal", () { + test("two distinct paths", () { + var set = new PathSet(); + expect(set.add("foo"), isTrue); + expect(set.add("bar"), isTrue); + expect(set, hasLength(2)); + expect(set, contains("foo")); + expect(set, contains("bar")); + }); + + test("a path and null", () { + var set = new PathSet(); + expect(set.add("foo"), isTrue); + expect(set.add(null), isTrue); + expect(set, hasLength(2)); + expect(set, contains("foo")); + expect(set, contains(null)); + }); + }); + + test("uses the custom context", () { + var set = new PathSet(context: windows); + expect(set.add("FOO"), isTrue); + expect(set.add("foo"), isFalse); + expect(set, hasLength(1)); + expect(set, contains("fOo")); + }); + + group(".of()", () { + test("copies the existing set's keys", () { + var set = new PathSet.of(["foo", "bar"]); + expect(set, hasLength(2)); + expect(set, contains("foo")); + expect(set, contains("bar")); + }); + + test("uses the first value in the case of duplicates", () { + var set = new PathSet.of(["foo", absolute("foo")]); + expect(set, hasLength(1)); + expect(set, contains("foo")); + expect(set, contains(absolute("foo"))); + expect(set.first, "foo"); + }); + }); +} From 29a641c1efebc84682bb49bc7719587a5c97d0ef Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 4 Jun 2018 21:03:55 -0400 Subject: [PATCH 097/183] Only support Dart 2 This is necessary because we're calling Set.cast() and Set.retype(). --- pkgs/path/.travis.yml | 5 +++-- pkgs/path/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index 63f5e5ee..140ce597 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -2,16 +2,17 @@ language: dart dart: - dev - - stable dart_task: - test - - dartanalyzer matrix: include: - dart: dev dart_task: dartfmt + include: + - dart: dev + dart_task: dartanalyzer # Only building master means that we don't run two builds for each pull request. branches: diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index e3c477c4..a9a4cdb1 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -9,4 +9,4 @@ homepage: http://github.com/dart-lang/path dev_dependencies: test: ">=0.12.0 <0.13.0" environment: - sdk: ">=1.0.0 <2.0.0" + sdk: ">=2.0.0-dev.35.0 <2.0.0" From 9b3f3e2ef7d8fe483a5303845c14060061eb7212 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 6 Jun 2018 07:18:46 -0700 Subject: [PATCH 098/183] Fix .travis.yml (dart-lang/path#41) --- pkgs/path/.travis.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index 140ce597..fc8e7680 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -5,14 +5,8 @@ dart: dart_task: - test - -matrix: - include: - - dart: dev - dart_task: dartfmt - include: - - dart: dev - dart_task: dartanalyzer + - dartfmt + - dartanalyze # Only building master means that we don't run two builds for each pull request. branches: From fc80294748b0bde99dee30861e276eecb9b34ae0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 13 Jun 2018 12:20:34 -0700 Subject: [PATCH 099/183] Drop implementation of `retype` (dart-lang/path#42) Technically it's possible that there exists a reference using `PathSet` and calling `retype` but the liklihood is very small and since we're in the `-dev` versions of the SDK it's not worth marking this a breaking change. --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/src/path_set.dart | 2 -- pkgs/path/pubspec.yaml | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 61e17883..42731adb 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.1 + +* Drop the `retype` implementation for compatibility with the latest SDK. + ## 1.6.0 * Add a `PathMap` class that uses path equality for its keys. diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index 98f08a92..e3b9461a 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -73,8 +73,6 @@ class PathSet extends IterableBase implements Set { void retainAll(Iterable elements) => _inner.retainAll(elements); - Set retype() => _inner.retype(); - void retainWhere(bool test(String element)) => _inner.retainWhere(test); Set union(Set other) => _inner.union(other); diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index a9a4cdb1..ece564fb 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.6.0 +version: 1.6.1 author: Dart Team description: > A string-based path manipulation library. All of the path operations you know @@ -9,4 +9,4 @@ homepage: http://github.com/dart-lang/path dev_dependencies: test: ">=0.12.0 <0.13.0" environment: - sdk: ">=2.0.0-dev.35.0 <2.0.0" + sdk: ">=2.0.0-dev.62.0 <2.0.0" From a66a2b5d5ef427e23e713f3200d71e266879ae87 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Tue, 17 Jul 2018 17:47:46 -0400 Subject: [PATCH 100/183] chore: set max SDK version to <3.0.0 (dart-lang/path#44) --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/README.md | 6 +++--- pkgs/path/analysis_options.yaml | 2 -- pkgs/path/pubspec.yaml | 19 +++++++++++-------- pkgs/path/test/path_test.dart | 2 ++ pkgs/path/test/utils.dart | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) delete mode 100644 pkgs/path/analysis_options.yaml diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 42731adb..19853b9f 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.2 + +* Set max SDK version to `<3.0.0`, and adjust other dependencies. + ## 1.6.1 * Drop the `retype` implementation for compatibility with the latest SDK. diff --git a/pkgs/path/README.md b/pkgs/path/README.md index a72ceec8..60991214 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -25,10 +25,10 @@ These manipulate path strings based on your current working directory and the path style (POSIX, Windows, or URLs) of the host platform. For example: ```dart -p.join("directory", "file.txt"); +p.join('directory', 'file.txt'); ``` -This calls the top-level [join] function to join "directory" and +This calls the top-level `join()` function to join "directory" and "file.txt" using the current platform's directory separator. If you want to work with paths for a specific platform regardless of the @@ -37,7 +37,7 @@ underlying platform that the program is running on, you can create a ```dart var context = new p.Context(style: Style.windows); -context.join("directory", "file.txt"); +context.join('directory', 'file.txt'); ``` This will join "directory" and "file.txt" using the Windows path separator, diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml deleted file mode 100644 index a10d4c5a..00000000 --- a/pkgs/path/analysis_options.yaml +++ /dev/null @@ -1,2 +0,0 @@ -analyzer: - strong-mode: true diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index ece564fb..5c22cb42 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,12 +1,15 @@ name: path -version: 1.6.1 -author: Dart Team +version: 1.6.2 + description: > - A string-based path manipulation library. All of the path operations you know - and love, with solid support on both Windows and POSIX (Linux and Mac OS X) - machines. + A string-based path manipulation library. All of the path operations you know + and love, with solid support on both Windows and POSIX (Linux and Mac OS X) + machines. +author: Dart Team homepage: http://github.com/dart-lang/path -dev_dependencies: - test: ">=0.12.0 <0.13.0" + environment: - sdk: ">=2.0.0-dev.62.0 <2.0.0" + sdk: '>=2.0.0-dev.62.0 <3.0.0' + +dev_dependencies: + test: '>=0.12.42 <2.0.0' diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index b45f8dca..782366c8 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -13,7 +13,9 @@ main() { }); test('separator', () { + // ignore: deprecated_member_use expect(path.Style.posix.separator, '/'); + // ignore: deprecated_member_use expect(path.Style.windows.separator, '\\'); }); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 5e22ce1f..79e95ec1 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -6,7 +6,7 @@ import "package:test/test.dart"; import "package:path/path.dart" as p; /// A matcher for a closure that throws a [path.PathException]. -final throwsPathException = throwsA(new isInstanceOf()); +final throwsPathException = throwsA(new TypeMatcher()); void expectEquals(p.Context context, String path1, String path2) { expect(context.equals(path1, path2), isTrue, From ede81bf7c9526ba925e2764256ccdb03b70ff40c Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 29 Aug 2018 11:38:24 -0700 Subject: [PATCH 101/183] Use backquotes for backslashes to avoid markdown escaping. (dart-lang/path#45) --- pkgs/path/lib/src/style.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 342a1028..5f689b07 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -13,11 +13,11 @@ abstract class Style { /// start with "/". Used by UNIX, Linux, Mac OS X, and others. static final Style posix = new PosixStyle(); - /// Windows paths use "\" (backslash) as separators. Absolute paths start with - /// a drive letter followed by a colon (example, "C:") or two backslashes - /// ("\\") for UNC paths. + /// Windows paths use `\` (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, `C:`) or two backslashes + /// (`\\`) for UNC paths. // TODO(rnystrom): The UNC root prefix should include the drive name too, not - // just the "\\". + // just the `\\`. static final Style windows = new WindowsStyle(); /// URLs aren't filesystem paths, but they're supported to make it easier to From de89a8a38e0a342b3e87e34d9151996e699e9ef1 Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Wed, 24 Jul 2019 15:02:10 -0700 Subject: [PATCH 102/183] Try to gracefully handle the cwd being deleted. --- pkgs/path/.gitignore | 2 +- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/lib/path.dart | 12 +++++++++++- pkgs/path/pubspec.yaml | 6 +++--- pkgs/path/test/io_test.dart | 26 ++++++++++++++++++++++++-- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/pkgs/path/.gitignore b/pkgs/path/.gitignore index 813a31ec..8608a826 100644 --- a/pkgs/path/.gitignore +++ b/pkgs/path/.gitignore @@ -2,8 +2,8 @@ .buildlog .pub/ .dart_tool/ +.idea/ build/ -packages .packages # Or the files created by dart2js. diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 19853b9f..129e440c 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.6.3 + +* Don't throw a FileSystemException from `current` if the working directory has + been deleted but we have a cached one we can use. + ## 1.6.2 * Set max SDK version to `<3.0.0`, and adjust other dependencies. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index ed70f9d5..9b7f1ab8 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -81,7 +81,17 @@ Style get style => context.style; /// /// In the browser, this means the current URL, without the last file segment. String get current { - var uri = Uri.base; + // If the current working directory gets deleted out from under the program, + // accessing it will throw an IO exception. In order to avoid transient + // errors, if we already have a cached working directory, catch the error and + // use that. + Uri uri; + try { + uri = Uri.base; + } on Exception { + if (_current != null) return _current; + rethrow; + } // Converting the base URI to a file path is pretty slow, and the base URI // rarely changes in practice, so we cache the result here. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 5c22cb42..504474b9 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,10 +1,10 @@ name: path -version: 1.6.2 +version: 1.6.3-dev description: > A string-based path manipulation library. All of the path operations you know - and love, with solid support on both Windows and POSIX (Linux and Mac OS X) - machines. + and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the + web. author: Dart Team homepage: http://github.com/dart-lang/path diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 82d8b097..7b9d49d9 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -35,8 +35,30 @@ main() { } }); - test('current', () { - expect(path.current, io.Directory.current.path); + group('current', () { + test('returns the current working directory', () { + expect(path.current, io.Directory.current.path); + }); + + test('uses the previous working directory if deleted', () { + var dir = io.Directory.current.path; + try { + var tempPath = path.normalize(path.absolute("temp_cwd")); + var temp = io.Directory(tempPath); + temp.createSync(); + io.Directory.current = temp; + + // Call "current" once so that it can be cached. + expect(path.normalize(path.absolute(path.current)), equals(tempPath)); + + temp.deleteSync(); + + // Even though the directory no longer exists, no exception is thrown. + expect(path.normalize(path.absolute(path.current)), equals(tempPath)); + } finally { + io.Directory.current = dir; + } + }); }); test('registers changes to the working directory', () { From c53e7cc588044132f1bb642dbfd13ad99a4c0a80 Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Wed, 24 Jul 2019 15:47:52 -0700 Subject: [PATCH 103/183] Run "dartfmt --fix". This mostly removes optional "new" and "const". Looks like there's a couple of lingering ":" for named parameters too. There are no substantive changes, aside from bumping versions in the pubspec to get ready to publish. --- pkgs/path/benchmark/benchmark.dart | 6 ++-- pkgs/path/lib/path.dart | 6 ++-- pkgs/path/lib/src/context.dart | 43 +++++++++++++-------------- pkgs/path/lib/src/internal_style.dart | 4 +-- pkgs/path/lib/src/parsed_path.dart | 14 ++++----- pkgs/path/lib/src/path_map.dart | 2 +- pkgs/path/lib/src/path_set.dart | 2 +- pkgs/path/lib/src/style.dart | 10 +++---- pkgs/path/lib/src/style/posix.dart | 14 ++++----- pkgs/path/lib/src/style/url.dart | 11 ++++--- pkgs/path/lib/src/style/windows.dart | 18 +++++------ pkgs/path/pubspec.yaml | 4 +-- pkgs/path/test/browser_test.dart | 6 ++-- pkgs/path/test/io_test.dart | 6 ++-- pkgs/path/test/path_map_test.dart | 16 +++++----- pkgs/path/test/path_set_test.dart | 16 +++++----- pkgs/path/test/path_test.dart | 4 +-- pkgs/path/test/posix_test.dart | 16 +++++----- pkgs/path/test/relative_test.dart | 18 +++++------ pkgs/path/test/url_test.dart | 16 +++++----- pkgs/path/test/utils.dart | 4 +-- pkgs/path/test/windows_test.dart | 19 ++++++------ 22 files changed, 124 insertions(+), 131 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index c741fa33..c3428160 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -5,7 +5,7 @@ import 'package:path/path.dart' as p; /// Some hopefully real-world representative platform-independent paths. -const genericPaths = const [ +const genericPaths = [ '.', '..', 'out/ReleaseIA32/packages', @@ -38,7 +38,7 @@ void main(List args) { arguments = args; for (var style in [p.Style.posix, p.Style.url, p.Style.windows]) { - var context = new p.Context(style: style); + var context = p.Context(style: style); var files = genericPaths.toList()..addAll(platformPaths[style]); benchmark(String name, Function function) { @@ -95,7 +95,7 @@ void runBenchmark(String name, int count, Function function) { function(); } - var stopwatch = new Stopwatch()..start(); + var stopwatch = Stopwatch()..start(); for (var i = 0; i < count; i++) { function(); } diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 9b7f1ab8..29e87ece 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -54,16 +54,16 @@ export 'src/path_set.dart'; export 'src/style.dart'; /// A default context for manipulating POSIX paths. -final Context posix = new Context(style: Style.posix); +final Context posix = Context(style: Style.posix); /// A default context for manipulating Windows paths. -final Context windows = new Context(style: Style.windows); +final Context windows = Context(style: Style.windows); /// A default context for manipulating URLs. /// /// URL path equality is undefined for paths that differ only in their /// percent-encoding or only in the case of their host segment. -final Context url = new Context(style: Style.url); +final Context url = Context(style: Style.url); /// The system path context. /// diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index badd2473..dc507ff7 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -11,7 +11,7 @@ import 'parsed_path.dart'; import 'path_exception.dart'; import '../path.dart' as p; -Context createInternal() => new Context._internal(); +Context createInternal() => Context._internal(); /// An instantiable class for manipulating paths. Unlike the top-level /// functions, this lets you explicitly select what platform the paths will use. @@ -37,11 +37,11 @@ class Context { if (style == null) { style = Style.platform; } else if (style is! InternalStyle) { - throw new ArgumentError("Only styles defined by the path package are " + throw ArgumentError("Only styles defined by the path package are " "allowed."); } - return new Context._(style as InternalStyle, current); + return Context._(style as InternalStyle, current); } /// Create a [Context] to be used internally within path. @@ -247,7 +247,7 @@ class Context { /// /// For a fixed number of parts, [join] is usually terser. String joinAll(Iterable parts) { - var buffer = new StringBuffer(); + var buffer = StringBuffer(); var needsSeparator = false; var isAbsoluteAndNotRootRelative = false; @@ -468,7 +468,7 @@ class Context { // If the path is still relative and `from` is absolute, we're unable to // find a path from `from` to `path`. if (this.isRelative(path) && this.isAbsolute(from)) { - throw new PathException('Unable to find a path to "$path" from "$from".'); + throw PathException('Unable to find a path to "$path" from "$from".'); } var fromParsed = _parse(from)..normalize(); @@ -502,13 +502,12 @@ class Context { // out of them. If a directory left in the from path is '..', it cannot // be cancelled by adding a '..'. if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { - throw new PathException('Unable to find a path to "$path" from "$from".'); + throw PathException('Unable to find a path to "$path" from "$from".'); } - pathParsed.parts - .insertAll(0, new List.filled(fromParsed.parts.length, '..')); + pathParsed.parts.insertAll(0, List.filled(fromParsed.parts.length, '..')); pathParsed.separators[0] = ''; - pathParsed.separators.insertAll( - 1, new List.filled(fromParsed.parts.length, style.separator)); + pathParsed.separators + .insertAll(1, List.filled(fromParsed.parts.length, style.separator)); // Corner case: the paths completely collapsed. if (pathParsed.parts.length == 0) return '.'; @@ -1053,7 +1052,7 @@ class Context { return split(rel).length > split(path).length ? path : rel; } - ParsedPath _parse(String path) => new ParsedPath.parse(path, style); + ParsedPath _parse(String path) => ParsedPath.parse(path, style); } /// Parses argument if it's a [String] or returns it intact if it's a [Uri]. @@ -1062,7 +1061,7 @@ class Context { Uri _parseUri(uri) { if (uri is String) return Uri.parse(uri); if (uri is Uri) return uri; - throw new ArgumentError.value(uri, 'uri', 'Value must be a String or a Uri'); + throw ArgumentError.value(uri, 'uri', 'Value must be a String or a Uri'); } /// Validates that there are no non-null arguments following a null one and @@ -1078,14 +1077,14 @@ void _validateArgList(String method, List args) { } // Show the arguments. - var message = new StringBuffer(); + var message = StringBuffer(); message.write("$method("); message.write(args .take(numArgs) .map((arg) => arg == null ? "null" : '"$arg"') .join(", ")); message.write("): part ${i - 1} was null, but part $i was not."); - throw new ArgumentError(message.toString()); + throw ArgumentError(message.toString()); } } @@ -1096,18 +1095,18 @@ class _PathDirection { /// /// Note that this applies even if the path ends beneath its original root. It /// takes precendence over any other return values that may apple. - static const aboveRoot = const _PathDirection("above root"); + static const aboveRoot = _PathDirection("above root"); /// The path contains enough ".." components that it ends at its original /// root. - static const atRoot = const _PathDirection("at root"); + static const atRoot = _PathDirection("at root"); /// The path contains enough ".." components that at some point it reaches its /// original root, but it ends beneath that root. - static const reachesRoot = const _PathDirection("reaches root"); + static const reachesRoot = _PathDirection("reaches root"); /// The path never reaches to or above its original root. - static const belowRoot = const _PathDirection("below root"); + static const belowRoot = _PathDirection("below root"); final String name; @@ -1121,21 +1120,21 @@ class _PathRelation { /// The first path is a proper parent of the second. /// /// For example, `foo` is a proper parent of `foo/bar`, but not of `foo`. - static const within = const _PathRelation("within"); + static const within = _PathRelation("within"); /// The two paths are equivalent. /// /// For example, `foo//bar` is equivalent to `foo/bar`. - static const equal = const _PathRelation("equal"); + static const equal = _PathRelation("equal"); /// The first path is neither a parent of nor equal to the second. - static const different = const _PathRelation("different"); + static const different = _PathRelation("different"); /// We couldn't quickly determine any information about the paths' /// relationship to each other. /// /// Only returned by [Context._isWithinOrEqualsFast]. - static const inconclusive = const _PathRelation("inconclusive"); + static const inconclusive = _PathRelation("inconclusive"); final String name; diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 21897d6e..8a7f2a62 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -37,7 +37,7 @@ abstract class InternalStyle extends Style { /// /// If [withDrive] is `true`, this should include the drive letter for `file:` /// URLs. Non-URL styles may ignore the parameter. - int rootLength(String path, {bool withDrive: false}); + int rootLength(String path, {bool withDrive = false}); /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. @@ -62,7 +62,7 @@ abstract class InternalStyle extends Style { // Ensure that a trailing slash in the path produces a trailing slash in the // URL. if (isSeparator(path.codeUnitAt(path.length - 1))) segments.add(''); - return new Uri(pathSegments: segments); + return Uri(pathSegments: segments); } /// Returns the URI that represents [path], which is assumed to be absolute. diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 811e9f49..6984591d 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -71,7 +71,7 @@ class ParsedPath { separators.add(''); } - return new ParsedPath._(style, root, isRootRelative, parts, separators); + return ParsedPath._(style, root, isRootRelative, parts, separators); } ParsedPath._( @@ -97,7 +97,7 @@ class ParsedPath { if (separators.length > 0) separators[separators.length - 1] = ''; } - void normalize({bool canonicalize: false}) { + void normalize({bool canonicalize = false}) { // Handle '.', '..', and empty parts. var leadingDoubles = 0; var newParts = []; @@ -119,7 +119,7 @@ class ParsedPath { // A relative path can back out from the start directory. if (!isAbsolute) { - newParts.insertAll(0, new List.filled(leadingDoubles, '..')); + newParts.insertAll(0, List.filled(leadingDoubles, '..')); } // If we collapsed down to nothing, do ".". @@ -128,7 +128,7 @@ class ParsedPath { } // Canonicalize separators. - var newSeparators = new List.generate( + var newSeparators = List.generate( newParts.length, (_) => style.separator, growable: true); newSeparators.insert( @@ -149,7 +149,7 @@ class ParsedPath { } String toString() { - var builder = new StringBuffer(); + var builder = StringBuffer(); if (root != null) builder.write(root); for (var i = 0; i < parts.length; i++) { builder.write(separators[i]); @@ -180,6 +180,6 @@ class ParsedPath { return [file.substring(0, lastDot), file.substring(lastDot)]; } - ParsedPath clone() => new ParsedPath._(style, root, isRootRelative, - new List.from(parts), new List.from(separators)); + ParsedPath clone() => ParsedPath._( + style, root, isRootRelative, List.from(parts), List.from(separators)); } diff --git a/pkgs/path/lib/src/path_map.dart b/pkgs/path/lib/src/path_map.dart index 53205ad1..7d560cdd 100644 --- a/pkgs/path/lib/src/path_map.dart +++ b/pkgs/path/lib/src/path_map.dart @@ -26,7 +26,7 @@ class PathMap extends MapView { /// Creates a map that uses [context] for equality and hashing. static Map _create(p.Context context) { context ??= p.context; - return new LinkedHashMap( + return LinkedHashMap( equals: (path1, path2) { if (path1 == null) return path2 == null; if (path2 == null) return false; diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index e3b9461a..6a2afd11 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -29,7 +29,7 @@ class PathSet extends IterableBase implements Set { /// Creates a set that uses [context] for equality and hashing. static Set _create(p.Context context) { context ??= p.context; - return new LinkedHashSet( + return LinkedHashSet( equals: (path1, path2) { if (path1 == null) return path2 == null; if (path2 == null) return false; diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 5f689b07..659c78c9 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -11,14 +11,14 @@ import 'style/windows.dart'; abstract class Style { /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths /// start with "/". Used by UNIX, Linux, Mac OS X, and others. - static final Style posix = new PosixStyle(); + static final Style posix = PosixStyle(); /// Windows paths use `\` (backslash) as separators. Absolute paths start with /// a drive letter followed by a colon (example, `C:`) or two backslashes /// (`\\`) for UNC paths. // TODO(rnystrom): The UNC root prefix should include the drive name too, not // just the `\\`. - static final Style windows = new WindowsStyle(); + static final Style windows = WindowsStyle(); /// URLs aren't filesystem paths, but they're supported to make it easier to /// manipulate URL paths in the browser. @@ -26,7 +26,7 @@ abstract class Style { /// URLs use "/" (forward slash) as separators. Absolute paths either start /// with a protocol and optional hostname (e.g. `http://dartlang.org`, /// `file://`) or with "/". - static final Style url = new UrlStyle(); + static final Style url = UrlStyle(); /// The style of the host platform. /// @@ -42,7 +42,7 @@ abstract class Style { // style to use. if (Uri.base.scheme != 'file') return Style.url; if (!Uri.base.path.endsWith('/')) return Style.url; - if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; + if (Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; return Style.posix; } @@ -50,7 +50,7 @@ abstract class Style { String get name; /// A [Context] that uses this style. - Context get context => new Context(style: this); + Context get context => Context(style: this); @Deprecated("Most Style members will be removed in path 2.0.") String get separator; diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 5044d437..a2388b45 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -16,9 +16,9 @@ class PosixStyle extends InternalStyle { // Deprecated properties. - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp(r'[^/]$'); - final rootPattern = new RegExp(r'^/'); + final separatorPattern = RegExp(r'/'); + final needsSeparatorPattern = RegExp(r'[^/]$'); + final rootPattern = RegExp(r'^/'); final relativeRootPattern = null; bool containsSeparator(String path) => path.contains('/'); @@ -28,7 +28,7 @@ class PosixStyle extends InternalStyle { bool needsSeparator(String path) => path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); - int rootLength(String path, {bool withDrive: false}) { + int rootLength(String path, {bool withDrive = false}) { if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1; return 0; } @@ -41,11 +41,11 @@ class PosixStyle extends InternalStyle { if (uri.scheme == '' || uri.scheme == 'file') { return Uri.decodeComponent(uri.path); } - throw new ArgumentError("Uri $uri must have scheme 'file:'."); + throw ArgumentError("Uri $uri must have scheme 'file:'."); } Uri absolutePathToUri(String path) { - var parsed = new ParsedPath.parse(path, this); + var parsed = ParsedPath.parse(path, this); if (parsed.parts.isEmpty) { // If the path is a bare root (e.g. "/"), [components] will // currently be empty. We add two empty components so the URL constructor @@ -57,6 +57,6 @@ class PosixStyle extends InternalStyle { parsed.parts.add(""); } - return new Uri(scheme: 'file', pathSegments: parsed.parts); + return Uri(scheme: 'file', pathSegments: parsed.parts); } } diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 6860b9ac..3b12d0e4 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -16,11 +16,10 @@ class UrlStyle extends InternalStyle { // Deprecated properties. - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = - new RegExp(r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); - final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); - final relativeRootPattern = new RegExp(r"^/"); + final separatorPattern = RegExp(r'/'); + final needsSeparatorPattern = RegExp(r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = RegExp(r"^/"); bool containsSeparator(String path) => path.contains('/'); @@ -37,7 +36,7 @@ class UrlStyle extends InternalStyle { return path.endsWith("://") && rootLength(path) == path.length; } - int rootLength(String path, {bool withDrive: false}) { + int rootLength(String path, {bool withDrive = false}) { if (path.isEmpty) return 0; if (isSeparator(path.codeUnitAt(0))) return 1; diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index c55d85e6..59039b8b 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -21,10 +21,10 @@ class WindowsStyle extends InternalStyle { // Deprecated properties. - final separatorPattern = new RegExp(r'[/\\]'); - final needsSeparatorPattern = new RegExp(r'[^/\\]$'); - final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); - final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + final separatorPattern = RegExp(r'[/\\]'); + final needsSeparatorPattern = RegExp(r'[^/\\]$'); + final rootPattern = RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + final relativeRootPattern = RegExp(r"^[/\\](?![/\\])"); bool containsSeparator(String path) => path.contains('/'); @@ -36,7 +36,7 @@ class WindowsStyle extends InternalStyle { return !isSeparator(path.codeUnitAt(path.length - 1)); } - int rootLength(String path, {bool withDrive: false}) { + int rootLength(String path, {bool withDrive = false}) { if (path.isEmpty) return 0; if (path.codeUnitAt(0) == chars.SLASH) return 1; if (path.codeUnitAt(0) == chars.BACKSLASH) { @@ -72,7 +72,7 @@ class WindowsStyle extends InternalStyle { String pathFromUri(Uri uri) { if (uri.scheme != '' && uri.scheme != 'file') { - throw new ArgumentError("Uri $uri must have scheme 'file:'."); + throw ArgumentError("Uri $uri must have scheme 'file:'."); } var path = uri.path; @@ -91,7 +91,7 @@ class WindowsStyle extends InternalStyle { } Uri absolutePathToUri(String path) { - var parsed = new ParsedPath.parse(path, this); + var parsed = ParsedPath.parse(path, this); if (parsed.root.startsWith(r'\\')) { // Network paths become "file://server/share/path/to/file". @@ -106,7 +106,7 @@ class WindowsStyle extends InternalStyle { parsed.parts.add(""); } - return new Uri( + return Uri( scheme: 'file', host: rootParts.first, pathSegments: parsed.parts); } else { // Drive-letter paths become "file:///C:/path/to/file". @@ -124,7 +124,7 @@ class WindowsStyle extends InternalStyle { parsed.parts .insert(0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); - return new Uri(scheme: 'file', pathSegments: parsed.parts); + return Uri(scheme: 'file', pathSegments: parsed.parts); } } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 504474b9..573d121e 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.6.3-dev +version: 1.6.3 description: > A string-based path manipulation library. All of the path operations you know @@ -9,7 +9,7 @@ author: Dart Team homepage: http://github.com/dart-lang/path environment: - sdk: '>=2.0.0-dev.62.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dev_dependencies: test: '>=0.12.42 <2.0.0' diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index b3291469..88aa2869 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -12,18 +12,18 @@ import 'package:path/path.dart' as path; main() { group('new Context()', () { test('uses the window location if root and style are omitted', () { - var context = new path.Context(); + var context = path.Context(); expect(context.current, Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { - var context = new path.Context(style: path.Style.platform); + var context = path.Context(style: path.Style.platform); expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var context = new path.Context(); + var context = path.Context(); expect(context.style, path.Style.platform); }); }); diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 7b9d49d9..c53d8dcc 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -12,17 +12,17 @@ import 'package:path/path.dart' as path; main() { group('new Context()', () { test('uses the current directory if root and style are omitted', () { - var context = new path.Context(); + var context = path.Context(); expect(context.current, io.Directory.current.path); }); test('uses "." if root is omitted', () { - var context = new path.Context(style: path.Style.platform); + var context = path.Context(style: path.Style.platform); expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var context = new path.Context(); + var context = path.Context(); expect(context.style, path.Style.platform); }); }); diff --git a/pkgs/path/test/path_map_test.dart b/pkgs/path/test/path_map_test.dart index ce025db2..4e0c57e9 100644 --- a/pkgs/path/test/path_map_test.dart +++ b/pkgs/path/test/path_map_test.dart @@ -9,7 +9,7 @@ import 'package:path/path.dart'; void main() { group("considers equal", () { test("two identical paths", () { - var map = new PathMap(); + var map = PathMap(); map[join("foo", "bar")] = 1; map[join("foo", "bar")] = 2; expect(map, hasLength(1)); @@ -17,7 +17,7 @@ void main() { }); test("two logically equivalent paths", () { - var map = new PathMap(); + var map = PathMap(); map["foo"] = 1; map[absolute("foo")] = 2; expect(map, hasLength(1)); @@ -26,7 +26,7 @@ void main() { }); test("two nulls", () { - var map = new PathMap(); + var map = PathMap(); map[null] = 1; map[null] = 2; expect(map, hasLength(1)); @@ -36,7 +36,7 @@ void main() { group("considers unequal", () { test("two distinct paths", () { - var map = new PathMap(); + var map = PathMap(); map["foo"] = 1; map["bar"] = 2; expect(map, hasLength(2)); @@ -45,7 +45,7 @@ void main() { }); test("a path and null", () { - var map = new PathMap(); + var map = PathMap(); map["foo"] = 1; map[null] = 2; expect(map, hasLength(2)); @@ -55,7 +55,7 @@ void main() { }); test("uses the custom context", () { - var map = new PathMap(context: windows); + var map = PathMap(context: windows); map["FOO"] = 1; map["foo"] = 2; expect(map, hasLength(1)); @@ -64,14 +64,14 @@ void main() { group(".of()", () { test("copies the existing map's keys", () { - var map = new PathMap.of({"foo": 1, "bar": 2}); + var map = PathMap.of({"foo": 1, "bar": 2}); expect(map, hasLength(2)); expect(map, containsPair("foo", 1)); expect(map, containsPair("bar", 2)); }); test("uses the second value in the case of duplicates", () { - var map = new PathMap.of({"foo": 1, absolute("foo"): 2}); + var map = PathMap.of({"foo": 1, absolute("foo"): 2}); expect(map, hasLength(1)); expect(map, containsPair("foo", 2)); expect(map, containsPair(absolute("foo"), 2)); diff --git a/pkgs/path/test/path_set_test.dart b/pkgs/path/test/path_set_test.dart index 884c1840..f7b6bd0e 100644 --- a/pkgs/path/test/path_set_test.dart +++ b/pkgs/path/test/path_set_test.dart @@ -9,7 +9,7 @@ import 'package:path/path.dart'; void main() { group("considers equal", () { test("two identical paths", () { - var set = new PathSet(); + var set = PathSet(); expect(set.add(join("foo", "bar")), isTrue); expect(set.add(join("foo", "bar")), isFalse); expect(set, hasLength(1)); @@ -17,7 +17,7 @@ void main() { }); test("two logically equivalent paths", () { - var set = new PathSet(); + var set = PathSet(); expect(set.add("foo"), isTrue); expect(set.add(absolute("foo")), isFalse); expect(set, hasLength(1)); @@ -26,7 +26,7 @@ void main() { }); test("two nulls", () { - var set = new PathSet(); + var set = PathSet(); expect(set.add(null), isTrue); expect(set.add(null), isFalse); expect(set, hasLength(1)); @@ -36,7 +36,7 @@ void main() { group("considers unequal", () { test("two distinct paths", () { - var set = new PathSet(); + var set = PathSet(); expect(set.add("foo"), isTrue); expect(set.add("bar"), isTrue); expect(set, hasLength(2)); @@ -45,7 +45,7 @@ void main() { }); test("a path and null", () { - var set = new PathSet(); + var set = PathSet(); expect(set.add("foo"), isTrue); expect(set.add(null), isTrue); expect(set, hasLength(2)); @@ -55,7 +55,7 @@ void main() { }); test("uses the custom context", () { - var set = new PathSet(context: windows); + var set = PathSet(context: windows); expect(set.add("FOO"), isTrue); expect(set.add("foo"), isFalse); expect(set, hasLength(1)); @@ -64,14 +64,14 @@ void main() { group(".of()", () { test("copies the existing set's keys", () { - var set = new PathSet.of(["foo", "bar"]); + var set = PathSet.of(["foo", "bar"]); expect(set, hasLength(2)); expect(set, contains("foo")); expect(set, contains("bar")); }); test("uses the first value in the case of duplicates", () { - var set = new PathSet.of(["foo", absolute("foo")]); + var set = PathSet.of(["foo", absolute("foo")]); expect(set, hasLength(1)); expect(set, contains("foo")); expect(set, contains(absolute("foo"))); diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index 782366c8..8f5de033 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -27,12 +27,12 @@ main() { group('new Context()', () { test('uses the given current directory', () { - var context = new path.Context(current: '/a/b/c'); + var context = path.Context(current: '/a/b/c'); expect(context.current, '/a/b/c'); }); test('uses the given style', () { - var context = new path.Context(style: path.Style.windows); + var context = path.Context(style: path.Style.windows); expect(context.style, path.Style.windows); }); }); diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index df3e6c59..6f7e08a7 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -8,8 +8,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var context = - new path.Context(style: path.Style.posix, current: '/root/path'); + var context = path.Context(style: path.Style.posix, current: '/root/path'); test('separator', () { expect(context.separator, '/'); @@ -367,7 +366,7 @@ main() { }); group('from relative root', () { - var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + var r = path.Context(style: path.Style.posix, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('/'), equals('/')); @@ -388,7 +387,7 @@ main() { }); test('from a root with extension', () { - var r = new path.Context(style: path.Style.posix, current: '/dir.ext'); + var r = path.Context(style: path.Style.posix, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); @@ -401,8 +400,7 @@ main() { }); test('with a root parameter and a relative root', () { - var r = - new path.Context(style: path.Style.posix, current: 'relative/root'); + var r = path.Context(style: path.Style.posix, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); expect( @@ -411,7 +409,7 @@ main() { }); test('from a . root', () { - var r = new path.Context(style: path.Style.posix, current: '.'); + var r = path.Context(style: path.Style.posix, current: '.'); expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); }); @@ -442,7 +440,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + var r = path.Context(style: path.Style.posix, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); @@ -479,7 +477,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + var r = path.Context(style: path.Style.posix, current: 'foo/bar'); expectEquals(r, 'a/b', 'a/b'); expectNotEquals(r, '.', 'foo/bar'); expectNotEquals(r, '.', '../a/b'); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 3cf02dcb..ffc44540 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -11,19 +11,17 @@ import "utils.dart"; void main() { test("test relative", () { - relativeTest(new path.Context(style: path.Style.posix, current: '.'), '/'); - relativeTest(new path.Context(style: path.Style.posix, current: '/'), '/'); + relativeTest(path.Context(style: path.Style.posix, current: '.'), '/'); + relativeTest(path.Context(style: path.Style.posix, current: '/'), '/'); relativeTest( - new path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); - relativeTest( - new path.Context(style: path.Style.windows, current: '.'), r'c:\'); - relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), - 'http://myserver/'); - relativeTest(new path.Context(style: path.Style.url, current: '.'), + path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); + relativeTest(path.Context(style: path.Style.windows, current: '.'), r'c:\'); + relativeTest(path.Context(style: path.Style.url, current: 'file:///'), 'http://myserver/'); relativeTest( - new path.Context(style: path.Style.url, current: 'file:///'), '/'); - relativeTest(new path.Context(style: path.Style.url, current: '.'), '/'); + path.Context(style: path.Style.url, current: '.'), 'http://myserver/'); + relativeTest(path.Context(style: path.Style.url, current: 'file:///'), '/'); + relativeTest(path.Context(style: path.Style.url, current: '.'), '/'); }); } diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 91e43394..c6fcd334 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var context = new path.Context( + var context = path.Context( style: path.Style.url, current: 'http://dartlang.org/root/path'); test('separator', () { @@ -549,7 +549,7 @@ main() { }); group('from relative root', () { - var r = new path.Context(style: path.Style.url, current: 'foo/bar'); + var r = path.Context(style: path.Style.url, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -574,7 +574,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Context(style: path.Style.url, current: '/foo/bar'); + var r = path.Context(style: path.Style.url, current: '/foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -599,7 +599,7 @@ main() { }); test('from a root with extension', () { - var r = new path.Context(style: path.Style.url, current: '/dir.ext'); + var r = path.Context(style: path.Style.url, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); @@ -643,7 +643,7 @@ main() { }); test('with a root parameter and a relative root', () { - var r = new path.Context(style: path.Style.url, current: 'relative/root'); + var r = path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('/foo/bar/baz')); @@ -669,7 +669,7 @@ main() { }); test('from a . root', () { - var r = new path.Context(style: path.Style.url, current: '.'); + var r = path.Context(style: path.Style.url, current: '.'); expect(r.relative('http://dartlang.org/foo/bar/baz'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); @@ -727,7 +727,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.url, current: 'foo/bar'); + var r = path.Context(style: path.Style.url, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); @@ -773,7 +773,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + var r = path.Context(style: path.Style.posix, current: 'foo/bar'); expectEquals(r, 'a/b', 'a/b'); expectNotEquals(r, '.', 'foo/bar'); expectNotEquals(r, '.', '../a/b'); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 79e95ec1..09b81651 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -6,7 +6,7 @@ import "package:test/test.dart"; import "package:path/path.dart" as p; /// A matcher for a closure that throws a [path.PathException]. -final throwsPathException = throwsA(new TypeMatcher()); +final throwsPathException = throwsA(TypeMatcher()); void expectEquals(p.Context context, String path1, String path2) { expect(context.equals(path1, path2), isTrue, @@ -18,7 +18,7 @@ void expectEquals(p.Context context, String path1, String path2) { } void expectNotEquals(p.Context context, String path1, String path2, - {bool allowSameHash: false}) { + {bool allowSameHash = false}) { expect(context.equals(path1, path2), isFalse, reason: 'Expected "$path1" not to equal "$path2".'); expect(context.equals(path2, path1), isFalse, diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index e24a3fcc..47f7ab0c 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -9,7 +9,7 @@ import 'utils.dart'; main() { var context = - new path.Context(style: path.Style.windows, current: r'C:\root\path'); + path.Context(style: path.Style.windows, current: r'C:\root\path'); test('separator', () { expect(context.separator, '\\'); @@ -449,7 +449,7 @@ main() { }); group('from relative root', () { - var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + var r = path.Context(style: path.Style.windows, current: r'foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -472,7 +472,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Context(style: path.Style.windows, current: r'\foo\bar'); + var r = path.Context(style: path.Style.windows, current: r'\foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -497,8 +497,7 @@ main() { }); test('from a root with extension', () { - var r = - new path.Context(style: path.Style.windows, current: r'C:\dir.ext'); + var r = path.Context(style: path.Style.windows, current: r'C:\dir.ext'); expect(r.relative(r'C:\dir.ext\file'), 'file'); }); @@ -514,8 +513,8 @@ main() { }); test('with a root parameter and a relative root', () { - var r = new path.Context( - style: path.Style.windows, current: r'relative\root'); + var r = + path.Context(style: path.Style.windows, current: r'relative\root'); expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); expect(() => r.relative('..', from: r'C:\foo\bar'), throwsPathException); expect(r.relative(r'C:\foo\bar\baz', from: r'foo\bar'), @@ -529,7 +528,7 @@ main() { }); test('from a . root', () { - var r = new path.Context(style: path.Style.windows, current: '.'); + var r = path.Context(style: path.Style.windows, current: '.'); expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); expect(r.relative(r'\foo\bar\baz'), equals(r'\foo\bar\baz')); @@ -576,7 +575,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + var r = path.Context(style: path.Style.windows, current: r'foo\bar'); expect(r.isWithin('.', r'a\b\c'), isTrue); expect(r.isWithin('.', r'..\a\b\c'), isFalse); expect(r.isWithin('.', r'..\..\a\foo\b\c'), isFalse); @@ -628,7 +627,7 @@ main() { }); test('from a relative root', () { - var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + var r = path.Context(style: path.Style.windows, current: r'foo\bar'); expectEquals(r, r'a\b', r'a\b'); expectNotEquals(r, '.', r'foo\bar'); expectNotEquals(r, '.', r'..\a\b'); From 30cae99c99ef7c1f659c4ce58155e22f4fbe0d53 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 16:46:22 -0700 Subject: [PATCH 104/183] pubspec: strip whitespace around description --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 573d121e..8ada54c5 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,7 +1,7 @@ name: path version: 1.6.3 -description: > +description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web. From 0f03e4443795a9922f79c2819d25c5e888a095dd Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 16:47:03 -0700 Subject: [PATCH 105/183] Remove unused codereview.settings --- pkgs/path/codereview.settings | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pkgs/path/codereview.settings diff --git a/pkgs/path/codereview.settings b/pkgs/path/codereview.settings deleted file mode 100644 index 1ff4094d..00000000 --- a/pkgs/path/codereview.settings +++ /dev/null @@ -1,3 +0,0 @@ -CODE_REVIEW_SERVER: https://codereview.chromium.org/ -VIEW_VC: https://github.com/dart-lang/path/commit/ -CC_LIST: reviews@dartlang.org \ No newline at end of file From 63e77259177ca5d08d064b0075346361ec1fa862 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 16:48:58 -0700 Subject: [PATCH 106/183] pubspsec: make homepage link secure --- pkgs/path/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 8ada54c5..5a33360d 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -6,7 +6,7 @@ description: >- and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web. author: Dart Team -homepage: http://github.com/dart-lang/path +homepage: https://github.com/dart-lang/path environment: sdk: '>=2.0.0 <3.0.0' From b7d21486596c631ce28ead5d5d28a1df50d6c2ef Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 16:53:39 -0700 Subject: [PATCH 107/183] Enable and fix a number of lints --- pkgs/path/analysis_options.yaml | 43 ++++++++++++++++++++++ pkgs/path/benchmark/benchmark.dart | 4 +-- pkgs/path/lib/src/characters.dart | 24 ++++++------- pkgs/path/lib/src/context.dart | 54 ++++++++++++++-------------- pkgs/path/lib/src/parsed_path.dart | 12 +++---- pkgs/path/lib/src/style/posix.dart | 4 +-- pkgs/path/lib/src/style/url.dart | 4 +-- pkgs/path/lib/src/style/windows.dart | 24 ++++++------- pkgs/path/lib/src/utils.dart | 10 +++--- pkgs/path/pubspec.yaml | 1 + pkgs/path/test/path_test.dart | 4 +-- pkgs/path/test/relative_test.dart | 2 +- 12 files changed, 115 insertions(+), 71 deletions(-) create mode 100644 pkgs/path/analysis_options.yaml diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml new file mode 100644 index 00000000..0711acad --- /dev/null +++ b/pkgs/path/analysis_options.yaml @@ -0,0 +1,43 @@ +include: package:pedantic/analysis_options.yaml +analyzer: + strong-mode: + implicit-casts: false +linter: + rules: + - avoid_empty_else + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_unused_constructor_parameters + - await_only_futures + - camel_case_types + - cancel_subscriptions + - constant_identifier_names + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - iterable_contains_unrelated_type + - library_names + - library_prefixes + - list_remove_unrelated_type + - non_constant_identifier_names + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_equal_for_default_values + - prefer_final_fields + - prefer_generic_function_type_aliases + - prefer_is_not_empty + - slash_for_doc_comments + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_new + - unrelated_type_equality_checks + - valid_regexps diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index c3428160..4fe479a2 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -49,7 +49,7 @@ void main(List args) { }); } - benchmarkPairs(String name, Function function) { + void benchmarkPairs(String name, void Function(String, String) function) { runBenchmark("${style.name}-$name", 1000, () { for (var file1 in files) { for (var file2 in files) { @@ -72,7 +72,7 @@ void main(List args) { benchmark('relative', context.relative); benchmarkPairs('relative from', (String file, String from) { try { - return context.relative(file, from: from); + context.relative(file, from: from); } on p.PathException { // Do nothing. } diff --git a/pkgs/path/lib/src/characters.dart b/pkgs/path/lib/src/characters.dart index 7dddb2f1..a0835431 100644 --- a/pkgs/path/lib/src/characters.dart +++ b/pkgs/path/lib/src/characters.dart @@ -3,15 +3,15 @@ // BSD-style license that can be found in the LICENSE file. /// This library contains character-code definitions. -const PLUS = 0x2b; -const MINUS = 0x2d; -const PERIOD = 0x2e; -const SLASH = 0x2f; -const ZERO = 0x30; -const NINE = 0x39; -const COLON = 0x3a; -const UPPER_A = 0x41; -const UPPER_Z = 0x5a; -const LOWER_A = 0x61; -const LOWER_Z = 0x7a; -const BACKSLASH = 0x5c; +const plus = 0x2b; +const minus = 0x2d; +const period = 0x2e; +const slash = 0x2f; +const zero = 0x30; +const nine = 0x39; +const colon = 0x3a; +const upperA = 0x41; +const upperZ = 0x5a; +const lowerA = 0x61; +const lowerZ = 0x7a; +const backslash = 0x5c; diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index dc507ff7..30db74ee 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -4,12 +4,12 @@ import 'dart:math' as math; +import '../path.dart' as p; import 'characters.dart' as chars; import 'internal_style.dart'; -import 'style.dart'; import 'parsed_path.dart'; import 'path_exception.dart'; -import '../path.dart' as p; +import 'style.dart'; Context createInternal() => Context._internal(); @@ -270,7 +270,7 @@ class Context { buffer.clear(); buffer.write(part); } else { - if (part.length > 0 && style.containsSeparator(part[0])) { + if (part.isNotEmpty && style.containsSeparator(part[0])) { // The part starts with a separator, so we don't need to add one. } else if (needsSeparator) { buffer.write(separator); @@ -308,7 +308,7 @@ class Context { List split(String path) { var parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); + parsed.parts = parsed.parts.where((part) => part.isNotEmpty).toList(); if (parsed.root != null) parsed.parts.insert(0, parsed.root); return parsed.parts; } @@ -363,13 +363,13 @@ class Context { var root = style.rootLength(path); if (root != 0) { start = root; - previous = chars.SLASH; + previous = chars.slash; // On Windows, the root still needs to be normalized if it contains a // forward slash. if (style == Style.windows) { for (var i = 0; i < root; i++) { - if (codeUnits[i] == chars.SLASH) return true; + if (codeUnits[i] == chars.slash) return true; } } } @@ -378,7 +378,7 @@ class Context { var codeUnit = codeUnits[i]; if (style.isSeparator(codeUnit)) { // Forward slashes in Windows paths are normalized to backslashes. - if (style == Style.windows && codeUnit == chars.SLASH) return true; + if (style == Style.windows && codeUnit == chars.slash) return true; // Multiple separators are normalized to single separators. if (previous != null && style.isSeparator(previous)) return true; @@ -387,9 +387,9 @@ class Context { // // This can return false positives for ".../", but that's unlikely // enough that it's probably not going to cause performance issues. - if (previous == chars.PERIOD && + if (previous == chars.period && (previousPrevious == null || - previousPrevious == chars.PERIOD || + previousPrevious == chars.period || style.isSeparator(previousPrevious))) { return true; } @@ -406,10 +406,10 @@ class Context { if (style.isSeparator(previous)) return true; // Single dots and double dots are normalized to directory traversals. - if (previous == chars.PERIOD && + if (previous == chars.period && (previousPrevious == null || style.isSeparator(previousPrevious) || - previousPrevious == chars.PERIOD)) { + previousPrevious == chars.period)) { return true; } @@ -474,7 +474,7 @@ class Context { var fromParsed = _parse(from)..normalize(); var pathParsed = _parse(path)..normalize(); - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + if (fromParsed.parts.isNotEmpty && fromParsed.parts[0] == '.') { return pathParsed.toString(); } @@ -489,8 +489,8 @@ class Context { } // Strip off their common prefix. - while (fromParsed.parts.length > 0 && - pathParsed.parts.length > 0 && + while (fromParsed.parts.isNotEmpty && + pathParsed.parts.isNotEmpty && style.pathsEqual(fromParsed.parts[0], pathParsed.parts[0])) { fromParsed.parts.removeAt(0); fromParsed.separators.removeAt(1); @@ -501,7 +501,7 @@ class Context { // If there are any directories left in the from path, we need to walk up // out of them. If a directory left in the from path is '..', it cannot // be cancelled by adding a '..'. - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + if (fromParsed.parts.isNotEmpty && fromParsed.parts[0] == '..') { throw PathException('Unable to find a path to "$path" from "$from".'); } pathParsed.parts.insertAll(0, List.filled(fromParsed.parts.length, '..')); @@ -510,7 +510,7 @@ class Context { .insertAll(1, List.filled(fromParsed.parts.length, style.separator)); // Corner case: the paths completely collapsed. - if (pathParsed.parts.length == 0) return '.'; + if (pathParsed.parts.isEmpty) return '.'; // Corner case: path was '.' and some '..' directories were added in front. // Don't add a final '/.' in that case. @@ -628,7 +628,7 @@ class Context { // Start by considering the last code unit as a separator, since // semantically we're starting at a new path component even if we're // comparing relative paths. - var lastCodeUnit = chars.SLASH; + var lastCodeUnit = chars.slash; /// The index of the last separator in [parent]. int lastParentSeparator; @@ -668,7 +668,7 @@ class Context { // // isWithin("foo/./bar", "foo/bar/baz") //=> true // isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false - if (parentCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) { + if (parentCodeUnit == chars.period && style.isSeparator(lastCodeUnit)) { parentIndex++; // We've hit "/." at the end of the parent path, which we can ignore, @@ -685,7 +685,7 @@ class Context { // We've hit "/..", which may be a directory traversal operator that // we can't handle on the fast track. - if (parentCodeUnit == chars.PERIOD) { + if (parentCodeUnit == chars.period) { parentIndex++; if (parentIndex == parent.length || style.isSeparator(parent.codeUnitAt(parentIndex))) { @@ -699,7 +699,7 @@ class Context { // This is the same logic as above, but for the child path instead of the // parent. - if (childCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) { + if (childCodeUnit == chars.period && style.isSeparator(lastCodeUnit)) { childIndex++; if (childIndex == child.length) break; childCodeUnit = child.codeUnitAt(childIndex); @@ -709,7 +709,7 @@ class Context { continue; } - if (childCodeUnit == chars.PERIOD) { + if (childCodeUnit == chars.period) { childIndex++; if (childIndex == child.length || style.isSeparator(child.codeUnitAt(childIndex))) { @@ -826,11 +826,11 @@ class Context { } // See if the path component is ".", "..", or a name. - if (i - start == 1 && path.codeUnitAt(start) == chars.PERIOD) { + if (i - start == 1 && path.codeUnitAt(start) == chars.period) { // Don't change the depth. } else if (i - start == 2 && - path.codeUnitAt(start) == chars.PERIOD && - path.codeUnitAt(start + 1) == chars.PERIOD) { + path.codeUnitAt(start) == chars.period && + path.codeUnitAt(start + 1) == chars.period) { // ".." backs out a directory. depth--; @@ -894,7 +894,7 @@ class Context { continue; } - if (codeUnit == chars.PERIOD && wasSeparator) { + if (codeUnit == chars.period && wasSeparator) { // If a dot comes after a separator, it may be a directory traversal // operator. To check that, we need to know if it's followed by either // "/" or "./". Otherwise, it's just a normal character. @@ -915,7 +915,7 @@ class Context { // at the beginning of the path, since those may appear even in a // canonicalized path. if (!beginning && - next == chars.PERIOD && + next == chars.period && (i + 2 == path.length || style.isSeparator(path.codeUnitAt(i + 2)))) { return null; @@ -939,7 +939,7 @@ class Context { var parsed = _parse(path); for (var i = parsed.parts.length - 1; i >= 0; i--) { - if (!parsed.parts[i].isEmpty) { + if (parsed.parts[i].isNotEmpty) { parsed.parts[i] = parsed.basenameWithoutExtension; break; } diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 6984591d..e4b7feb5 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -87,14 +87,14 @@ class ParsedPath { String get basenameWithoutExtension => _splitExtension()[0]; bool get hasTrailingSeparator => - !parts.isEmpty && (parts.last == '' || separators.last != ''); + parts.isNotEmpty && (parts.last == '' || separators.last != ''); void removeTrailingSeparators() { - while (!parts.isEmpty && parts.last == '') { + while (parts.isNotEmpty && parts.last == '') { parts.removeLast(); separators.removeLast(); } - if (separators.length > 0) separators[separators.length - 1] = ''; + if (separators.isNotEmpty) separators[separators.length - 1] = ''; } void normalize({bool canonicalize = false}) { @@ -106,7 +106,7 @@ class ParsedPath { // Do nothing. Ignore it. } else if (part == '..') { // Pop the last part off. - if (newParts.length > 0) { + if (newParts.isNotEmpty) { newParts.removeLast(); } else { // Backed out past the beginning, so preserve the "..". @@ -123,7 +123,7 @@ class ParsedPath { } // If we collapsed down to nothing, do ".". - if (newParts.length == 0 && !isAbsolute) { + if (newParts.isEmpty && !isAbsolute) { newParts.add('.'); } @@ -133,7 +133,7 @@ class ParsedPath { growable: true); newSeparators.insert( 0, - isAbsolute && newParts.length > 0 && style.needsSeparator(root) + isAbsolute && newParts.isNotEmpty && style.needsSeparator(root) ? style.separator : ''); diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index a2388b45..e673613c 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -3,8 +3,8 @@ // BSD-style license that can be found in the LICENSE file. import '../characters.dart' as chars; -import '../parsed_path.dart'; import '../internal_style.dart'; +import '../parsed_path.dart'; /// The style for POSIX paths. class PosixStyle extends InternalStyle { @@ -23,7 +23,7 @@ class PosixStyle extends InternalStyle { bool containsSeparator(String path) => path.contains('/'); - bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + bool isSeparator(int codeUnit) => codeUnit == chars.slash; bool needsSeparator(String path) => path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 3b12d0e4..a189916b 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -23,7 +23,7 @@ class UrlStyle extends InternalStyle { bool containsSeparator(String path) => path.contains('/'); - bool isSeparator(int codeUnit) => codeUnit == chars.SLASH; + bool isSeparator(int codeUnit) => codeUnit == chars.slash; bool needsSeparator(String path) { if (path.isEmpty) return false; @@ -43,7 +43,7 @@ class UrlStyle extends InternalStyle { for (var i = 0; i < path.length; i++) { var codeUnit = path.codeUnitAt(i); if (isSeparator(codeUnit)) return 0; - if (codeUnit == chars.COLON) { + if (codeUnit == chars.colon) { if (i == 0) return 0; // The root part is up until the next '/', or the full path. Skip ':' diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 59039b8b..37c501f8 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -29,7 +29,7 @@ class WindowsStyle extends InternalStyle { bool containsSeparator(String path) => path.contains('/'); bool isSeparator(int codeUnit) => - codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH; + codeUnit == chars.slash || codeUnit == chars.backslash; bool needsSeparator(String path) { if (path.isEmpty) return false; @@ -38,9 +38,9 @@ class WindowsStyle extends InternalStyle { int rootLength(String path, {bool withDrive = false}) { if (path.isEmpty) return 0; - if (path.codeUnitAt(0) == chars.SLASH) return 1; - if (path.codeUnitAt(0) == chars.BACKSLASH) { - if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1; + if (path.codeUnitAt(0) == chars.slash) return 1; + if (path.codeUnitAt(0) == chars.backslash) { + if (path.length < 2 || path.codeUnitAt(1) != chars.backslash) return 1; // The path is a network share. Search for up to two '\'s, as they are // the server and share - and part of the root part. var index = path.indexOf('\\', 2); @@ -56,7 +56,7 @@ class WindowsStyle extends InternalStyle { // Check for the letter. if (!isAlphabetic(path.codeUnitAt(0))) return 0; // Check for the ':'. - if (path.codeUnitAt(1) != chars.COLON) return 0; + if (path.codeUnitAt(1) != chars.colon) return 0; // Check for either '/' or '\'. if (!isSeparator(path.codeUnitAt(2))) return 0; return 3; @@ -115,7 +115,7 @@ class WindowsStyle extends InternalStyle { // be empty. We add an empty component so the URL constructor produces // "file:///C:/", with a trailing slash. We also add an empty component if // the URL otherwise has a trailing slash. - if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + if (parsed.parts.isEmpty || parsed.hasTrailingSeparator) { parsed.parts.add(""); } @@ -132,8 +132,8 @@ class WindowsStyle extends InternalStyle { if (codeUnit1 == codeUnit2) return true; /// Forward slashes and backslashes are equivalent on Windows. - if (codeUnit1 == chars.SLASH) return codeUnit2 == chars.BACKSLASH; - if (codeUnit1 == chars.BACKSLASH) return codeUnit2 == chars.SLASH; + if (codeUnit1 == chars.slash) return codeUnit2 == chars.backslash; + if (codeUnit1 == chars.backslash) return codeUnit2 == chars.slash; // If this check fails, the code units are definitely different. If it // succeeds *and* either codeUnit is an ASCII letter, they're equivalent. @@ -141,7 +141,7 @@ class WindowsStyle extends InternalStyle { // Now we just need to verify that one of the code units is an ASCII letter. var upperCase1 = codeUnit1 | _asciiCaseBit; - return upperCase1 >= chars.LOWER_A && upperCase1 <= chars.LOWER_Z; + return upperCase1 >= chars.lowerA && upperCase1 <= chars.lowerZ; } bool pathsEqual(String path1, String path2) { @@ -156,9 +156,9 @@ class WindowsStyle extends InternalStyle { } int canonicalizeCodeUnit(int codeUnit) { - if (codeUnit == chars.SLASH) return chars.BACKSLASH; - if (codeUnit < chars.UPPER_A) return codeUnit; - if (codeUnit > chars.UPPER_Z) return codeUnit; + if (codeUnit == chars.slash) return chars.backslash; + if (codeUnit < chars.upperA) return codeUnit; + if (codeUnit > chars.upperZ) return codeUnit; return codeUnit | _asciiCaseBit; } diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index 561ff632..2443ccd4 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -7,18 +7,18 @@ import 'characters.dart' as chars; /// Returns whether [char] is the code for an ASCII letter (uppercase or /// lowercase). bool isAlphabetic(int char) => - (char >= chars.UPPER_A && char <= chars.UPPER_Z) || - (char >= chars.LOWER_A && char <= chars.LOWER_Z); + (char >= chars.upperA && char <= chars.upperZ) || + (char >= chars.lowerA && char <= chars.lowerZ); /// Returns whether [char] is the code for an ASCII digit. -bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE; +bool isNumeric(int char) => char >= chars.zero && char <= chars.nine; /// Returns whether [path] has a URL-formatted Windows drive letter beginning at /// [index]. bool isDriveLetter(String path, int index) { if (path.length < index + 2) return false; if (!isAlphabetic(path.codeUnitAt(index))) return false; - if (path.codeUnitAt(index + 1) != chars.COLON) return false; + if (path.codeUnitAt(index + 1) != chars.colon) return false; if (path.length == index + 2) return true; - return path.codeUnitAt(index + 2) == chars.SLASH; + return path.codeUnitAt(index + 2) == chars.slash; } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 5a33360d..1b5426d7 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -12,4 +12,5 @@ environment: sdk: '>=2.0.0 <3.0.0' dev_dependencies: + pedantic: ^1.0.0 test: '>=0.12.42 <2.0.0' diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index 8f5de033..16811595 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -13,9 +13,9 @@ main() { }); test('separator', () { - // ignore: deprecated_member_use + // ignore: deprecated_member_use_from_same_package expect(path.Style.posix.separator, '/'); - // ignore: deprecated_member_use + // ignore: deprecated_member_use_from_same_package expect(path.Style.windows.separator, '\\'); }); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index ffc44540..8da7eac0 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -28,7 +28,7 @@ void main() { void relativeTest(path.Context context, String prefix) { var isRelative = (context.current == '.'); // Cases where the arguments are absolute paths. - expectRelative(result, pathArg, fromArg) { + void expectRelative(String result, String pathArg, String fromArg) { expect(context.relative(pathArg, from: fromArg), context.normalize(result)); } From 727d3f1217388f99ab138a8112d32820fabb6413 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 16:54:47 -0700 Subject: [PATCH 108/183] Test oldest supported SDK, on browser, and with stronger analysis --- pkgs/path/.travis.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index fc8e7680..f63530ca 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -1,12 +1,23 @@ language: dart dart: + - 2.0.0 - dev dart_task: - - test - - dartfmt - - dartanalyze + - test: --platform vm,chrome + +matrix: + include: + # Only validate formatting using the dev release + - dart: dev + dart_task: dartfmt + - dart: dev + dart_task: + dartanalyzer: --fatal-infos --fatal-warnings . + - dart: 2.0.0 + dart_task: + dartanalyzer: --fatal-warnings . # Only building master means that we don't run two builds for each pull request. branches: From af78bcc2997c735ba7ddd736795f27a416439d0e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 24 Jul 2019 17:22:36 -0700 Subject: [PATCH 109/183] Example, prepare for 1.6.4 (dart-lang/path#52) --- pkgs/path/CHANGELOG.md | 6 ++++++ pkgs/path/example/example.dart | 16 ++++++++++++++++ pkgs/path/pubspec.yaml | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pkgs/path/example/example.dart diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 129e440c..0804f020 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.6.4 + +* Fixed a number of lints that affect the package health score. + +* Added an example. + ## 1.6.3 * Don't throw a FileSystemException from `current` if the working directory has diff --git a/pkgs/path/example/example.dart b/pkgs/path/example/example.dart new file mode 100644 index 00000000..89271fd4 --- /dev/null +++ b/pkgs/path/example/example.dart @@ -0,0 +1,16 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:path/path.dart' as p; + +void main() { + print('Current path style: ${p.style}'); + + print('Current process path: ${p.current}'); + + print('Separators'); + for (var entry in [p.posix, p.windows, p.url]) { + print(' ${entry.style.toString().padRight(7)}: ${entry.separator}'); + } +} diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 1b5426d7..918c4fef 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.6.3 +version: 1.6.4 description: >- A string-based path manipulation library. All of the path operations you know From 82765e7eefc1717e001eab77489d9f57c65d051e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 9 Dec 2019 15:55:48 -0800 Subject: [PATCH 110/183] Fix newly enforced package:pedantic lints (dart-lang/path#54) - always_declare_return_types - annotate_overrides - prefer_if_null_operators - prefer_single_quotes - unnecessary_this - use_function_type_syntax_for_parameters --- pkgs/path/benchmark/benchmark.dart | 8 ++-- pkgs/path/lib/src/context.dart | 66 +++++++++++++-------------- pkgs/path/lib/src/internal_style.dart | 5 ++ pkgs/path/lib/src/parsed_path.dart | 5 +- pkgs/path/lib/src/path_exception.dart | 3 +- pkgs/path/lib/src/path_set.dart | 22 ++++++++- pkgs/path/lib/src/style.dart | 21 +++++---- pkgs/path/lib/src/style/posix.dart | 18 +++++++- pkgs/path/lib/src/style/url.dart | 25 ++++++++-- pkgs/path/lib/src/style/windows.dart | 30 +++++++++--- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/browser_test.dart | 4 +- pkgs/path/test/io_test.dart | 6 +-- pkgs/path/test/path_map_test.dart | 64 +++++++++++++------------- pkgs/path/test/path_set_test.dart | 66 +++++++++++++-------------- pkgs/path/test/path_test.dart | 8 ++-- pkgs/path/test/posix_test.dart | 2 +- pkgs/path/test/relative_test.dart | 8 ++-- pkgs/path/test/url_test.dart | 2 +- pkgs/path/test/utils.dart | 4 +- pkgs/path/test/windows_test.dart | 2 +- 21 files changed, 222 insertions(+), 149 deletions(-) diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 4fe479a2..60550a47 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -41,8 +41,8 @@ void main(List args) { var context = p.Context(style: style); var files = genericPaths.toList()..addAll(platformPaths[style]); - benchmark(String name, Function function) { - runBenchmark("${style.name}-$name", 100000, () { + void benchmark(String name, Function function) { + runBenchmark('${style.name}-$name', 100000, () { for (var file in files) { function(file); } @@ -50,7 +50,7 @@ void main(List args) { } void benchmarkPairs(String name, void Function(String, String) function) { - runBenchmark("${style.name}-$name", 1000, () { + runBenchmark('${style.name}-$name', 1000, () { for (var file1 in files) { for (var file2 in files) { function(file1, file2); @@ -102,5 +102,5 @@ void runBenchmark(String name, int count, Function function) { var rate = (count / stopwatch.elapsedMicroseconds).toStringAsFixed(5).padLeft(9); - print("${name.padLeft(32)}: $rate iter/us (${stopwatch.elapsed})"); + print('${name.padLeft(32)}: $rate iter/us (${stopwatch.elapsed})'); } diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 30db74ee..8c7af5aa 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -30,15 +30,15 @@ class Context { if (style == null) { current = p.current; } else { - current = "."; + current = '.'; } } if (style == null) { style = Style.platform; } else if (style is! InternalStyle) { - throw ArgumentError("Only styles defined by the path package are " - "allowed."); + throw ArgumentError('Only styles defined by the path package are ' + 'allowed.'); } return Context._(style as InternalStyle, current); @@ -59,7 +59,7 @@ class Context { final String _current; /// The current directory that relative paths are relative to. - String get current => _current != null ? _current : p.current; + String get current => _current ?? p.current; /// Gets the path separator for the context's [style]. On Mac and Linux, /// this is `/`. On Windows, it's `\`. @@ -80,7 +80,7 @@ class Context { String part6, String part7]) { _validateArgList( - "absolute", [part1, part2, part3, part4, part5, part6, part7]); + 'absolute', [part1, part2, part3, part4, part5, part6, part7]); // If there's a single absolute path, just return it. This is a lot faster // for the common case of `p.absolute(path)`. @@ -124,10 +124,8 @@ class Context { String dirname(String path) { var parsed = _parse(path); parsed.removeTrailingSeparators(); - if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; - if (parsed.parts.length == 1) { - return parsed.root == null ? '.' : parsed.root; - } + if (parsed.parts.isEmpty) return parsed.root ?? '.'; + if (parsed.parts.length == 1) return parsed.root ?? '.'; parsed.parts.removeLast(); parsed.separators.removeLast(); parsed.removeTrailingSeparators(); @@ -185,7 +183,7 @@ class Context { /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. - bool isRelative(String path) => !this.isAbsolute(path); + bool isRelative(String path) => !isAbsolute(path); /// Returns `true` if [path] is a root-relative path and `false` if it's not. /// @@ -228,7 +226,7 @@ class Context { part7, part8 ]; - _validateArgList("join", parts); + _validateArgList('join', parts); return joinAll(parts.where((part) => part != null)); } @@ -252,7 +250,7 @@ class Context { var isAbsoluteAndNotRootRelative = false; for (var part in parts.where((part) => part != '')) { - if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + if (isRootRelative(part) && isAbsoluteAndNotRootRelative) { // If the new part is root-relative, it preserves the previous root but // replaces the path after it. var parsed = _parse(part); @@ -264,8 +262,8 @@ class Context { } buffer.clear(); buffer.write(parsed.toString()); - } else if (this.isAbsolute(part)) { - isAbsoluteAndNotRootRelative = !this.isRootRelative(part); + } else if (isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !isRootRelative(part); // An absolute path discards everything before it. buffer.clear(); buffer.write(part); @@ -450,24 +448,24 @@ class Context { /// thrown. String relative(String path, {String from}) { // Avoid expensive computation if the path is already relative. - if (from == null && this.isRelative(path)) return this.normalize(path); + if (from == null && isRelative(path)) return normalize(path); from = from == null ? current : absolute(from); // We can't determine the path from a relative path to an absolute path. - if (this.isRelative(from) && this.isAbsolute(path)) { - return this.normalize(path); + if (isRelative(from) && isAbsolute(path)) { + return normalize(path); } // If the given path is relative, resolve it relative to the context's // current directory. - if (this.isRelative(path) || this.isRootRelative(path)) { - path = this.absolute(path); + if (isRelative(path) || isRootRelative(path)) { + path = absolute(path); } // If the path is still relative and `from` is absolute, we're unable to // find a path from `from` to `path`. - if (this.isRelative(path) && this.isAbsolute(from)) { + if (isRelative(path) && isAbsolute(from)) { throw PathException('Unable to find a path to "$path" from "$from".'); } @@ -585,7 +583,7 @@ class Context { return _PathRelation.different; } - if (!this.isRelative(relative)) return _PathRelation.different; + if (!isRelative(relative)) return _PathRelation.different; if (relative == '.') return _PathRelation.equal; if (relative == '..') return _PathRelation.different; return (relative.length >= 3 && @@ -1078,12 +1076,12 @@ void _validateArgList(String method, List args) { // Show the arguments. var message = StringBuffer(); - message.write("$method("); + message.write('$method('); message.write(args .take(numArgs) - .map((arg) => arg == null ? "null" : '"$arg"') - .join(", ")); - message.write("): part ${i - 1} was null, but part $i was not."); + .map((arg) => arg == null ? 'null' : '"$arg"') + .join(', ')); + message.write('): part ${i - 1} was null, but part $i was not.'); throw ArgumentError(message.toString()); } } @@ -1095,23 +1093,24 @@ class _PathDirection { /// /// Note that this applies even if the path ends beneath its original root. It /// takes precendence over any other return values that may apple. - static const aboveRoot = _PathDirection("above root"); + static const aboveRoot = _PathDirection('above root'); /// The path contains enough ".." components that it ends at its original /// root. - static const atRoot = _PathDirection("at root"); + static const atRoot = _PathDirection('at root'); /// The path contains enough ".." components that at some point it reaches its /// original root, but it ends beneath that root. - static const reachesRoot = _PathDirection("reaches root"); + static const reachesRoot = _PathDirection('reaches root'); /// The path never reaches to or above its original root. - static const belowRoot = _PathDirection("below root"); + static const belowRoot = _PathDirection('below root'); final String name; const _PathDirection(this.name); + @override String toString() => name; } @@ -1120,25 +1119,26 @@ class _PathRelation { /// The first path is a proper parent of the second. /// /// For example, `foo` is a proper parent of `foo/bar`, but not of `foo`. - static const within = _PathRelation("within"); + static const within = _PathRelation('within'); /// The two paths are equivalent. /// /// For example, `foo//bar` is equivalent to `foo/bar`. - static const equal = _PathRelation("equal"); + static const equal = _PathRelation('equal'); /// The first path is neither a parent of nor equal to the second. - static const different = _PathRelation("different"); + static const different = _PathRelation('different'); /// We couldn't quickly determine any information about the paths' /// relationship to each other. /// /// Only returned by [Context._isWithinOrEqualsFast]. - static const inconclusive = _PathRelation("inconclusive"); + static const inconclusive = _PathRelation('inconclusive'); final String name; const _PathRelation(this.name); + @override String toString() => name; } diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 8a7f2a62..53b4adee 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -14,6 +14,7 @@ abstract class InternalStyle extends Style { /// The default path separator for this style. /// /// On POSIX, this is `/`. On Windows, it's `\`. + @override String get separator; /// Returns whether [path] contains a separator. @@ -41,6 +42,7 @@ abstract class InternalStyle extends Style { /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. + @override String getRoot(String path) { var length = rootLength(path); if (length > 0) return path.substring(0, length); @@ -53,9 +55,11 @@ abstract class InternalStyle extends Style { bool isRootRelative(String path); /// Returns the path represented by [uri] in this style. + @override String pathFromUri(Uri uri); /// Returns the URI that represents the relative path made of [parts]. + @override Uri relativePathToUri(String path) { var segments = context.split(path); @@ -66,6 +70,7 @@ abstract class InternalStyle extends Style { } /// Returns the URI that represents [path], which is assumed to be absolute. + @override Uri absolutePathToUri(String path); /// Returns whether [codeUnit1] and [codeUnit2] are considered equivalent for diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index e4b7feb5..42c9f2fb 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -78,9 +78,9 @@ class ParsedPath { this.style, this.root, this.isRootRelative, this.parts, this.separators); String get basename { - var copy = this.clone(); + var copy = clone(); copy.removeTrailingSeparators(); - if (copy.parts.isEmpty) return root == null ? '' : root; + if (copy.parts.isEmpty) return root ?? ''; return copy.parts.last; } @@ -148,6 +148,7 @@ class ParsedPath { removeTrailingSeparators(); } + @override String toString() { var builder = StringBuffer(); if (root != null) builder.write(root); diff --git a/pkgs/path/lib/src/path_exception.dart b/pkgs/path/lib/src/path_exception.dart index 55f5be30..12a84322 100644 --- a/pkgs/path/lib/src/path_exception.dart +++ b/pkgs/path/lib/src/path_exception.dart @@ -9,5 +9,6 @@ class PathException implements Exception { PathException(this.message); - String toString() => "PathException: $message"; + @override + String toString() => 'PathException: $message'; } diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index 6a2afd11..03a7eebb 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -43,39 +43,57 @@ class PathSet extends IterableBase implements Set { // implement these, but we want to avoid adding dependencies from path because // it's so widely used that even brief version skew can be very painful. + @override Iterator get iterator => _inner.iterator; + @override int get length => _inner.length; + @override bool add(String value) => _inner.add(value); + @override void addAll(Iterable elements) => _inner.addAll(elements); + @override Set cast() => _inner.cast(); + @override void clear() => _inner.clear(); + @override bool contains(Object other) => _inner.contains(other); + @override bool containsAll(Iterable other) => _inner.containsAll(other); + @override Set difference(Set other) => _inner.difference(other); + @override Set intersection(Set other) => _inner.intersection(other); + @override String lookup(Object element) => _inner.lookup(element); + @override bool remove(Object value) => _inner.remove(value); + @override void removeAll(Iterable elements) => _inner.removeAll(elements); - void removeWhere(bool test(String element)) => _inner.removeWhere(test); + @override + void removeWhere(bool Function(String) test) => _inner.removeWhere(test); + @override void retainAll(Iterable elements) => _inner.retainAll(elements); - void retainWhere(bool test(String element)) => _inner.retainWhere(test); + @override + void retainWhere(bool Function(String) test) => _inner.retainWhere(test); + @override Set union(Set other) => _inner.union(other); + @override Set toSet() => _inner.toSet(); } diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 659c78c9..409394c9 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -52,35 +52,36 @@ abstract class Style { /// A [Context] that uses this style. Context get context => Context(style: this); - @Deprecated("Most Style members will be removed in path 2.0.") + @Deprecated('Most Style members will be removed in path 2.0.') String get separator; - @Deprecated("Most Style members will be removed in path 2.0.") + @Deprecated('Most Style members will be removed in path 2.0.') Pattern get separatorPattern; - @Deprecated("Most Style members will be removed in path 2.0.") + @Deprecated('Most Style members will be removed in path 2.0.') Pattern get needsSeparatorPattern; - @Deprecated("Most Style members will be removed in path 2.0.") + @Deprecated('Most Style members will be removed in path 2.0.') Pattern get rootPattern; - @Deprecated("Most Style members will be removed in path 2.0.") + @Deprecated('Most Style members will be removed in path 2.0.') Pattern get relativeRootPattern; - @Deprecated("Most style members will be removed in path 2.0.") + @Deprecated('Most style members will be removed in path 2.0.') String getRoot(String path); - @Deprecated("Most style members will be removed in path 2.0.") + @Deprecated('Most style members will be removed in path 2.0.') String getRelativeRoot(String path); - @Deprecated("Most style members will be removed in path 2.0.") + @Deprecated('Most style members will be removed in path 2.0.') String pathFromUri(Uri uri); - @Deprecated("Most style members will be removed in path 2.0.") + @Deprecated('Most style members will be removed in path 2.0.') Uri relativePathToUri(String path); - @Deprecated("Most style members will be removed in path 2.0.") + @Deprecated('Most style members will be removed in path 2.0.') Uri absolutePathToUri(String path); + @override String toString() => name; } diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index e673613c..0b9803b5 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -10,33 +10,46 @@ import '../parsed_path.dart'; class PosixStyle extends InternalStyle { PosixStyle(); + @override final name = 'posix'; + @override final separator = '/'; final separators = const ['/']; // Deprecated properties. + @override final separatorPattern = RegExp(r'/'); + @override final needsSeparatorPattern = RegExp(r'[^/]$'); + @override final rootPattern = RegExp(r'^/'); + @override final relativeRootPattern = null; + @override bool containsSeparator(String path) => path.contains('/'); + @override bool isSeparator(int codeUnit) => codeUnit == chars.slash; + @override bool needsSeparator(String path) => path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1)); + @override int rootLength(String path, {bool withDrive = false}) { if (path.isNotEmpty && isSeparator(path.codeUnitAt(0))) return 1; return 0; } + @override bool isRootRelative(String path) => false; + @override String getRelativeRoot(String path) => null; + @override String pathFromUri(Uri uri) { if (uri.scheme == '' || uri.scheme == 'file') { return Uri.decodeComponent(uri.path); @@ -44,17 +57,18 @@ class PosixStyle extends InternalStyle { throw ArgumentError("Uri $uri must have scheme 'file:'."); } + @override Uri absolutePathToUri(String path) { var parsed = ParsedPath.parse(path, this); if (parsed.parts.isEmpty) { // If the path is a bare root (e.g. "/"), [components] will // currently be empty. We add two empty components so the URL constructor // produces "file:///", with a trailing slash. - parsed.parts.addAll(["", ""]); + parsed.parts.addAll(['', '']); } else if (parsed.hasTrailingSeparator) { // If the path has a trailing slash, add a single empty component so the // URI has a trailing slash as well. - parsed.parts.add(""); + parsed.parts.add(''); } return Uri(scheme: 'file', pathSegments: parsed.parts); diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index a189916b..b9d6dffa 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -10,21 +10,30 @@ import '../utils.dart'; class UrlStyle extends InternalStyle { UrlStyle(); + @override final name = 'url'; + @override final separator = '/'; final separators = const ['/']; // Deprecated properties. + @override final separatorPattern = RegExp(r'/'); - final needsSeparatorPattern = RegExp(r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); - final rootPattern = RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); - final relativeRootPattern = RegExp(r"^/"); - + @override + final needsSeparatorPattern = RegExp(r'(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$'); + @override + final rootPattern = RegExp(r'[a-zA-Z][-+.a-zA-Z\d]*://[^/]*'); + @override + final relativeRootPattern = RegExp(r'^/'); + + @override bool containsSeparator(String path) => path.contains('/'); + @override bool isSeparator(int codeUnit) => codeUnit == chars.slash; + @override bool needsSeparator(String path) { if (path.isEmpty) return false; @@ -33,9 +42,10 @@ class UrlStyle extends InternalStyle { // A URI that's just "scheme://" needs an extra separator, despite ending // with "/". - return path.endsWith("://") && rootLength(path) == path.length; + return path.endsWith('://') && rootLength(path) == path.length; } + @override int rootLength(String path, {bool withDrive = false}) { if (path.isEmpty) return 0; if (isSeparator(path.codeUnitAt(0))) return 1; @@ -64,13 +74,18 @@ class UrlStyle extends InternalStyle { return 0; } + @override bool isRootRelative(String path) => path.isNotEmpty && isSeparator(path.codeUnitAt(0)); + @override String getRelativeRoot(String path) => isRootRelative(path) ? '/' : null; + @override String pathFromUri(Uri uri) => uri.toString(); + @override Uri relativePathToUri(String path) => Uri.parse(path); + @override Uri absolutePathToUri(String path) => Uri.parse(path); } diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 37c501f8..287733a8 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -15,27 +15,37 @@ const _asciiCaseBit = 0x20; class WindowsStyle extends InternalStyle { WindowsStyle(); + @override final name = 'windows'; + @override final separator = '\\'; final separators = const ['/', '\\']; // Deprecated properties. + @override final separatorPattern = RegExp(r'[/\\]'); + @override final needsSeparatorPattern = RegExp(r'[^/\\]$'); + @override final rootPattern = RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); - final relativeRootPattern = RegExp(r"^[/\\](?![/\\])"); + @override + final relativeRootPattern = RegExp(r'^[/\\](?![/\\])'); + @override bool containsSeparator(String path) => path.contains('/'); + @override bool isSeparator(int codeUnit) => codeUnit == chars.slash || codeUnit == chars.backslash; + @override bool needsSeparator(String path) { if (path.isEmpty) return false; return !isSeparator(path.codeUnitAt(path.length - 1)); } + @override int rootLength(String path, {bool withDrive = false}) { if (path.isEmpty) return 0; if (path.codeUnitAt(0) == chars.slash) return 1; @@ -62,14 +72,17 @@ class WindowsStyle extends InternalStyle { return 3; } + @override bool isRootRelative(String path) => rootLength(path) == 1; + @override String getRelativeRoot(String path) { var length = rootLength(path); if (length == 1) return path[0]; return null; } + @override String pathFromUri(Uri uri) { if (uri.scheme != '' && uri.scheme != 'file') { throw ArgumentError("Uri $uri must have scheme 'file:'."); @@ -81,15 +94,16 @@ class WindowsStyle extends InternalStyle { // replaceFirst removes the extra initial slash. Otherwise, leave the // slash to match IE's interpretation of "/foo" as a root-relative path. if (path.length >= 3 && path.startsWith('/') && isDriveLetter(path, 1)) { - path = path.replaceFirst("/", ""); + path = path.replaceFirst('/', ''); } } else { // Network paths look like "file://hostname/path/to/file". path = '\\\\${uri.host}$path'; } - return Uri.decodeComponent(path.replaceAll("/", "\\")); + return Uri.decodeComponent(path.replaceAll('/', '\\')); } + @override Uri absolutePathToUri(String path) { var parsed = ParsedPath.parse(path, this); if (parsed.root.startsWith(r'\\')) { @@ -103,7 +117,7 @@ class WindowsStyle extends InternalStyle { if (parsed.hasTrailingSeparator) { // If the path has a trailing slash, add a single empty component so the // URI has a trailing slash as well. - parsed.parts.add(""); + parsed.parts.add(''); } return Uri( @@ -116,18 +130,19 @@ class WindowsStyle extends InternalStyle { // "file:///C:/", with a trailing slash. We also add an empty component if // the URL otherwise has a trailing slash. if (parsed.parts.isEmpty || parsed.hasTrailingSeparator) { - parsed.parts.add(""); + parsed.parts.add(''); } // Get rid of the trailing "\" in "C:\" because the URI constructor will // add a separator on its own. parsed.parts - .insert(0, parsed.root.replaceAll("/", "").replaceAll("\\", "")); + .insert(0, parsed.root.replaceAll('/', '').replaceAll('\\', '')); return Uri(scheme: 'file', pathSegments: parsed.parts); } } + @override bool codeUnitsEqual(int codeUnit1, int codeUnit2) { if (codeUnit1 == codeUnit2) return true; @@ -144,6 +159,7 @@ class WindowsStyle extends InternalStyle { return upperCase1 >= chars.lowerA && upperCase1 <= chars.lowerZ; } + @override bool pathsEqual(String path1, String path2) { if (identical(path1, path2)) return true; if (path1.length != path2.length) return false; @@ -155,6 +171,7 @@ class WindowsStyle extends InternalStyle { return true; } + @override int canonicalizeCodeUnit(int codeUnit) { if (codeUnit == chars.slash) return chars.backslash; if (codeUnit < chars.upperA) return codeUnit; @@ -162,5 +179,6 @@ class WindowsStyle extends InternalStyle { return codeUnit | _asciiCaseBit; } + @override String canonicalizePart(String part) => part.toLowerCase(); } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 918c4fef..95451a9a 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.6.4 +version: 1.6.5-dev description: >- A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 88aa2869..1a6f6230 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -9,7 +9,7 @@ import 'dart:html'; import 'package:test/test.dart'; import 'package:path/path.dart' as path; -main() { +void main() { group('new Context()', () { test('uses the window location if root and style are omitted', () { var context = path.Context(); @@ -19,7 +19,7 @@ main() { test('uses "." if root is omitted', () { var context = path.Context(style: path.Style.platform); - expect(context.current, "."); + expect(context.current, '.'); }); test('uses the host platform if style is omitted', () { diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index c53d8dcc..2a83c72b 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -9,7 +9,7 @@ import 'dart:io' as io; import 'package:test/test.dart'; import 'package:path/path.dart' as path; -main() { +void main() { group('new Context()', () { test('uses the current directory if root and style are omitted', () { var context = path.Context(); @@ -18,7 +18,7 @@ main() { test('uses "." if root is omitted', () { var context = path.Context(style: path.Style.platform); - expect(context.current, "."); + expect(context.current, '.'); }); test('uses the host platform if style is omitted', () { @@ -43,7 +43,7 @@ main() { test('uses the previous working directory if deleted', () { var dir = io.Directory.current.path; try { - var tempPath = path.normalize(path.absolute("temp_cwd")); + var tempPath = path.normalize(path.absolute('temp_cwd')); var temp = io.Directory(tempPath); temp.createSync(); io.Directory.current = temp; diff --git a/pkgs/path/test/path_map_test.dart b/pkgs/path/test/path_map_test.dart index 4e0c57e9..c9170e18 100644 --- a/pkgs/path/test/path_map_test.dart +++ b/pkgs/path/test/path_map_test.dart @@ -7,25 +7,25 @@ import 'package:test/test.dart'; import 'package:path/path.dart'; void main() { - group("considers equal", () { - test("two identical paths", () { + group('considers equal', () { + test('two identical paths', () { var map = PathMap(); - map[join("foo", "bar")] = 1; - map[join("foo", "bar")] = 2; + map[join('foo', 'bar')] = 1; + map[join('foo', 'bar')] = 2; expect(map, hasLength(1)); - expect(map, containsPair(join("foo", "bar"), 2)); + expect(map, containsPair(join('foo', 'bar'), 2)); }); - test("two logically equivalent paths", () { + test('two logically equivalent paths', () { var map = PathMap(); - map["foo"] = 1; - map[absolute("foo")] = 2; + map['foo'] = 1; + map[absolute('foo')] = 2; expect(map, hasLength(1)); - expect(map, containsPair("foo", 2)); - expect(map, containsPair(absolute("foo"), 2)); + expect(map, containsPair('foo', 2)); + expect(map, containsPair(absolute('foo'), 2)); }); - test("two nulls", () { + test('two nulls', () { var map = PathMap(); map[null] = 1; map[null] = 2; @@ -34,47 +34,47 @@ void main() { }); }); - group("considers unequal", () { - test("two distinct paths", () { + group('considers unequal', () { + test('two distinct paths', () { var map = PathMap(); - map["foo"] = 1; - map["bar"] = 2; + map['foo'] = 1; + map['bar'] = 2; expect(map, hasLength(2)); - expect(map, containsPair("foo", 1)); - expect(map, containsPair("bar", 2)); + expect(map, containsPair('foo', 1)); + expect(map, containsPair('bar', 2)); }); - test("a path and null", () { + test('a path and null', () { var map = PathMap(); - map["foo"] = 1; + map['foo'] = 1; map[null] = 2; expect(map, hasLength(2)); - expect(map, containsPair("foo", 1)); + expect(map, containsPair('foo', 1)); expect(map, containsPair(null, 2)); }); }); - test("uses the custom context", () { + test('uses the custom context', () { var map = PathMap(context: windows); - map["FOO"] = 1; - map["foo"] = 2; + map['FOO'] = 1; + map['foo'] = 2; expect(map, hasLength(1)); - expect(map, containsPair("fOo", 2)); + expect(map, containsPair('fOo', 2)); }); - group(".of()", () { + group('.of()', () { test("copies the existing map's keys", () { - var map = PathMap.of({"foo": 1, "bar": 2}); + var map = PathMap.of({'foo': 1, 'bar': 2}); expect(map, hasLength(2)); - expect(map, containsPair("foo", 1)); - expect(map, containsPair("bar", 2)); + expect(map, containsPair('foo', 1)); + expect(map, containsPair('bar', 2)); }); - test("uses the second value in the case of duplicates", () { - var map = PathMap.of({"foo": 1, absolute("foo"): 2}); + test('uses the second value in the case of duplicates', () { + var map = PathMap.of({'foo': 1, absolute('foo'): 2}); expect(map, hasLength(1)); - expect(map, containsPair("foo", 2)); - expect(map, containsPair(absolute("foo"), 2)); + expect(map, containsPair('foo', 2)); + expect(map, containsPair(absolute('foo'), 2)); }); }); } diff --git a/pkgs/path/test/path_set_test.dart b/pkgs/path/test/path_set_test.dart index f7b6bd0e..cfbab9e3 100644 --- a/pkgs/path/test/path_set_test.dart +++ b/pkgs/path/test/path_set_test.dart @@ -7,25 +7,25 @@ import 'package:test/test.dart'; import 'package:path/path.dart'; void main() { - group("considers equal", () { - test("two identical paths", () { + group('considers equal', () { + test('two identical paths', () { var set = PathSet(); - expect(set.add(join("foo", "bar")), isTrue); - expect(set.add(join("foo", "bar")), isFalse); + expect(set.add(join('foo', 'bar')), isTrue); + expect(set.add(join('foo', 'bar')), isFalse); expect(set, hasLength(1)); - expect(set, contains(join("foo", "bar"))); + expect(set, contains(join('foo', 'bar'))); }); - test("two logically equivalent paths", () { + test('two logically equivalent paths', () { var set = PathSet(); - expect(set.add("foo"), isTrue); - expect(set.add(absolute("foo")), isFalse); + expect(set.add('foo'), isTrue); + expect(set.add(absolute('foo')), isFalse); expect(set, hasLength(1)); - expect(set, contains("foo")); - expect(set, contains(absolute("foo"))); + expect(set, contains('foo')); + expect(set, contains(absolute('foo'))); }); - test("two nulls", () { + test('two nulls', () { var set = PathSet(); expect(set.add(null), isTrue); expect(set.add(null), isFalse); @@ -34,48 +34,48 @@ void main() { }); }); - group("considers unequal", () { - test("two distinct paths", () { + group('considers unequal', () { + test('two distinct paths', () { var set = PathSet(); - expect(set.add("foo"), isTrue); - expect(set.add("bar"), isTrue); + expect(set.add('foo'), isTrue); + expect(set.add('bar'), isTrue); expect(set, hasLength(2)); - expect(set, contains("foo")); - expect(set, contains("bar")); + expect(set, contains('foo')); + expect(set, contains('bar')); }); - test("a path and null", () { + test('a path and null', () { var set = PathSet(); - expect(set.add("foo"), isTrue); + expect(set.add('foo'), isTrue); expect(set.add(null), isTrue); expect(set, hasLength(2)); - expect(set, contains("foo")); + expect(set, contains('foo')); expect(set, contains(null)); }); }); - test("uses the custom context", () { + test('uses the custom context', () { var set = PathSet(context: windows); - expect(set.add("FOO"), isTrue); - expect(set.add("foo"), isFalse); + expect(set.add('FOO'), isTrue); + expect(set.add('foo'), isFalse); expect(set, hasLength(1)); - expect(set, contains("fOo")); + expect(set, contains('fOo')); }); - group(".of()", () { + group('.of()', () { test("copies the existing set's keys", () { - var set = PathSet.of(["foo", "bar"]); + var set = PathSet.of(['foo', 'bar']); expect(set, hasLength(2)); - expect(set, contains("foo")); - expect(set, contains("bar")); + expect(set, contains('foo')); + expect(set, contains('bar')); }); - test("uses the first value in the case of duplicates", () { - var set = PathSet.of(["foo", absolute("foo")]); + test('uses the first value in the case of duplicates', () { + var set = PathSet.of(['foo', absolute('foo')]); expect(set, hasLength(1)); - expect(set, contains("foo")); - expect(set, contains(absolute("foo"))); - expect(set.first, "foo"); + expect(set, contains('foo')); + expect(set, contains(absolute('foo'))); + expect(set.first, 'foo'); }); }); } diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index 16811595..ce7efdb3 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -5,7 +5,7 @@ import 'package:test/test.dart'; import 'package:path/path.dart' as path; -main() { +void main() { group('path.Style', () { test('name', () { expect(path.Style.posix.name, 'posix'); @@ -39,16 +39,16 @@ main() { test('posix is a default Context for the POSIX style', () { expect(path.posix.style, path.Style.posix); - expect(path.posix.current, "."); + expect(path.posix.current, '.'); }); test('windows is a default Context for the Windows style', () { expect(path.windows.style, path.Style.windows); - expect(path.windows.current, "."); + expect(path.windows.current, '.'); }); test('url is a default Context for the URL style', () { expect(path.url.style, path.Style.url); - expect(path.url.current, "."); + expect(path.url.current, '.'); }); } diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 6f7e08a7..b08d584a 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -7,7 +7,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; -main() { +void main() { var context = path.Context(style: path.Style.posix, current: '/root/path'); test('separator', () { diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 8da7eac0..140b9e24 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -4,13 +4,13 @@ // // Test "relative" on all styles of path.Context, on all platforms. -import "package:test/test.dart"; -import "package:path/path.dart" as path; +import 'package:test/test.dart'; +import 'package:path/path.dart' as path; -import "utils.dart"; +import 'utils.dart'; void main() { - test("test relative", () { + test('test relative', () { relativeTest(path.Context(style: path.Style.posix, current: '.'), '/'); relativeTest(path.Context(style: path.Style.posix, current: '/'), '/'); relativeTest( diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index c6fcd334..6af34e69 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -7,7 +7,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; -main() { +void main() { var context = path.Context( style: path.Style.url, current: 'http://dartlang.org/root/path'); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 09b81651..68e3c85b 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import "package:test/test.dart"; -import "package:path/path.dart" as p; +import 'package:test/test.dart'; +import 'package:path/path.dart' as p; /// A matcher for a closure that throws a [path.PathException]. final throwsPathException = throwsA(TypeMatcher()); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 47f7ab0c..096fa6f1 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -7,7 +7,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; -main() { +void main() { var context = path.Context(style: path.Style.windows, current: r'C:\root\path'); From 091927bc1eff29761a1f01b3711ba15767716497 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 11 Dec 2019 15:58:34 -0800 Subject: [PATCH 111/183] Use a system temp directory for io test (dart-lang/path#57) This allows the test to work in environments where we can't write to the `cwd` of the test process. --- pkgs/path/test/io_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 2a83c72b..da57a775 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -43,9 +43,8 @@ void main() { test('uses the previous working directory if deleted', () { var dir = io.Directory.current.path; try { - var tempPath = path.normalize(path.absolute('temp_cwd')); - var temp = io.Directory(tempPath); - temp.createSync(); + var temp = io.Directory.systemTemp.createTempSync('path_test'); + var tempPath = temp.path; io.Directory.current = temp; // Call "current" once so that it can be cached. From 98f724c46ddee90efa2cd782ff7c26d0e01a6093 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Jan 2020 09:48:05 -0800 Subject: [PATCH 112/183] Update very outdated doc reference to root -> current (dart-lang/path#60) --- pkgs/path/lib/src/context.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 8c7af5aa..df1eba20 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -415,7 +415,7 @@ class Context { } /// Attempts to convert [path] to an equivalent relative path relative to - /// [root]. + /// [current]. /// /// var context = new Context(current: '/root/path'); /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' @@ -442,9 +442,9 @@ class Context { /// var context = new Context(r'some/relative/path'); /// context.relative(r'/absolute/path'); // -> '/absolute/path' /// - /// If [root] is relative, it may be impossible to determine a path from - /// [from] to [path]. For example, if [root] and [path] are "." and [from] is - /// "/", no path can be determined. In this case, a [PathException] will be + /// If [current] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [current] and [path] are "." and [from] + /// is "/", no path can be determined. In this case, a [PathException] will be /// thrown. String relative(String path, {String from}) { // Avoid expensive computation if the path is already relative. From bf69ec294a018a49455cf2c16e03a30ec0c871df Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 25 Feb 2020 11:33:04 -0800 Subject: [PATCH 113/183] Remove lints duplicated in pkg:pedantic (dart-lang/path#62) --- pkgs/path/analysis_options.yaml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 0711acad..4a486d13 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -1,12 +1,11 @@ include: package:pedantic/analysis_options.yaml + analyzer: strong-mode: implicit-casts: false + linter: rules: - - avoid_empty_else - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - avoid_unused_constructor_parameters - await_only_futures - camel_case_types @@ -14,30 +13,16 @@ linter: - constant_identifier_names - control_flow_in_finally - directives_ordering - - empty_catches - - empty_constructor_bodies - empty_statements - hash_and_equals - implementation_imports - iterable_contains_unrelated_type - - library_names - - library_prefixes - list_remove_unrelated_type - non_constant_identifier_names - overridden_fields - package_api_docs - package_names - package_prefixed_library_names - - prefer_equal_for_default_values - - prefer_final_fields - - prefer_generic_function_type_aliases - - prefer_is_not_empty - - slash_for_doc_comments - test_types_in_equals - throw_in_finally - - type_init_formals - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_new - - unrelated_type_equality_checks - - valid_regexps From 3a256efda08812061114e54f4d201ba7e9c4f34f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 25 Feb 2020 12:24:51 -0800 Subject: [PATCH 114/183] Remove author from pubspec, fix outdated URL in readme (dart-lang/path#63) --- pkgs/path/README.md | 2 +- pkgs/path/pubspec.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 60991214..a767500c 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -9,7 +9,7 @@ functions, it will assume the current platform's path style and work with that. If you want to explicitly work with paths of a specific style, you can construct a [`p.Context`][Context] for that style. -[Context]: https://www.dartdocs.org/documentation/path/latest/path/Context-class.html +[Context]: https://pub.dev/documentation/path/latest/path/Context-class.html ## Using diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 95451a9a..c2f69280 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -5,7 +5,6 @@ description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web. -author: Dart Team homepage: https://github.com/dart-lang/path environment: From fb528813a3d9d479e5b98a374b7e7f8850b381c5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 25 Feb 2020 12:27:01 -0800 Subject: [PATCH 115/183] Enable and fix a number of lints (dart-lang/path#64) --- pkgs/path/analysis_options.yaml | 37 ++++++++++++++ pkgs/path/benchmark/benchmark.dart | 8 +-- pkgs/path/lib/path.dart | 7 ++- pkgs/path/lib/src/context.dart | 72 +++++++++++++-------------- pkgs/path/lib/src/internal_style.dart | 4 +- pkgs/path/lib/src/parsed_path.dart | 20 ++++---- pkgs/path/lib/src/path_set.dart | 2 +- pkgs/path/lib/src/style/posix.dart | 2 +- pkgs/path/lib/src/style/url.dart | 4 +- pkgs/path/lib/src/style/windows.dart | 8 +-- pkgs/path/test/browser_test.dart | 6 +-- pkgs/path/test/io_test.dart | 16 +++--- pkgs/path/test/path_map_test.dart | 16 +++--- pkgs/path/test/path_set_test.dart | 16 +++--- pkgs/path/test/path_test.dart | 4 +- pkgs/path/test/posix_test.dart | 14 +++--- pkgs/path/test/relative_test.dart | 2 +- pkgs/path/test/url_test.dart | 16 +++--- pkgs/path/test/utils.dart | 2 +- pkgs/path/test/windows_test.dart | 16 +++--- 20 files changed, 154 insertions(+), 118 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 4a486d13..47ffcd49 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -6,7 +6,14 @@ analyzer: linter: rules: + - avoid_catching_errors + - avoid_function_literals_in_foreach_calls + - avoid_private_typedef_functions + - avoid_redundant_argument_values + - avoid_renaming_method_parameters + - avoid_returning_null_for_void - avoid_unused_constructor_parameters + - avoid_void_async - await_only_futures - camel_case_types - cancel_subscriptions @@ -14,15 +21,45 @@ linter: - control_flow_in_finally - directives_ordering - empty_statements + - file_names - hash_and_equals - implementation_imports - iterable_contains_unrelated_type + - join_return_with_assignment + - lines_longer_than_80_chars - list_remove_unrelated_type + - missing_whitespace_between_adjacent_strings + - no_runtimeType_toString - non_constant_identifier_names + - only_throw_errors - overridden_fields - package_api_docs - package_names - package_prefixed_library_names + - prefer_asserts_in_initializer_lists + - prefer_const_constructors + - prefer_const_declarations + - prefer_expression_function_bodies + - prefer_final_locals + - prefer_function_declarations_over_variables + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_interpolation_to_compose_strings + - prefer_is_not_operator + - prefer_null_aware_operators + - prefer_relative_imports + - prefer_typing_uninitialized_variables + - prefer_void_to_null + - provide_deprecation_message + - sort_pub_dependencies - test_types_in_equals - throw_in_finally - unnecessary_brace_in_string_interps + - unnecessary_lambdas + - unnecessary_null_aware_assignments + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_string_interpolations + - use_string_buffers + - void_checks diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index 60550a47..a7fbb7ad 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -38,8 +38,8 @@ void main(List args) { arguments = args; for (var style in [p.Style.posix, p.Style.url, p.Style.windows]) { - var context = p.Context(style: style); - var files = genericPaths.toList()..addAll(platformPaths[style]); + final context = p.Context(style: style); + final files = genericPaths.toList()..addAll(platformPaths[style]); void benchmark(String name, Function function) { runBenchmark('${style.name}-$name', 100000, () { @@ -95,12 +95,12 @@ void runBenchmark(String name, int count, Function function) { function(); } - var stopwatch = Stopwatch()..start(); + final stopwatch = Stopwatch()..start(); for (var i = 0; i < count; i++) { function(); } - var rate = + final rate = (count / stopwatch.elapsedMicroseconds).toStringAsFixed(5).padLeft(9); print('${name.padLeft(32)}: $rate iter/us (${stopwatch.elapsed})'); } diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 29e87ece..a501e0c5 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -100,16 +100,15 @@ String get current { if (Style.platform == Style.url) { _current = uri.resolve('.').toString(); - return _current; } else { - var path = uri.toFilePath(); + final path = uri.toFilePath(); // Remove trailing '/' or '\' unless it is the only thing left // (for instance the root on Linux). - var lastIndex = path.length - 1; + final lastIndex = path.length - 1; assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); _current = lastIndex == 0 ? path : path.substring(0, lastIndex); - return _current; } + return _current; } /// The last value returned by [Uri.base]. diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index df1eba20..9e58e4ad 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -122,7 +122,7 @@ class Context { /// /// context.dirname('path/to/'); // -> 'path' String dirname(String path) { - var parsed = _parse(path); + final parsed = _parse(path); parsed.removeTrailingSeparators(); if (parsed.parts.isEmpty) return parsed.root ?? '.'; if (parsed.parts.length == 1) return parsed.root ?? '.'; @@ -216,7 +216,7 @@ class Context { String part6, String part7, String part8]) { - var parts = [ + final parts = [ part1, part2, part3, @@ -245,7 +245,7 @@ class Context { /// /// For a fixed number of parts, [join] is usually terser. String joinAll(Iterable parts) { - var buffer = StringBuffer(); + final buffer = StringBuffer(); var needsSeparator = false; var isAbsoluteAndNotRootRelative = false; @@ -253,8 +253,8 @@ class Context { if (isRootRelative(part) && isAbsoluteAndNotRootRelative) { // If the new part is root-relative, it preserves the previous root but // replaces the path after it. - var parsed = _parse(part); - var path = buffer.toString(); + final parsed = _parse(part); + final path = buffer.toString(); parsed.root = path.substring(0, style.rootLength(path, withDrive: true)); if (style.needsSeparator(parsed.root)) { @@ -304,7 +304,7 @@ class Context { /// // Windows /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] List split(String path) { - var parsed = _parse(path); + final parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. parsed.parts = parsed.parts.where((part) => part.isNotEmpty).toList(); if (parsed.root != null) parsed.parts.insert(0, parsed.root); @@ -327,7 +327,7 @@ class Context { path = absolute(path); if (style != Style.windows && !_needsNormalization(path)) return path; - var parsed = _parse(path); + final parsed = _parse(path); parsed.normalize(canonicalize: true); return parsed.toString(); } @@ -343,7 +343,7 @@ class Context { String normalize(String path) { if (!_needsNormalization(path)) return path; - var parsed = _parse(path); + final parsed = _parse(path); parsed.normalize(); return parsed.toString(); } @@ -351,14 +351,14 @@ class Context { /// Returns whether [path] needs to be normalized. bool _needsNormalization(String path) { var start = 0; - var codeUnits = path.codeUnits; + final codeUnits = path.codeUnits; int previousPrevious; int previous; // Skip past the root before we start looking for snippets that need // normalization. We want to normalize "//", but not when it's part of // "http://". - var root = style.rootLength(path); + final root = style.rootLength(path); if (root != 0) { start = root; previous = chars.slash; @@ -373,7 +373,7 @@ class Context { } for (var i = start; i < codeUnits.length; i++) { - var codeUnit = codeUnits[i]; + final codeUnit = codeUnits[i]; if (style.isSeparator(codeUnit)) { // Forward slashes in Windows paths are normalized to backslashes. if (style == Style.windows && codeUnit == chars.slash) return true; @@ -469,8 +469,8 @@ class Context { throw PathException('Unable to find a path to "$path" from "$from".'); } - var fromParsed = _parse(from)..normalize(); - var pathParsed = _parse(path)..normalize(); + final fromParsed = _parse(from)..normalize(); + final pathParsed = _parse(path)..normalize(); if (fromParsed.parts.isNotEmpty && fromParsed.parts[0] == '.') { return pathParsed.toString(); @@ -552,8 +552,8 @@ class Context { // Make both paths the same level of relative. We're only able to do the // quick comparison if both paths are in the same format, and making a path // absolute is faster than making it relative. - var parentIsAbsolute = isAbsolute(parent); - var childIsAbsolute = isAbsolute(child); + final parentIsAbsolute = isAbsolute(parent); + final childIsAbsolute = isAbsolute(child); if (parentIsAbsolute && !childIsAbsolute) { child = absolute(child); if (style.isRootRelative(parent)) parent = absolute(parent); @@ -561,8 +561,8 @@ class Context { parent = absolute(parent); if (style.isRootRelative(child)) child = absolute(child); } else if (childIsAbsolute && parentIsAbsolute) { - var childIsRootRelative = style.isRootRelative(child); - var parentIsRootRelative = style.isRootRelative(parent); + final childIsRootRelative = style.isRootRelative(child); + final parentIsRootRelative = style.isRootRelative(parent); if (childIsRootRelative && !parentIsRootRelative) { child = absolute(child); @@ -571,7 +571,7 @@ class Context { } } - var result = _isWithinOrEqualsFast(parent, child); + final result = _isWithinOrEqualsFast(parent, child); if (result != _PathRelation.inconclusive) return result; String relative; @@ -600,8 +600,8 @@ class Context { // a single dot easily enough. if (parent == '.') parent = ''; - var parentRootLength = style.rootLength(parent); - var childRootLength = style.rootLength(child); + final parentRootLength = style.rootLength(parent); + final childRootLength = style.rootLength(child); // If the roots aren't the same length, we know both paths are absolute or // both are root-relative, and thus that the roots are meaningfully @@ -616,8 +616,8 @@ class Context { // isWithin("C:/bar", "D:/bar/baz") //=> false // isWithin("http://example.com/", "http://example.org/bar") //=> false for (var i = 0; i < parentRootLength; i++) { - var parentCodeUnit = parent.codeUnitAt(i); - var childCodeUnit = child.codeUnitAt(i); + final parentCodeUnit = parent.codeUnitAt(i); + final childCodeUnit = child.codeUnitAt(i); if (!style.codeUnitsEqual(parentCodeUnit, childCodeUnit)) { return _PathRelation.different; } @@ -720,12 +720,12 @@ class Context { // As long as the remainders of the two paths don't have any unresolved // ".." components, we can be confident that [child] is not within // [parent]. - var childDirection = _pathDirection(child, childIndex); + final childDirection = _pathDirection(child, childIndex); if (childDirection != _PathDirection.belowRoot) { return _PathRelation.inconclusive; } - var parentDirection = _pathDirection(parent, parentIndex); + final parentDirection = _pathDirection(parent, parentIndex); if (parentDirection != _PathDirection.belowRoot) { return _PathRelation.inconclusive; } @@ -747,7 +747,7 @@ class Context { lastParentSeparator ??= math.max(0, parentRootLength - 1); } - var direction = + final direction = _pathDirection(parent, lastParentSeparator ?? parentRootLength - 1); if (direction == _PathDirection.atRoot) return _PathRelation.equal; return direction == _PathDirection.aboveRoot @@ -758,7 +758,7 @@ class Context { // We've reached the end of the parent path, which means it's time to make a // decision. Before we do, though, we'll check the rest of the child to see // what that tells us. - var direction = _pathDirection(child, childIndex); + final direction = _pathDirection(child, childIndex); // If there are no more components in the child, then it's the same as // the parent. @@ -818,7 +818,7 @@ class Context { if (i == path.length) break; // Move through the path component to the next separator. - var start = i; + final start = i; while (i < path.length && !style.isSeparator(path.codeUnitAt(i))) { i++; } @@ -865,10 +865,10 @@ class Context { // paths have the same hash code. path = absolute(path); - var result = _hashFast(path); + final result = _hashFast(path); if (result != null) return result; - var parsed = _parse(path); + final parsed = _parse(path); parsed.normalize(); return _hashFast(parsed.toString()); } @@ -882,7 +882,7 @@ class Context { var beginning = true; var wasSeparator = true; for (var i = 0; i < path.length; i++) { - var codeUnit = style.canonicalizeCodeUnit(path.codeUnitAt(i)); + final codeUnit = style.canonicalizeCodeUnit(path.codeUnitAt(i)); // Take advantage of the fact that collisions are allowed to ignore // separators entirely. This lets us avoid worrying about cases like @@ -902,7 +902,7 @@ class Context { // We've hit "/." at the end of the path, which we can ignore. if (i + 1 == path.length) break; - var next = path.codeUnitAt(i + 1); + final next = path.codeUnitAt(i + 1); // We can just ignore "/./", since they don't affect the semantics of // the path. @@ -934,7 +934,7 @@ class Context { /// /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' String withoutExtension(String path) { - var parsed = _parse(path); + final parsed = _parse(path); for (var i = parsed.parts.length - 1; i >= 0; i--) { if (parsed.parts[i].isNotEmpty) { @@ -1032,7 +1032,7 @@ class Context { /// // -> r'a/b.dart' /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' String prettyUri(uri) { - var typedUri = _parseUri(uri); + final typedUri = _parseUri(uri); if (typedUri.scheme == 'file' && style == Style.url) { return typedUri.toString(); } else if (typedUri.scheme != 'file' && @@ -1041,8 +1041,8 @@ class Context { return typedUri.toString(); } - var path = normalize(fromUri(typedUri)); - var rel = relative(path); + final path = normalize(fromUri(typedUri)); + final rel = relative(path); // Only return a relative path if it's actually shorter than the absolute // path. This avoids ugly things like long "../" chains to get to the root @@ -1075,7 +1075,7 @@ void _validateArgList(String method, List args) { } // Show the arguments. - var message = StringBuffer(); + final message = StringBuffer(); message.write('$method('); message.write(args .take(numArgs) diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 53b4adee..b7f6e934 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -44,7 +44,7 @@ abstract class InternalStyle extends Style { /// returns `null`. @override String getRoot(String path) { - var length = rootLength(path); + final length = rootLength(path); if (length > 0) return path.substring(0, length); return isRootRelative(path) ? path[0] : null; } @@ -61,7 +61,7 @@ abstract class InternalStyle extends Style { /// Returns the URI that represents the relative path made of [parts]. @override Uri relativePathToUri(String path) { - var segments = context.split(path); + final segments = context.split(path); // Ensure that a trailing slash in the path produces a trailing slash in the // URL. diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 42c9f2fb..9efd5299 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -40,13 +40,13 @@ class ParsedPath { factory ParsedPath.parse(String path, InternalStyle style) { // Remove the root prefix, if any. - var root = style.getRoot(path); - var isRootRelative = style.isRootRelative(path); + final root = style.getRoot(path); + final isRootRelative = style.isRootRelative(path); if (root != null) path = path.substring(root.length); // Split the parts on path separators. - var parts = []; - var separators = []; + final parts = []; + final separators = []; var start = 0; @@ -78,7 +78,7 @@ class ParsedPath { this.style, this.root, this.isRootRelative, this.parts, this.separators); String get basename { - var copy = clone(); + final copy = clone(); copy.removeTrailingSeparators(); if (copy.parts.isEmpty) return root ?? ''; return copy.parts.last; @@ -100,7 +100,7 @@ class ParsedPath { void normalize({bool canonicalize = false}) { // Handle '.', '..', and empty parts. var leadingDoubles = 0; - var newParts = []; + final newParts = []; for (var part in parts) { if (part == '.' || part == '') { // Do nothing. Ignore it. @@ -128,7 +128,7 @@ class ParsedPath { } // Canonicalize separators. - var newSeparators = List.generate( + final newSeparators = List.generate( newParts.length, (_) => style.separator, growable: true); newSeparators.insert( @@ -150,7 +150,7 @@ class ParsedPath { @override String toString() { - var builder = StringBuffer(); + final builder = StringBuffer(); if (root != null) builder.write(root); for (var i = 0; i < parts.length; i++) { builder.write(separators[i]); @@ -167,12 +167,12 @@ class ParsedPath { /// 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 _splitExtension() { - var file = parts.lastWhere((p) => p != '', orElse: () => null); + final file = parts.lastWhere((p) => p != '', orElse: () => null); if (file == null) return ['', '']; if (file == '..') return ['..', '']; - var lastDot = file.lastIndexOf('.'); + final lastDot = file.lastIndexOf('.'); // If there is no dot, or it's the first character, like '.bashrc', it // doesn't count. diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index 03a7eebb..01397c8c 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -62,7 +62,7 @@ class PathSet extends IterableBase implements Set { void clear() => _inner.clear(); @override - bool contains(Object other) => _inner.contains(other); + bool contains(Object element) => _inner.contains(element); @override bool containsAll(Iterable other) => _inner.containsAll(other); diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 0b9803b5..f8b7e781 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -59,7 +59,7 @@ class PosixStyle extends InternalStyle { @override Uri absolutePathToUri(String path) { - var parsed = ParsedPath.parse(path, this); + final parsed = ParsedPath.parse(path, this); if (parsed.parts.isEmpty) { // If the path is a bare root (e.g. "/"), [components] will // currently be empty. We add two empty components so the URL constructor diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index b9d6dffa..1f99bd55 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -51,7 +51,7 @@ class UrlStyle extends InternalStyle { if (isSeparator(path.codeUnitAt(0))) return 1; for (var i = 0; i < path.length; i++) { - var codeUnit = path.codeUnitAt(i); + final codeUnit = path.codeUnitAt(i); if (isSeparator(codeUnit)) return 0; if (codeUnit == chars.colon) { if (i == 0) return 0; @@ -59,7 +59,7 @@ class UrlStyle extends InternalStyle { // The root part is up until the next '/', or the full path. Skip ':' // (and '//' if it exists) and search for '/' after that. if (path.startsWith('//', i + 1)) i += 3; - var index = path.indexOf('/', i); + final index = path.indexOf('/', i); if (index <= 0) return path.length; // file: URLs sometimes consider Windows drive letters part of the root. diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 287733a8..64d5a3f3 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -77,7 +77,7 @@ class WindowsStyle extends InternalStyle { @override String getRelativeRoot(String path) { - var length = rootLength(path); + final length = rootLength(path); if (length == 1) return path[0]; return null; } @@ -105,13 +105,13 @@ class WindowsStyle extends InternalStyle { @override Uri absolutePathToUri(String path) { - var parsed = ParsedPath.parse(path, this); + final parsed = ParsedPath.parse(path, this); if (parsed.root.startsWith(r'\\')) { // Network paths become "file://server/share/path/to/file". // The root is of the form "\\server\share". We want "server" to be the // URI host, and "share" to be the first element of the path. - var rootParts = parsed.root.split('\\').where((part) => part != ''); + final rootParts = parsed.root.split('\\').where((part) => part != ''); parsed.parts.insert(0, rootParts.last); if (parsed.hasTrailingSeparator) { @@ -155,7 +155,7 @@ class WindowsStyle extends InternalStyle { if (codeUnit1 ^ codeUnit2 != _asciiCaseBit) return false; // Now we just need to verify that one of the code units is an ASCII letter. - var upperCase1 = codeUnit1 | _asciiCaseBit; + final upperCase1 = codeUnit1 | _asciiCaseBit; return upperCase1 >= chars.lowerA && upperCase1 <= chars.lowerZ; } diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 1a6f6230..ac25dbe2 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -12,18 +12,18 @@ import 'package:path/path.dart' as path; void main() { group('new Context()', () { test('uses the window location if root and style are omitted', () { - var context = path.Context(); + final context = path.Context(); expect(context.current, Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { - var context = path.Context(style: path.Style.platform); + final context = path.Context(style: path.Style.platform); expect(context.current, '.'); }); test('uses the host platform if style is omitted', () { - var context = path.Context(); + final context = path.Context(); expect(context.style, path.Style.platform); }); }); diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index da57a775..293d762a 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -12,17 +12,17 @@ import 'package:path/path.dart' as path; void main() { group('new Context()', () { test('uses the current directory if root and style are omitted', () { - var context = path.Context(); + final context = path.Context(); expect(context.current, io.Directory.current.path); }); test('uses "." if root is omitted', () { - var context = path.Context(style: path.Style.platform); + final context = path.Context(style: path.Style.platform); expect(context.current, '.'); }); test('uses the host platform if style is omitted', () { - var context = path.Context(); + final context = path.Context(); expect(context.style, path.Style.platform); }); }); @@ -41,10 +41,10 @@ void main() { }); test('uses the previous working directory if deleted', () { - var dir = io.Directory.current.path; + final dir = io.Directory.current.path; try { - var temp = io.Directory.systemTemp.createTempSync('path_test'); - var tempPath = temp.path; + final temp = io.Directory.systemTemp.createTempSync('path_test'); + final tempPath = temp.path; io.Directory.current = temp; // Call "current" once so that it can be cached. @@ -61,7 +61,7 @@ void main() { }); test('registers changes to the working directory', () { - var dir = io.Directory.current.path; + final dir = io.Directory.current.path; try { expect(path.absolute('foo/bar'), equals(path.join(dir, 'foo/bar'))); expect( @@ -81,7 +81,7 @@ void main() { // rather than just a custom context because we do some processing in // [path.current] that has clobbered the root in the past. test('absolute works on root working directory', () { - var dir = path.current; + final dir = path.current; try { io.Directory.current = path.rootPrefix(path.current); diff --git a/pkgs/path/test/path_map_test.dart b/pkgs/path/test/path_map_test.dart index c9170e18..127d7c6f 100644 --- a/pkgs/path/test/path_map_test.dart +++ b/pkgs/path/test/path_map_test.dart @@ -9,7 +9,7 @@ import 'package:path/path.dart'; void main() { group('considers equal', () { test('two identical paths', () { - var map = PathMap(); + final map = PathMap(); map[join('foo', 'bar')] = 1; map[join('foo', 'bar')] = 2; expect(map, hasLength(1)); @@ -17,7 +17,7 @@ void main() { }); test('two logically equivalent paths', () { - var map = PathMap(); + final map = PathMap(); map['foo'] = 1; map[absolute('foo')] = 2; expect(map, hasLength(1)); @@ -26,7 +26,7 @@ void main() { }); test('two nulls', () { - var map = PathMap(); + final map = PathMap(); map[null] = 1; map[null] = 2; expect(map, hasLength(1)); @@ -36,7 +36,7 @@ void main() { group('considers unequal', () { test('two distinct paths', () { - var map = PathMap(); + final map = PathMap(); map['foo'] = 1; map['bar'] = 2; expect(map, hasLength(2)); @@ -45,7 +45,7 @@ void main() { }); test('a path and null', () { - var map = PathMap(); + final map = PathMap(); map['foo'] = 1; map[null] = 2; expect(map, hasLength(2)); @@ -55,7 +55,7 @@ void main() { }); test('uses the custom context', () { - var map = PathMap(context: windows); + final map = PathMap(context: windows); map['FOO'] = 1; map['foo'] = 2; expect(map, hasLength(1)); @@ -64,14 +64,14 @@ void main() { group('.of()', () { test("copies the existing map's keys", () { - var map = PathMap.of({'foo': 1, 'bar': 2}); + final map = PathMap.of({'foo': 1, 'bar': 2}); expect(map, hasLength(2)); expect(map, containsPair('foo', 1)); expect(map, containsPair('bar', 2)); }); test('uses the second value in the case of duplicates', () { - var map = PathMap.of({'foo': 1, absolute('foo'): 2}); + final map = PathMap.of({'foo': 1, absolute('foo'): 2}); expect(map, hasLength(1)); expect(map, containsPair('foo', 2)); expect(map, containsPair(absolute('foo'), 2)); diff --git a/pkgs/path/test/path_set_test.dart b/pkgs/path/test/path_set_test.dart index cfbab9e3..3214e19e 100644 --- a/pkgs/path/test/path_set_test.dart +++ b/pkgs/path/test/path_set_test.dart @@ -9,7 +9,7 @@ import 'package:path/path.dart'; void main() { group('considers equal', () { test('two identical paths', () { - var set = PathSet(); + final set = PathSet(); expect(set.add(join('foo', 'bar')), isTrue); expect(set.add(join('foo', 'bar')), isFalse); expect(set, hasLength(1)); @@ -17,7 +17,7 @@ void main() { }); test('two logically equivalent paths', () { - var set = PathSet(); + final set = PathSet(); expect(set.add('foo'), isTrue); expect(set.add(absolute('foo')), isFalse); expect(set, hasLength(1)); @@ -26,7 +26,7 @@ void main() { }); test('two nulls', () { - var set = PathSet(); + final set = PathSet(); expect(set.add(null), isTrue); expect(set.add(null), isFalse); expect(set, hasLength(1)); @@ -36,7 +36,7 @@ void main() { group('considers unequal', () { test('two distinct paths', () { - var set = PathSet(); + final set = PathSet(); expect(set.add('foo'), isTrue); expect(set.add('bar'), isTrue); expect(set, hasLength(2)); @@ -45,7 +45,7 @@ void main() { }); test('a path and null', () { - var set = PathSet(); + final set = PathSet(); expect(set.add('foo'), isTrue); expect(set.add(null), isTrue); expect(set, hasLength(2)); @@ -55,7 +55,7 @@ void main() { }); test('uses the custom context', () { - var set = PathSet(context: windows); + final set = PathSet(context: windows); expect(set.add('FOO'), isTrue); expect(set.add('foo'), isFalse); expect(set, hasLength(1)); @@ -64,14 +64,14 @@ void main() { group('.of()', () { test("copies the existing set's keys", () { - var set = PathSet.of(['foo', 'bar']); + final set = PathSet.of(['foo', 'bar']); expect(set, hasLength(2)); expect(set, contains('foo')); expect(set, contains('bar')); }); test('uses the first value in the case of duplicates', () { - var set = PathSet.of(['foo', absolute('foo')]); + final set = PathSet.of(['foo', absolute('foo')]); expect(set, hasLength(1)); expect(set, contains('foo')); expect(set, contains(absolute('foo'))); diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index ce7efdb3..6625ac4b 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -27,12 +27,12 @@ void main() { group('new Context()', () { test('uses the given current directory', () { - var context = path.Context(current: '/a/b/c'); + final context = path.Context(current: '/a/b/c'); expect(context.current, '/a/b/c'); }); test('uses the given style', () { - var context = path.Context(style: path.Style.windows); + final context = path.Context(style: path.Style.windows); expect(context.style, path.Style.windows); }); }); diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index b08d584a..bd6c3e02 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; void main() { - var context = path.Context(style: path.Style.posix, current: '/root/path'); + final context = path.Context(style: path.Style.posix, current: '/root/path'); test('separator', () { expect(context.separator, '/'); @@ -366,7 +366,7 @@ void main() { }); group('from relative root', () { - var r = path.Context(style: path.Style.posix, current: 'foo/bar'); + final r = path.Context(style: path.Style.posix, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('/'), equals('/')); @@ -387,7 +387,7 @@ void main() { }); test('from a root with extension', () { - var r = path.Context(style: path.Style.posix, current: '/dir.ext'); + final r = path.Context(style: path.Style.posix, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); @@ -400,7 +400,7 @@ void main() { }); test('with a root parameter and a relative root', () { - var r = path.Context(style: path.Style.posix, current: 'relative/root'); + final r = path.Context(style: path.Style.posix, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); expect( @@ -409,7 +409,7 @@ void main() { }); test('from a . root', () { - var r = path.Context(style: path.Style.posix, current: '.'); + final r = path.Context(style: path.Style.posix, current: '.'); expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); }); @@ -440,7 +440,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.posix, current: 'foo/bar'); + final r = path.Context(style: path.Style.posix, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); @@ -477,7 +477,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.posix, current: 'foo/bar'); + final r = path.Context(style: path.Style.posix, current: 'foo/bar'); expectEquals(r, 'a/b', 'a/b'); expectNotEquals(r, '.', 'foo/bar'); expectNotEquals(r, '.', '../a/b'); diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 140b9e24..657611be 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -26,7 +26,7 @@ void main() { } void relativeTest(path.Context context, String prefix) { - var isRelative = (context.current == '.'); + final isRelative = context.current == '.'; // Cases where the arguments are absolute paths. void expectRelative(String result, String pathArg, String fromArg) { expect(context.relative(pathArg, from: fromArg), context.normalize(result)); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 6af34e69..ebb45328 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; void main() { - var context = path.Context( + final context = path.Context( style: path.Style.url, current: 'http://dartlang.org/root/path'); test('separator', () { @@ -549,7 +549,7 @@ void main() { }); group('from relative root', () { - var r = path.Context(style: path.Style.url, current: 'foo/bar'); + final r = path.Context(style: path.Style.url, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -574,7 +574,7 @@ void main() { }); group('from root-relative root', () { - var r = path.Context(style: path.Style.url, current: '/foo/bar'); + final r = path.Context(style: path.Style.url, current: '/foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -599,7 +599,7 @@ void main() { }); test('from a root with extension', () { - var r = path.Context(style: path.Style.url, current: '/dir.ext'); + final r = path.Context(style: path.Style.url, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); @@ -643,7 +643,7 @@ void main() { }); test('with a root parameter and a relative root', () { - var r = path.Context(style: path.Style.url, current: 'relative/root'); + final r = path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('/foo/bar/baz')); @@ -669,7 +669,7 @@ void main() { }); test('from a . root', () { - var r = path.Context(style: path.Style.url, current: '.'); + final r = path.Context(style: path.Style.url, current: '.'); expect(r.relative('http://dartlang.org/foo/bar/baz'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); @@ -727,7 +727,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.url, current: 'foo/bar'); + final r = path.Context(style: path.Style.url, current: 'foo/bar'); expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); @@ -773,7 +773,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.posix, current: 'foo/bar'); + final r = path.Context(style: path.Style.posix, current: 'foo/bar'); expectEquals(r, 'a/b', 'a/b'); expectNotEquals(r, '.', 'foo/bar'); expectNotEquals(r, '.', '../a/b'); diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 68e3c85b..dfd62a86 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -6,7 +6,7 @@ import 'package:test/test.dart'; import 'package:path/path.dart' as p; /// A matcher for a closure that throws a [path.PathException]. -final throwsPathException = throwsA(TypeMatcher()); +final throwsPathException = throwsA(const TypeMatcher()); void expectEquals(p.Context context, String path1, String path2) { expect(context.equals(path1, path2), isTrue, diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 096fa6f1..2d6b90f9 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as path; import 'utils.dart'; void main() { - var context = + final context = path.Context(style: path.Style.windows, current: r'C:\root\path'); test('separator', () { @@ -449,7 +449,7 @@ void main() { }); group('from relative root', () { - var r = path.Context(style: path.Style.windows, current: r'foo\bar'); + final r = path.Context(style: path.Style.windows, current: r'foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -472,7 +472,7 @@ void main() { }); group('from root-relative root', () { - var r = path.Context(style: path.Style.windows, current: r'\foo\bar'); + final r = path.Context(style: path.Style.windows, current: r'\foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -497,7 +497,7 @@ void main() { }); test('from a root with extension', () { - var r = path.Context(style: path.Style.windows, current: r'C:\dir.ext'); + final r = path.Context(style: path.Style.windows, current: r'C:\dir.ext'); expect(r.relative(r'C:\dir.ext\file'), 'file'); }); @@ -513,7 +513,7 @@ void main() { }); test('with a root parameter and a relative root', () { - var r = + final r = path.Context(style: path.Style.windows, current: r'relative\root'); expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); expect(() => r.relative('..', from: r'C:\foo\bar'), throwsPathException); @@ -528,7 +528,7 @@ void main() { }); test('from a . root', () { - var r = path.Context(style: path.Style.windows, current: '.'); + final r = path.Context(style: path.Style.windows, current: '.'); expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); expect(r.relative(r'\foo\bar\baz'), equals(r'\foo\bar\baz')); @@ -575,7 +575,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.windows, current: r'foo\bar'); + final r = path.Context(style: path.Style.windows, current: r'foo\bar'); expect(r.isWithin('.', r'a\b\c'), isTrue); expect(r.isWithin('.', r'..\a\b\c'), isFalse); expect(r.isWithin('.', r'..\..\a\foo\b\c'), isFalse); @@ -627,7 +627,7 @@ void main() { }); test('from a relative root', () { - var r = path.Context(style: path.Style.windows, current: r'foo\bar'); + final r = path.Context(style: path.Style.windows, current: r'foo\bar'); expectEquals(r, r'a\b', r'a\b'); expectNotEquals(r, '.', r'foo\bar'); expectNotEquals(r, '.', r'..\a\b'); From 66a899e84b83bdcc6ca373dc4437a044586b8286 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 25 Feb 2020 13:01:21 -0800 Subject: [PATCH 116/183] Cleanup relative_test (dart-lang/path#65) Define individual groups and tests for most cases Makes debugging individual failures much easier --- pkgs/path/test/relative_test.dart | 147 +++++++++++++++++------------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/pkgs/path/test/relative_test.dart b/pkgs/path/test/relative_test.dart index 657611be..189af73d 100644 --- a/pkgs/path/test/relative_test.dart +++ b/pkgs/path/test/relative_test.dart @@ -4,82 +4,103 @@ // // Test "relative" on all styles of path.Context, on all platforms. -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; import 'utils.dart'; void main() { - test('test relative', () { - relativeTest(path.Context(style: path.Style.posix, current: '.'), '/'); - relativeTest(path.Context(style: path.Style.posix, current: '/'), '/'); - relativeTest( - path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); - relativeTest(path.Context(style: path.Style.windows, current: '.'), r'c:\'); - relativeTest(path.Context(style: path.Style.url, current: 'file:///'), - 'http://myserver/'); - relativeTest( - path.Context(style: path.Style.url, current: '.'), 'http://myserver/'); - relativeTest(path.Context(style: path.Style.url, current: 'file:///'), '/'); - relativeTest(path.Context(style: path.Style.url, current: '.'), '/'); - }); + relativeTest(path.Context(style: path.Style.posix, current: '.'), '/'); + relativeTest(path.Context(style: path.Style.posix, current: '/'), '/'); + relativeTest( + path.Context(style: path.Style.windows, current: r'd:\'), + r'c:\', + ); + relativeTest(path.Context(style: path.Style.windows, current: '.'), r'c:\'); + relativeTest( + path.Context(style: path.Style.url, current: 'file:///'), + 'http://myserver/', + ); + relativeTest( + path.Context(style: path.Style.url, current: '.'), + 'http://myserver/', + ); + relativeTest(path.Context(style: path.Style.url, current: 'file:///'), '/'); + relativeTest(path.Context(style: path.Style.url, current: '.'), '/'); } void relativeTest(path.Context context, String prefix) { - final isRelative = context.current == '.'; // Cases where the arguments are absolute paths. void expectRelative(String result, String pathArg, String fromArg) { - expect(context.relative(pathArg, from: fromArg), context.normalize(result)); + test('relative $pathArg from $fromArg', () { + expect( + context.relative(pathArg, from: fromArg), + context.normalize(result), + ); + }); } - expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); - expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b/'); - expectRelative('.', '${prefix}a', '${prefix}a'); - // Trailing slashes in the inputs have no effect. - expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d/'); - expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d'); - expectRelative('../../z/x/y', '${prefix}a/b/z/x/y/', '${prefix}a/b/c/d'); - expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c'); - expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c/'); + group('${context.style}', () { + expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); + expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b/'); + expectRelative('.', '${prefix}a', '${prefix}a'); + // Trailing slashes in the inputs have no effect. + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d/'); + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d'); + expectRelative('../../z/x/y', '${prefix}a/b/z/x/y/', '${prefix}a/b/c/d'); + expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c'); + expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c/'); - // Cases where the arguments are relative paths. - expectRelative('c/d', 'a/b/c/d', 'a/b'); - expectRelative('.', 'a/b/c', 'a/b/c'); - expectRelative('.', 'a/d/../b/c', 'a/b/c/'); - expectRelative('.', '', ''); - expectRelative('.', '.', ''); - expectRelative('.', '', '.'); - expectRelative('.', '.', '.'); - expectRelative('.', '..', '..'); - if (isRelative) expectRelative('..', '..', '.'); - expectRelative('a', 'a', ''); - expectRelative('a', 'a', '.'); - expectRelative('..', '.', 'a'); - expectRelative('.', 'a/b/f/../c', 'a/e/../b/c'); - expectRelative('d', 'a/b/f/../c/d', 'a/e/../b/c'); - expectRelative('..', 'a/b/f/../c', 'a/e/../b/c/e/'); - expectRelative('../..', '', 'a/b/'); - if (isRelative) expectRelative('../../..', '..', 'a/b/'); - expectRelative('../b/c/d', 'b/c/d/', 'a/'); - expectRelative('../a/b/c', 'x/y/a//b/./f/../c', 'x//y/z'); + // Cases where the arguments are relative paths. + expectRelative('c/d', 'a/b/c/d', 'a/b'); + expectRelative('.', 'a/b/c', 'a/b/c'); + expectRelative('.', 'a/d/../b/c', 'a/b/c/'); + expectRelative('.', '', ''); + expectRelative('.', '.', ''); + expectRelative('.', '', '.'); + expectRelative('.', '.', '.'); + expectRelative('.', '..', '..'); + expectRelative('a', 'a', ''); + expectRelative('a', 'a', '.'); + expectRelative('..', '.', 'a'); + expectRelative('.', 'a/b/f/../c', 'a/e/../b/c'); + expectRelative('d', 'a/b/f/../c/d', 'a/e/../b/c'); + expectRelative('..', 'a/b/f/../c', 'a/e/../b/c/e/'); + expectRelative('../..', '', 'a/b/'); + expectRelative('../b/c/d', 'b/c/d/', 'a/'); + expectRelative('../a/b/c', 'x/y/a//b/./f/../c', 'x//y/z'); - // Case where from is an exact substring of path. - expectRelative('a/b', '${prefix}x/y//a/b', '${prefix}x/y/'); - expectRelative('a/b', 'x/y//a/b', 'x/y/'); - expectRelative('../ya/b', '${prefix}x/ya/b', '${prefix}x/y'); - expectRelative('../ya/b', 'x/ya/b', 'x/y'); - expectRelative('../b', 'x/y/../b', 'x/y/.'); - expectRelative('a/b/c', 'x/y/a//b/./f/../c', 'x/y'); - expectRelative('.', '${prefix}x/y//', '${prefix}x/y/'); - expectRelative('.', '${prefix}x/y/', '${prefix}x/y'); + // Case where from is an exact substring of path. + expectRelative('a/b', '${prefix}x/y//a/b', '${prefix}x/y/'); + expectRelative('a/b', 'x/y//a/b', 'x/y/'); + expectRelative('../ya/b', '${prefix}x/ya/b', '${prefix}x/y'); + expectRelative('../ya/b', 'x/ya/b', 'x/y'); + expectRelative('../b', 'x/y/../b', 'x/y/.'); + expectRelative('a/b/c', 'x/y/a//b/./f/../c', 'x/y'); + expectRelative('.', '${prefix}x/y//', '${prefix}x/y/'); + expectRelative('.', '${prefix}x/y/', '${prefix}x/y'); - // Should always throw - no relative path can be constructed. - if (isRelative) { - expect(() => context.relative('.', from: '..'), throwsPathException); - expect(() => context.relative('a/b', from: '../../d'), throwsPathException); - expect(() => context.relative('a/b', from: '${prefix}a/b'), - throwsPathException); - // An absolute path relative from a relative path returns the absolute path. - expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); - } + if (context.current == '.') { + group('current directory', () { + // Should always throw - no relative path can be constructed. + test('throws', () { + expect(() => context.relative('.', from: '..'), throwsPathException); + expect( + () => context.relative('a/b', from: '../../d'), + throwsPathException, + ); + expect( + () => context.relative('a/b', from: '${prefix}a/b'), + throwsPathException, + ); + }); + + expectRelative('..', '..', '.'); + expectRelative('../../..', '..', 'a/b/'); + + // absolute path relative from a relative path returns the absolute path + expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); + }); + } + }); } From d1bdce2a113a2fe3ef4f3de64b4b469bbc0e95e6 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 25 Feb 2020 13:43:00 -0800 Subject: [PATCH 117/183] Run tests on all operating systems (dart-lang/path#66) skip failures for now --- pkgs/path/.travis.yml | 7 +++++++ pkgs/path/test/io_test.dart | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index f63530ca..5afa17a9 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -9,6 +9,13 @@ dart_task: matrix: include: + # Run tests on mac and windows – but just dev SDKs, no browser + - dart: dev + dart_task: test + os: windows + - dart: dev + dart_task: test + os: osx # Only validate formatting using the dev release - dart: dev dart_task: dartfmt diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 293d762a..fdd152c2 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -3,11 +3,10 @@ // BSD-style license that can be found in the LICENSE file. @TestOn('vm') - import 'dart:io' as io; -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; void main() { group('new Context()', () { @@ -57,7 +56,11 @@ void main() { } finally { io.Directory.current = dir; } - }); + }, + //TODO(kevmoo): figure out why this is failing on windows/mac and fix! + skip: (io.Platform.isWindows || io.Platform.isMacOS) + ? 'Untriaged failure on Mac and Windows' + : null); }); test('registers changes to the working directory', () { @@ -96,5 +99,7 @@ void main() { } finally { io.Directory.current = dir; } - }); + }, + //TODO(kevmoo): figure out why this is failing on windows and fix! + skip: io.Platform.isWindows ? 'Untriaged failure on Windows' : null); } From 3bd6f0d89ecdbf17692dee8061af71b5d5b7c405 Mon Sep 17 00:00:00 2001 From: Ayan Banerjee Date: Tue, 7 Apr 2020 04:47:07 +0530 Subject: [PATCH 118/183] Add support for multiple extensions (dart-lang/path#69) Closes dart-lang/path#13 --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/lib/path.dart | 13 ++++++++++- pkgs/path/lib/src/context.dart | 13 ++++++++++- pkgs/path/lib/src/parsed_path.dart | 35 +++++++++++++++++++++++++++--- pkgs/path/test/posix_test.dart | 8 +++++++ pkgs/path/test/windows_test.dart | 9 ++++++++ 6 files changed, 77 insertions(+), 5 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 0804f020..7564f60e 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/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/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index a501e0c5..1cecf665 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -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 diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 9e58e4ad..3a998047 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -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 diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 9efd5299..a9d443ac 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -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; @@ -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 _splitExtension() { + List _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/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index bd6c3e02..51380866 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -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', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 2d6b90f9..5710f06c 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -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', () { From 0546ed63bcd5bc06045de8b172275474ad76041d Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 6 Apr 2020 16:34:22 -0700 Subject: [PATCH 119/183] Prepare to publish (dart-lang/path#72) Tweak a doc comment, bump version in pubspec. --- pkgs/path/lib/src/parsed_path.dart | 6 +++--- pkgs/path/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index a9d443ac..dfef4bdc 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -179,13 +179,13 @@ class ParsedPath { return leftMostIndexedCharacter; } - /// Splits the last non-empty part of the path into a `[basename, extension`] + /// 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. + /// number of dots, the path is split at the first 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. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index c2f69280..6e1cd54e 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.6.5-dev +version: 1.7.0 description: >- A string-based path manipulation library. All of the path operations you know From 5c479bd899f9dcc00d1f52a7e473e2fe6db1cc6e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 6 Apr 2020 17:12:36 -0700 Subject: [PATCH 120/183] Add a UNC examples to rootPrefix and split docs (dart-lang/path#73) Resolves some old TODOs. The related issue is closed: https://github.com/dart-lang/sdk/issues/7323 The change that closed the issues is https://codereview.chromium.org/59133009 Add examples based on the tests added in that change. Remove another TODO referencing the issue which I think is resolved. --- pkgs/path/lib/path.dart | 5 +++-- pkgs/path/lib/src/context.dart | 5 +++-- pkgs/path/lib/src/style.dart | 2 -- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 1cecf665..940748d9 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -207,7 +207,6 @@ String dirname(String path) => context.dirname(path); 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 /// relative. /// @@ -218,6 +217,7 @@ String extension(String path, [int level = 1]) => /// // Windows /// p.rootPrefix(r'path\to\foo'); // -> '' /// p.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' +/// p.rootPrefix(r'\\server\share\a\b'); // -> r'\\server\share' /// /// // URL /// p.rootPrefix('path/to/foo'); // -> '' @@ -295,7 +295,6 @@ String join(String part1, /// For a fixed number of parts, [join] is usually terser. String joinAll(Iterable parts) => context.joinAll(parts); -// TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. /// /// p.split('path/to/foo'); // -> ['path', 'to', 'foo'] @@ -312,6 +311,8 @@ String joinAll(Iterable parts) => context.joinAll(parts); /// /// // Windows /// p.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] +/// p.split(r'\\server\share\path\to\foo'); +/// // -> [r'\\server\share', 'foo', 'bar', 'baz'] /// /// // Browser /// p.split('http://dartlang.org/path/to/foo'); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 3a998047..10a9688f 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -158,7 +158,6 @@ class Context { 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 /// relative. /// @@ -169,6 +168,7 @@ class Context { /// // Windows /// context.rootPrefix(r'path\to\foo'); // -> '' /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// context.rootPrefix(r'\\server\share\a\b'); // -> r'\\server\share' /// /// // URL /// context.rootPrefix('path/to/foo'); // -> '' @@ -296,7 +296,6 @@ class Context { return buffer.toString(); } - // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's /// [separator]. Example: /// @@ -314,6 +313,8 @@ class Context { /// /// // Windows /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + /// context.split(r'\\server\share\path\to\foo'); + /// // -> [r'\\server\share', 'foo', 'bar', 'baz'] List split(String path) { final parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 409394c9..74d896d3 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -16,8 +16,6 @@ abstract class Style { /// Windows paths use `\` (backslash) as separators. Absolute paths start with /// a drive letter followed by a colon (example, `C:`) or two backslashes /// (`\\`) for UNC paths. - // TODO(rnystrom): The UNC root prefix should include the drive name too, not - // just the `\\`. static final Style windows = WindowsStyle(); /// URLs aren't filesystem paths, but they're supported to make it easier to From a3f6c05fc611b1e76a1fad08c52b2d9716c3821f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 6 Apr 2020 17:26:43 -0700 Subject: [PATCH 121/183] Use dart.dev everywhere (dart-lang/path#74) Drop section on installing from library level doc comment - it isn't useful enough there, and it had some outdated links. Replace `http://dartlang.org` with `https://dart.dev` everywhere. Add a browser example to the doc comment for `context.split` for consistency with the comment on `p.split`. Switch some doc comments to use single quotes and avoid `new`. --- pkgs/path/README.md | 2 +- pkgs/path/lib/path.dart | 53 ++--- pkgs/path/lib/src/context.dart | 36 ++-- pkgs/path/lib/src/style.dart | 2 +- pkgs/path/test/posix_test.dart | 9 +- pkgs/path/test/url_test.dart | 326 ++++++++++++++----------------- pkgs/path/test/windows_test.dart | 5 +- 7 files changed, 198 insertions(+), 235 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index a767500c..b4ac3e29 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -36,7 +36,7 @@ underlying platform that the program is running on, you can create a [Context] and give it an explicit [Style]: ```dart -var context = new p.Context(style: Style.windows); +var context = p.Context(style: Style.windows); context.join('directory', 'file.txt'); ``` diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 940748d9..d4f194bc 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -4,23 +4,6 @@ /// A comprehensive, cross-platform path manipulation library. /// -/// ## Installing ## -/// -/// Use [pub][] to install this package. Add the following to your -/// `pubspec.yaml` file. -/// -/// dependencies: -/// path: any -/// -/// Then run `pub install`. -/// -/// For more information, see the [path package on pub.dartlang.org][pkg]. -/// -/// [pub]: http://pub.dartlang.org -/// [pkg]: http://pub.dartlang.org/packages/path -/// -/// ## Usage ## -/// /// The path library was designed to be imported with a prefix, though you don't /// have to if you don't want to: /// @@ -30,7 +13,7 @@ /// These manipulate path strings based on your current working directory and /// the path style (POSIX, Windows, or URLs) of the host platform. For example: /// -/// p.join("directory", "file.txt"); +/// p.join('directory', 'file.txt'); /// /// This calls the top-level [join] function to join "directory" and "file.txt" /// using the current platform's directory separator. @@ -39,8 +22,8 @@ /// underlying platform that the program is running on, you can create a /// [Context] and give it an explicit [Style]: /// -/// var context = new p.Context(style: Style.windows); -/// context.join("directory", "file.txt"); +/// var context = p.Context(style: Style.windows); +/// context.join('directory', 'file.txt'); /// /// This will join "directory" and "file.txt" using the Windows path separator, /// even when the program is run on a POSIX machine. @@ -221,8 +204,8 @@ String extension(String path, [int level = 1]) => /// /// // URL /// p.rootPrefix('path/to/foo'); // -> '' -/// p.rootPrefix('http://dartlang.org/path/to/foo'); -/// // -> 'http://dartlang.org' +/// p.rootPrefix('https://dart.dev/path/to/foo'); +/// // -> 'https://dart.dev' String rootPrefix(String path) => context.rootPrefix(path); /// Returns `true` if [path] is an absolute path and `false` if it is a @@ -231,7 +214,7 @@ String rootPrefix(String path) => context.rootPrefix(path); /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and -/// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. +/// optional hostname (e.g. `https://dart.dev`, `file://`) or with a `/`. /// /// URLs that start with `/` are known as "root-relative", since they're /// relative to the root of the current URL. Since root-relative paths are still @@ -315,8 +298,8 @@ String joinAll(Iterable parts) => context.joinAll(parts); /// // -> [r'\\server\share', 'foo', 'bar', 'baz'] /// /// // Browser -/// p.split('http://dartlang.org/path/to/foo'); -/// // -> ['http://dartlang.org', 'path', 'to', 'foo'] +/// p.split('https://dart.dev/path/to/foo'); +/// // -> ['https://dart.dev', 'path', 'to', 'foo'] List split(String path) => context.split(path); /// Canonicalizes [path]. @@ -367,8 +350,8 @@ String normalize(String path) => context.normalize(path); /// p.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other' /// /// // URL -/// p.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); -/// // -> 'http://dartlang.org' +/// p.relative('https://dart.dev', from: 'https://pub.dev'); +/// // -> 'https://dart.dev' String relative(String path, {String from}) => context.relative(path, from: from); @@ -422,8 +405,8 @@ String setExtension(String path, String extension) => /// p.fromUri('file:///C:/path/to/foo') // -> r'C:\path\to\foo' /// /// // URL -/// p.fromUri('http://dartlang.org/path/to/foo') -/// // -> 'http://dartlang.org/path/to/foo' +/// p.fromUri('https://dart.dev/path/to/foo') +/// // -> 'https://dart.dev/path/to/foo' /// /// If [uri] is relative, a relative path will be returned. /// @@ -444,8 +427,8 @@ String fromUri(uri) => context.fromUri(uri); /// // -> Uri.parse('file:///C:/path/to/foo') /// /// // URL -/// p.toUri('http://dartlang.org/path/to/foo') -/// // -> Uri.parse('http://dartlang.org/path/to/foo') +/// p.toUri('https://dart.dev/path/to/foo') +/// // -> Uri.parse('https://dart.dev/path/to/foo') /// /// If [path] is relative, a relative URI will be returned. /// @@ -463,13 +446,13 @@ Uri toUri(String path) => context.toUri(path); /// /// // POSIX at "/root/path" /// p.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' -/// p.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// p.prettyUri('https://dart.dev/'); // -> 'https://dart.dev' /// /// // Windows at "C:\root\path" /// p.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' -/// p.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' +/// p.prettyUri('https://dart.dev/'); // -> 'https://dart.dev' /// -/// // URL at "http://dartlang.org/root/path" -/// p.prettyUri('http://dartlang.org/root/path/a/b.dart'); // -> r'a/b.dart' +/// // URL at "https://dart.dev/root/path" +/// p.prettyUri('https://dart.dev/root/path/a/b.dart'); // -> r'a/b.dart' /// p.prettyUri('file:///root/path'); // -> 'file:///root/path' String prettyUri(uri) => context.prettyUri(uri); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 10a9688f..df1c15d9 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -68,7 +68,7 @@ class Context { /// Creates a new path by appending the given path parts to [current]. /// Equivalent to [join()] with [current] as the first argument. Example: /// - /// var context = new Context(current: '/root'); + /// var context = Context(current: '/root'); /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' /// /// If [current] isn't absolute, this won't return an absolute path. @@ -172,8 +172,8 @@ class Context { /// /// // URL /// context.rootPrefix('path/to/foo'); // -> '' - /// context.rootPrefix('http://dartlang.org/path/to/foo'); - /// // -> 'http://dartlang.org' + /// context.rootPrefix('https://dart.dev/path/to/foo'); + /// // -> 'https://dart.dev' String rootPrefix(String path) => path.substring(0, style.rootLength(path)); /// Returns `true` if [path] is an absolute path and `false` if it is a @@ -182,7 +182,7 @@ class Context { /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and - /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// optional hostname (e.g. `https://dart.dev`, `file://`) or with a `/`. /// /// URLs that start with `/` are known as "root-relative", since they're /// relative to the root of the current URL. Since root-relative paths are @@ -315,6 +315,10 @@ class Context { /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] /// context.split(r'\\server\share\path\to\foo'); /// // -> [r'\\server\share', 'foo', 'bar', 'baz'] + /// + /// // Browser + /// context.split('https://dart.dev/path/to/foo'); + /// // -> ['https://dart.dev', 'path', 'to', 'foo'] List split(String path) { final parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. @@ -429,7 +433,7 @@ class Context { /// Attempts to convert [path] to an equivalent relative path relative to /// [current]. /// - /// var context = new Context(current: '/root/path'); + /// var context = Context(current: '/root/path'); /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' /// context.relative('/root/other.dart'); // -> '../other.dart' /// @@ -451,7 +455,7 @@ class Context { /// This will also return an absolute path if an absolute [path] is passed to /// a context with a relative path for [current]. /// - /// var context = new Context(r'some/relative/path'); + /// var context = Context(r'some/relative/path'); /// context.relative(r'/absolute/path'); // -> '/absolute/path' /// /// If [current] is relative, it may be impossible to determine a path from @@ -986,8 +990,8 @@ class Context { /// // -> r'C:\path\to\foo' /// /// // URL - /// context.fromUri('http://dartlang.org/path/to/foo') - /// // -> 'http://dartlang.org/path/to/foo' + /// context.fromUri('https://dart.dev/path/to/foo') + /// // -> 'https://dart.dev/path/to/foo' /// /// If [uri] is relative, a relative path will be returned. /// @@ -1008,8 +1012,8 @@ class Context { /// // -> Uri.parse('file:///C:/path/to/foo') /// /// // URL - /// context.toUri('http://dartlang.org/path/to/foo') - /// // -> Uri.parse('http://dartlang.org/path/to/foo') + /// context.toUri('https://dart.dev/path/to/foo') + /// // -> Uri.parse('https://dart.dev/path/to/foo') Uri toUri(String path) { if (isRelative(path)) { return style.relativePathToUri(path); @@ -1029,18 +1033,18 @@ class Context { /// or path-formatted. /// /// // POSIX - /// var context = new Context(current: '/root/path'); + /// var context = Context(current: '/root/path'); /// context.prettyUri('file:///root/path/a/b.dart'); // -> 'a/b.dart' - /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// context.prettyUri('https://dart.dev/'); // -> 'https://dart.dev' /// /// // Windows - /// var context = new Context(current: r'C:\root\path'); + /// var context = Context(current: r'C:\root\path'); /// context.prettyUri('file:///C:/root/path/a/b.dart'); // -> r'a\b.dart' - /// context.prettyUri('http://dartlang.org/'); // -> 'http://dartlang.org' + /// context.prettyUri('https://dart.dev/'); // -> 'https://dart.dev' /// /// // URL - /// var context = new Context(current: 'http://dartlang.org/root/path'); - /// context.prettyUri('http://dartlang.org/root/path/a/b.dart'); + /// var context = Context(current: 'https://dart.dev/root/path'); + /// context.prettyUri('https://dart.dev/root/path/a/b.dart'); /// // -> r'a/b.dart' /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' String prettyUri(uri) { diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index 74d896d3..d8b8ff17 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -22,7 +22,7 @@ abstract class Style { /// manipulate URL paths in the browser. /// /// URLs use "/" (forward slash) as separators. Absolute paths either start - /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// with a protocol and optional hostname (e.g. `https://dart.dev`, /// `file://`) or with "/". static final Style url = UrlStyle(); diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 51380866..6b731457 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -297,8 +297,8 @@ void main() { test('does not walk before root on absolute paths', () { expect(context.normalize('..'), '..'); expect(context.normalize('../'), '..'); - expect(context.normalize('http://dartlang.org/..'), 'http:'); - expect(context.normalize('http://dartlang.org/../../a'), 'a'); + expect(context.normalize('https://dart.dev/..'), 'https:'); + expect(context.normalize('https://dart.dev/../../a'), 'a'); expect(context.normalize('file:///..'), '.'); expect(context.normalize('file:///../../a'), '../a'); expect(context.normalize('/..'), '/'); @@ -572,7 +572,7 @@ void main() { '/path/to/foo#bar'); expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); - expect(() => context.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('https://dart.dev')), throwsArgumentError); }); @@ -604,8 +604,7 @@ void main() { }); test('with an http: URI', () { - expect(context.prettyUri('http://dartlang.org/a/b'), - 'http://dartlang.org/a/b'); + expect(context.prettyUri('https://dart.dev/a/b'), 'https://dart.dev/a/b'); }); test('with a relative URI', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index ebb45328..143b6024 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -9,7 +9,7 @@ import 'utils.dart'; void main() { final context = path.Context( - style: path.Style.url, current: 'http://dartlang.org/root/path'); + style: path.Style.url, current: 'https://dart.dev/root/path'); test('separator', () { expect(context.separator, '/'); @@ -30,13 +30,12 @@ void main() { expect(context.rootPrefix(''), ''); expect(context.rootPrefix('a'), ''); expect(context.rootPrefix('a/b'), ''); - expect( - context.rootPrefix('http://dartlang.org/a/c'), 'http://dartlang.org'); + expect(context.rootPrefix('https://dart.dev/a/c'), 'https://dart.dev'); expect(context.rootPrefix('file:///a/c'), 'file://'); expect(context.rootPrefix('/a/c'), '/'); - expect(context.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.rootPrefix('https://dart.dev/'), 'https://dart.dev'); expect(context.rootPrefix('file:///'), 'file://'); - expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); + expect(context.rootPrefix('https://dart.dev'), 'https://dart.dev'); expect(context.rootPrefix('file://'), 'file://'); expect(context.rootPrefix('/'), '/'); expect(context.rootPrefix('foo/bar://'), ''); @@ -53,18 +52,18 @@ void main() { expect(context.dirname('a/'), '.'); expect(context.dirname('a/.'), 'a'); expect(context.dirname(r'a\b/c'), r'a\b'); - expect(context.dirname('http://dartlang.org/a'), 'http://dartlang.org'); + expect(context.dirname('https://dart.dev/a'), 'https://dart.dev'); expect(context.dirname('file:///a'), 'file://'); expect(context.dirname('/a'), '/'); - expect(context.dirname('http://dartlang.org///a'), 'http://dartlang.org'); + expect(context.dirname('https://dart.dev///a'), 'https://dart.dev'); expect(context.dirname('file://///a'), 'file://'); expect(context.dirname('///a'), '/'); - expect(context.dirname('http://dartlang.org/'), 'http://dartlang.org'); - expect(context.dirname('http://dartlang.org'), 'http://dartlang.org'); + expect(context.dirname('https://dart.dev/'), 'https://dart.dev'); + expect(context.dirname('https://dart.dev'), 'https://dart.dev'); expect(context.dirname('file:///'), 'file://'); expect(context.dirname('file://'), 'file://'); expect(context.dirname('/'), '/'); - expect(context.dirname('http://dartlang.org///'), 'http://dartlang.org'); + expect(context.dirname('https://dart.dev///'), 'https://dart.dev'); expect(context.dirname('file://///'), 'file://'); expect(context.dirname('///'), '/'); expect(context.dirname('a/b/'), 'a'); @@ -83,11 +82,11 @@ void main() { expect(context.basename('a/'), 'a'); expect(context.basename('a/.'), '.'); expect(context.basename(r'a\b/c'), 'c'); - expect(context.basename('http://dartlang.org/a'), 'a'); + expect(context.basename('https://dart.dev/a'), 'a'); expect(context.basename('file:///a'), 'a'); expect(context.basename('/a'), 'a'); - expect(context.basename('http://dartlang.org/'), 'http://dartlang.org'); - expect(context.basename('http://dartlang.org'), 'http://dartlang.org'); + expect(context.basename('https://dart.dev/'), 'https://dart.dev'); + expect(context.basename('https://dart.dev'), 'https://dart.dev'); expect(context.basename('file:///'), 'file://'); expect(context.basename('file://'), 'file://'); expect(context.basename('/'), '/'); @@ -124,15 +123,15 @@ void main() { expect(context.isAbsolute(''), false); expect(context.isAbsolute('a'), false); expect(context.isAbsolute('a/b'), false); - expect(context.isAbsolute('http://dartlang.org/a'), true); + expect(context.isAbsolute('https://dart.dev/a'), true); expect(context.isAbsolute('file:///a'), true); expect(context.isAbsolute('/a'), true); - expect(context.isAbsolute('http://dartlang.org/a/b'), true); + expect(context.isAbsolute('https://dart.dev/a/b'), true); expect(context.isAbsolute('file:///a/b'), true); expect(context.isAbsolute('/a/b'), true); - expect(context.isAbsolute('http://dartlang.org/'), true); + expect(context.isAbsolute('https://dart.dev/'), true); expect(context.isAbsolute('file:///'), true); - expect(context.isAbsolute('http://dartlang.org'), true); + expect(context.isAbsolute('https://dart.dev'), true); expect(context.isAbsolute('file://'), true); expect(context.isAbsolute('/'), true); expect(context.isAbsolute('~'), false); @@ -149,15 +148,15 @@ void main() { expect(context.isRelative(''), true); expect(context.isRelative('a'), true); expect(context.isRelative('a/b'), true); - expect(context.isRelative('http://dartlang.org/a'), false); + expect(context.isRelative('https://dart.dev/a'), false); expect(context.isRelative('file:///a'), false); expect(context.isRelative('/a'), false); - expect(context.isRelative('http://dartlang.org/a/b'), false); + expect(context.isRelative('https://dart.dev/a/b'), false); expect(context.isRelative('file:///a/b'), false); expect(context.isRelative('/a/b'), false); - expect(context.isRelative('http://dartlang.org/'), false); + expect(context.isRelative('https://dart.dev/'), false); expect(context.isRelative('file:///'), false); - expect(context.isRelative('http://dartlang.org'), false); + expect(context.isRelative('https://dart.dev'), false); expect(context.isRelative('file://'), false); expect(context.isRelative('/'), false); expect(context.isRelative('~'), true); @@ -174,15 +173,15 @@ void main() { expect(context.isRootRelative(''), false); expect(context.isRootRelative('a'), false); expect(context.isRootRelative('a/b'), false); - expect(context.isRootRelative('http://dartlang.org/a'), false); + expect(context.isRootRelative('https://dart.dev/a'), false); expect(context.isRootRelative('file:///a'), false); expect(context.isRootRelative('/a'), true); - expect(context.isRootRelative('http://dartlang.org/a/b'), false); + expect(context.isRootRelative('https://dart.dev/a/b'), false); expect(context.isRootRelative('file:///a/b'), false); expect(context.isRootRelative('/a/b'), true); - expect(context.isRootRelative('http://dartlang.org/'), false); + expect(context.isRootRelative('https://dart.dev/'), false); expect(context.isRootRelative('file:///'), false); - expect(context.isRootRelative('http://dartlang.org'), false); + expect(context.isRootRelative('https://dart.dev'), false); expect(context.isRootRelative('file://'), false); expect(context.isRootRelative('/'), true); expect(context.isRootRelative('~'), false); @@ -214,16 +213,15 @@ void main() { }); test('ignores parts before an absolute path', () { - expect(context.join('a', 'http://dartlang.org', 'b', 'c'), - 'http://dartlang.org/b/c'); + expect(context.join('a', 'https://dart.dev', 'b', 'c'), + 'https://dart.dev/b/c'); expect(context.join('a', 'file://', 'b', 'c'), 'file:///b/c'); expect(context.join('a', '/', 'b', 'c'), '/b/c'); - expect(context.join('a', '/b', 'http://dartlang.org/c', 'd'), - 'http://dartlang.org/c/d'); + expect(context.join('a', '/b', 'https://dart.dev/c', 'd'), + 'https://dart.dev/c/d'); expect( - context.join( - 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), - 'http://dartlang.org/c/d'); + context.join('a', 'http://google.com/b', 'https://dart.dev/c', 'd'), + 'https://dart.dev/c/d'); expect(context.join('a', '/b', '/c', 'd'), '/c/d'); expect(context.join('a', r'c:\b', 'c', 'd'), r'c:\b/c/d'); expect(context.join('a', 'package:foo/bar', 'c', 'd'), @@ -232,8 +230,8 @@ void main() { }); test('preserves roots before a root-relative path', () { - expect(context.join('http://dartlang.org', 'a', '/b', 'c'), - 'http://dartlang.org/b/c'); + expect(context.join('https://dart.dev', 'a', '/b', 'c'), + 'https://dart.dev/b/c'); expect(context.join('file://', 'a', '/b', 'c'), 'file:///b/c'); expect(context.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); expect(context.join('package:foo/bar.dart', '/baz.dart'), @@ -294,16 +292,16 @@ void main() { }); test('ignores parts before an absolute path', () { - expect(context.joinAll(['a', 'http://dartlang.org', 'b', 'c']), - 'http://dartlang.org/b/c'); + expect(context.joinAll(['a', 'https://dart.dev', 'b', 'c']), + 'https://dart.dev/b/c'); expect(context.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); - expect(context.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), - 'http://dartlang.org/c/d'); + expect(context.joinAll(['a', '/b', 'https://dart.dev/c', 'd']), + 'https://dart.dev/c/d'); expect( - context.joinAll( - ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), - 'http://dartlang.org/c/d'); + context + .joinAll(['a', 'http://google.com/b', 'https://dart.dev/c', 'd']), + 'https://dart.dev/c/d'); expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b/c/d'); expect(context.joinAll(['a', 'package:foo/bar', 'c', 'd']), @@ -312,8 +310,8 @@ void main() { }); test('preserves roots before a root-relative path', () { - expect(context.joinAll(['http://dartlang.org', 'a', '/b', 'c']), - 'http://dartlang.org/b/c'); + expect(context.joinAll(['https://dart.dev', 'a', '/b', 'c']), + 'https://dart.dev/b/c'); expect(context.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); expect(context.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); }); @@ -334,22 +332,19 @@ void main() { expect(context.split('.'), equals(['.'])); expect(context.split(''), equals([])); expect(context.split('foo/'), equals(['foo'])); - expect(context.split('http://dartlang.org//'), - equals(['http://dartlang.org'])); + expect(context.split('https://dart.dev//'), equals(['https://dart.dev'])); expect(context.split('file:////'), equals(['file://'])); expect(context.split('//'), equals(['/'])); }); test('includes the root for absolute paths', () { - expect(context.split('http://dartlang.org/foo/bar/baz'), - equals(['http://dartlang.org', 'foo', 'bar', 'baz'])); + expect(context.split('https://dart.dev/foo/bar/baz'), + equals(['https://dart.dev', 'foo', 'bar', 'baz'])); expect(context.split('file:///foo/bar/baz'), equals(['file://', 'foo', 'bar', 'baz'])); expect(context.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); - expect(context.split('http://dartlang.org/'), - equals(['http://dartlang.org'])); - expect(context.split('http://dartlang.org'), - equals(['http://dartlang.org'])); + expect(context.split('https://dart.dev/'), equals(['https://dart.dev'])); + expect(context.split('https://dart.dev'), equals(['https://dart.dev'])); expect(context.split('file:///'), equals(['file://'])); expect(context.split('file://'), equals(['file://'])); expect(context.split('/'), equals(['/'])); @@ -362,8 +357,8 @@ void main() { expect(context.normalize('.'), '.'); expect(context.normalize('..'), '..'); expect(context.normalize('a'), 'a'); - expect(context.normalize('http://dartlang.org/'), 'http://dartlang.org'); - expect(context.normalize('http://dartlang.org'), 'http://dartlang.org'); + expect(context.normalize('https://dart.dev/'), 'https://dart.dev'); + expect(context.normalize('https://dart.dev'), 'https://dart.dev'); expect(context.normalize('file://'), 'file://'); expect(context.normalize('file:///'), 'file://'); expect(context.normalize('/'), '/'); @@ -386,11 +381,10 @@ void main() { test('eliminates "." parts', () { expect(context.normalize('./'), '.'); - expect(context.normalize('http://dartlang.org/.'), 'http://dartlang.org'); + expect(context.normalize('https://dart.dev/.'), 'https://dart.dev'); expect(context.normalize('file:///.'), 'file://'); expect(context.normalize('/.'), '/'); - expect( - context.normalize('http://dartlang.org/./'), 'http://dartlang.org'); + expect(context.normalize('https://dart.dev/./'), 'https://dart.dev'); expect(context.normalize('file:///./'), 'file://'); expect(context.normalize('/./'), '/'); expect(context.normalize('./.'), '.'); @@ -406,16 +400,15 @@ void main() { expect(context.normalize('../'), '..'); expect(context.normalize('../../..'), '../../..'); expect(context.normalize('../../../'), '../../..'); - expect( - context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); + expect(context.normalize('https://dart.dev/..'), 'https://dart.dev'); expect(context.normalize('file:///..'), 'file://'); expect(context.normalize('/..'), '/'); - expect(context.normalize('http://dartlang.org/../../..'), - 'http://dartlang.org'); + expect( + context.normalize('https://dart.dev/../../..'), 'https://dart.dev'); expect(context.normalize('file:///../../..'), 'file://'); expect(context.normalize('/../../..'), '/'); - expect(context.normalize('http://dartlang.org/../../../a'), - 'http://dartlang.org/a'); + expect(context.normalize('https://dart.dev/../../../a'), + 'https://dart.dev/a'); expect(context.normalize('file:///../../../a'), 'file:///a'); expect(context.normalize('/../../../a'), '/a'); expect(context.normalize('c:/..'), 'c:'); @@ -434,10 +427,8 @@ void main() { test('does not walk before root on absolute paths', () { expect(context.normalize('..'), '..'); expect(context.normalize('../'), '..'); - expect( - context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); - expect(context.normalize('http://dartlang.org/../a'), - 'http://dartlang.org/a'); + expect(context.normalize('https://dart.dev/..'), 'https://dart.dev'); + expect(context.normalize('https://dart.dev/../a'), 'https://dart.dev/a'); expect(context.normalize('file:///..'), 'file://'); expect(context.normalize('file:///../a'), 'file:///a'); expect(context.normalize('/..'), '/'); @@ -466,11 +457,11 @@ void main() { }); test('when canonicalizing', () { - expect(context.canonicalize('.'), 'http://dartlang.org/root/path'); + expect(context.canonicalize('.'), 'https://dart.dev/root/path'); expect(context.canonicalize('foo/bar'), - 'http://dartlang.org/root/path/foo/bar'); - expect(context.canonicalize('FoO'), 'http://dartlang.org/root/path/FoO'); - expect(context.canonicalize('/foo'), 'http://dartlang.org/foo'); + 'https://dart.dev/root/path/foo/bar'); + expect(context.canonicalize('FoO'), 'https://dart.dev/root/path/FoO'); + expect(context.canonicalize('/foo'), 'https://dart.dev/foo'); expect(context.canonicalize('http://google.com/foo'), 'http://google.com/foo'); }); @@ -479,34 +470,32 @@ void main() { group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(context.relative('http://dartlang.org'), '../..'); - expect(context.relative('http://dartlang.org/'), '../..'); + expect(context.relative('https://dart.dev'), '../..'); + expect(context.relative('https://dart.dev/'), '../..'); expect(context.relative('/'), '../..'); - expect(context.relative('http://dartlang.org/root'), '..'); + expect(context.relative('https://dart.dev/root'), '..'); expect(context.relative('/root'), '..'); - expect(context.relative('http://dartlang.org/root/path'), '.'); + expect(context.relative('https://dart.dev/root/path'), '.'); expect(context.relative('/root/path'), '.'); - expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('https://dart.dev/root/path/a'), 'a'); expect(context.relative('/root/path/a'), 'a'); - expect(context.relative('http://dartlang.org/root/path/a/b.txt'), - 'a/b.txt'); - expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); expect( - context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); + context.relative('https://dart.dev/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('https://dart.dev/root/a/b.txt'), '../a/b.txt'); expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path outside of root', () { - expect(context.relative('http://dartlang.org/a/b'), '../../a/b'); + expect(context.relative('https://dart.dev/a/b'), '../../a/b'); expect(context.relative('/a/b'), '../../a/b'); - expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('https://dart.dev/root/path/a'), 'a'); expect(context.relative('/root/path/a'), 'a'); - expect(context.relative('http://dartlang.org/root/path/a/b.txt'), - 'a/b.txt'); - expect(context.relative('http://dartlang.org/root/path/a/b.txt'), - 'a/b.txt'); expect( - context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); + context.relative('https://dart.dev/root/path/a/b.txt'), 'a/b.txt'); + expect( + context.relative('https://dart.dev/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('https://dart.dev/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path with different hostname/protocol', () { @@ -527,23 +516,21 @@ void main() { }); test('is case-sensitive', () { - expect(context.relative('HtTp://dartlang.org/root'), - 'HtTp://dartlang.org/root'); - expect(context.relative('http://DaRtLaNg.OrG/root'), - 'http://DaRtLaNg.OrG/root'); + expect( + context.relative('HtTps://dart.dev/root'), 'HtTps://dart.dev/root'); + expect( + context.relative('https://DaRt.DeV/root'), 'https://DaRt.DeV/root'); expect(context.relative('/RoOt'), '../../RoOt'); expect(context.relative('/rOoT/pAtH/a'), '../../rOoT/pAtH/a'); }); // Regression test('from root-only path', () { - expect( - context.relative('http://dartlang.org', - from: 'http://dartlang.org'), + expect(context.relative('https://dart.dev', from: 'https://dart.dev'), '.'); expect( - context.relative('http://dartlang.org/root/path', - from: 'http://dartlang.org'), + context.relative('https://dart.dev/root/path', + from: 'https://dart.dev'), 'root/path'); }); }); @@ -605,34 +592,31 @@ void main() { test('with a root parameter', () { expect(context.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect( - context.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + expect(context.relative('/foo/bar/baz', from: 'https://dart.dev/foo/bar'), equals('baz')); - expect( - context.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + expect(context.relative('https://dart.dev/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect( - context.relative('http://dartlang.org/foo/bar/baz', + context.relative('https://dart.dev/foo/bar/baz', from: 'file:///foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + equals('https://dart.dev/foo/bar/baz')); expect( - context.relative('http://dartlang.org/foo/bar/baz', - from: 'http://dartlang.org/foo/bar'), + context.relative('https://dart.dev/foo/bar/baz', + from: 'https://dart.dev/foo/bar'), equals('baz')); expect(context.relative('/foo/bar/baz', from: 'file:///foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + equals('https://dart.dev/foo/bar/baz')); expect(context.relative('file:///foo/bar/baz', from: '/foo/bar'), equals('file:///foo/bar/baz')); expect(context.relative('..', from: '/foo/bar'), equals('../../root')); - expect(context.relative('..', from: 'http://dartlang.org/foo/bar'), + expect(context.relative('..', from: 'https://dart.dev/foo/bar'), equals('../../root')); expect(context.relative('..', from: 'file:///foo/bar'), - equals('http://dartlang.org/root')); + equals('https://dart.dev/root')); expect(context.relative('..', from: '/foo/bar'), equals('../../root')); - expect( - context.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), + expect(context.relative('https://dart.dev/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); expect(context.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); @@ -645,21 +629,20 @@ void main() { test('with a root parameter and a relative root', () { final r = path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + expect(r.relative('/foo/bar/baz', from: 'https://dart.dev/foo/bar'), equals('/foo/bar/baz')); - expect(r.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('https://dart.dev/foo/bar/baz', from: '/foo/bar'), + equals('https://dart.dev/foo/bar/baz')); expect( - r.relative('http://dartlang.org/foo/bar/baz', - from: 'file:///foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + r.relative('https://dart.dev/foo/bar/baz', from: 'file:///foo/bar'), + equals('https://dart.dev/foo/bar/baz')); expect( - r.relative('http://dartlang.org/foo/bar/baz', - from: 'http://dartlang.org/foo/bar'), + r.relative('https://dart.dev/foo/bar/baz', + from: 'https://dart.dev/foo/bar'), equals('baz')); - expect(r.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), - equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('https://dart.dev/foo/bar/baz', from: 'foo/bar'), + equals('https://dart.dev/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); expect( @@ -670,8 +653,8 @@ void main() { test('from a . root', () { final r = path.Context(style: path.Style.url, current: '.'); - expect(r.relative('http://dartlang.org/foo/bar/baz'), - equals('http://dartlang.org/foo/bar/baz')); + expect(r.relative('https://dart.dev/foo/bar/baz'), + equals('https://dart.dev/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); @@ -684,20 +667,17 @@ void main() { expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect( - context.isWithin( - 'http://dartlang.org', 'http://dartlang.org/foo/bar'), + expect(context.isWithin('https://dart.dev', 'https://dart.dev/foo/bar'), isTrue); expect( - context.isWithin( - 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), + context.isWithin('https://dart.dev', 'http://psub.dart.dev/foo/bar'), isFalse); - expect(context.isWithin('http://dartlang.org', '/foo/bar'), isTrue); - expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); - expect(context.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); - expect(context.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), + expect(context.isWithin('https://dart.dev', '/foo/bar'), isTrue); + expect(context.isWithin('https://dart.dev/foo', '/foo/bar'), isTrue); + expect(context.isWithin('https://dart.dev/foo', '/bar/baz'), isFalse); + expect(context.isWithin('baz', 'https://dart.dev/root/path/baz/bang'), isTrue); - expect(context.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), + expect(context.isWithin('baz', 'https://dart.dev/root/path/bang/baz'), isFalse); }); @@ -714,13 +694,13 @@ void main() { expect(context.isWithin('foo/bar', 'foo/bar/baz/../qux'), isTrue); expect(context.isWithin('http://example.org/', 'http://example.com/foo'), isFalse); - expect(context.isWithin('http://example.org/', 'http://dartlang.org/foo'), + expect(context.isWithin('http://example.org/', 'https://dart.dev/foo'), isFalse); }); test('with root-relative paths', () { - expect(context.isWithin('/foo', 'http://dartlang.org/foo/bar'), isTrue); - expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); + expect(context.isWithin('/foo', 'https://dart.dev/foo/bar'), isTrue); + expect(context.isWithin('https://dart.dev/foo', '/foo/bar'), isTrue); expect(context.isWithin('/root', 'foo/bar'), isTrue); expect(context.isWithin('foo', '/root/path/foo/bar'), isTrue); expect(context.isWithin('/foo', '/foo/bar'), isTrue); @@ -731,9 +711,9 @@ void main() { expect(r.isWithin('.', 'a/b/c'), isTrue); expect(r.isWithin('.', '../a/b/c'), isFalse); expect(r.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(r.isWithin('http://dartlang.org/', 'http://dartlang.org/baz/bang'), - isTrue); - expect(r.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); + expect( + r.isWithin('https://dart.dev/', 'https://dart.dev/baz/bang'), isTrue); + expect(r.isWithin('.', 'https://dart.dev/baz/bang'), isFalse); }); }); @@ -745,7 +725,7 @@ void main() { expectNotEquals(context, 'foo/bar', 'foo/baz'); expectEquals(context, 'foo/bar', '../path/foo/bar'); expectEquals(context, 'http://google.com', 'http://google.com'); - expectEquals(context, 'http://dartlang.org', '../..'); + expectEquals(context, 'https://dart.dev', '../..'); expectEquals(context, 'baz', '/root/path/baz'); }); @@ -763,11 +743,11 @@ void main() { expectEquals(context, 'foo/bar', 'foo/bar/baz/..'); expectNotEquals(context, 'FoO/bAr', 'foo/bar'); expectEquals(context, 'http://google.com', 'http://google.com/'); - expectEquals(context, 'http://dartlang.org/root', '..'); + expectEquals(context, 'https://dart.dev/root', '..'); }); test('with root-relative paths', () { - expectEquals(context, '/foo', 'http://dartlang.org/foo'); + expectEquals(context, '/foo', 'https://dart.dev/foo'); expectNotEquals(context, '/foo', 'http://google.com/foo'); expectEquals(context, '/root/path/foo/bar', 'foo/bar'); }); @@ -785,33 +765,32 @@ void main() { group('absolute', () { test('allows up to seven parts', () { - expect(context.absolute('a'), 'http://dartlang.org/root/path/a'); - expect(context.absolute('a', 'b'), 'http://dartlang.org/root/path/a/b'); - expect(context.absolute('a', 'b', 'c'), - 'http://dartlang.org/root/path/a/b/c'); + expect(context.absolute('a'), 'https://dart.dev/root/path/a'); + expect(context.absolute('a', 'b'), 'https://dart.dev/root/path/a/b'); + expect( + context.absolute('a', 'b', 'c'), 'https://dart.dev/root/path/a/b/c'); expect(context.absolute('a', 'b', 'c', 'd'), - 'http://dartlang.org/root/path/a/b/c/d'); + 'https://dart.dev/root/path/a/b/c/d'); expect(context.absolute('a', 'b', 'c', 'd', 'e'), - 'http://dartlang.org/root/path/a/b/c/d/e'); + 'https://dart.dev/root/path/a/b/c/d/e'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), - 'http://dartlang.org/root/path/a/b/c/d/e/f'); + 'https://dart.dev/root/path/a/b/c/d/e/f'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), - 'http://dartlang.org/root/path/a/b/c/d/e/f/g'); + 'https://dart.dev/root/path/a/b/c/d/e/f/g'); }); test('does not add separator if a part ends in one', () { expect(context.absolute('a/', 'b', 'c/', 'd'), - 'http://dartlang.org/root/path/a/b/c/d'); - expect( - context.absolute(r'a\', 'b'), r'http://dartlang.org/root/path/a\/b'); + 'https://dart.dev/root/path/a/b/c/d'); + expect(context.absolute(r'a\', 'b'), r'https://dart.dev/root/path/a\/b'); }); test('ignores parts before an absolute path', () { - expect(context.absolute('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); + expect(context.absolute('a', '/b', '/c', 'd'), 'https://dart.dev/c/d'); expect(context.absolute('a', '/b', 'file:///c', 'd'), 'file:///c/d'); expect(context.absolute('a', r'c:\b', 'c', 'd'), r'c:\b/c/d'); expect(context.absolute('a', r'\\b', 'c', 'd'), - r'http://dartlang.org/root/path/a/\\b/c/d'); + r'https://dart.dev/root/path/a/\\b/c/d'); }); }); @@ -855,16 +834,15 @@ void main() { group('fromUri', () { test('with a URI', () { - expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), - 'http://dartlang.org/path/to/foo'); - expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), - 'http://dartlang.org/path/to/foo/'); + expect(context.fromUri(Uri.parse('https://dart.dev/path/to/foo')), + 'https://dart.dev/path/to/foo'); + expect(context.fromUri(Uri.parse('https://dart.dev/path/to/foo/')), + 'https://dart.dev/path/to/foo/'); expect(context.fromUri(Uri.parse('file:///path/to/foo')), 'file:///path/to/foo'); expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect( - context.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), - 'http://dartlang.org/path/to/foo%23bar'); + expect(context.fromUri(Uri.parse('https://dart.dev/path/to/foo%23bar')), + 'https://dart.dev/path/to/foo%23bar'); // Since the resulting "path" is also a URL, special characters should // remain percent-encoded in the result. expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), @@ -872,22 +850,22 @@ void main() { }); test('with a string', () { - expect(context.fromUri('http://dartlang.org/path/to/foo'), - 'http://dartlang.org/path/to/foo'); + expect(context.fromUri('https://dart.dev/path/to/foo'), + 'https://dart.dev/path/to/foo'); }); }); test('toUri', () { - expect(context.toUri('http://dartlang.org/path/to/foo'), - Uri.parse('http://dartlang.org/path/to/foo')); - expect(context.toUri('http://dartlang.org/path/to/foo/'), - Uri.parse('http://dartlang.org/path/to/foo/')); + expect(context.toUri('https://dart.dev/path/to/foo'), + Uri.parse('https://dart.dev/path/to/foo')); + expect(context.toUri('https://dart.dev/path/to/foo/'), + Uri.parse('https://dart.dev/path/to/foo/')); expect(context.toUri('path/to/foo/'), Uri.parse('path/to/foo/')); expect( context.toUri('file:///path/to/foo'), Uri.parse('file:///path/to/foo')); expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); - expect(context.toUri('http://dartlang.org/path/to/foo%23bar'), - Uri.parse('http://dartlang.org/path/to/foo%23bar')); + expect(context.toUri('https://dart.dev/path/to/foo%23bar'), + Uri.parse('https://dart.dev/path/to/foo%23bar')); // Since the input path is also a URI, special characters should already // be percent encoded there too. expect(context.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), @@ -903,13 +881,13 @@ void main() { }); test('with an http: URI', () { - expect(context.prettyUri('http://dartlang.org/root/path/a/b'), 'a/b'); - expect(context.prettyUri('http://dartlang.org/root/path/a/../b'), 'b'); - expect(context.prettyUri('http://dartlang.org/other/path/a/b'), - 'http://dartlang.org/other/path/a/b'); - expect(context.prettyUri('http://pub.dartlang.org/root/path'), - 'http://pub.dartlang.org/root/path'); - expect(context.prettyUri('http://dartlang.org/root/other'), '../other'); + expect(context.prettyUri('https://dart.dev/root/path/a/b'), 'a/b'); + expect(context.prettyUri('https://dart.dev/root/path/a/../b'), 'b'); + expect(context.prettyUri('https://dart.dev/other/path/a/b'), + 'https://dart.dev/other/path/a/b'); + expect(context.prettyUri('http://psub.dart.dev/root/path'), + 'http://psub.dart.dev/root/path'); + expect(context.prettyUri('https://dart.dev/root/other'), '../other'); }); test('with a relative URI', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 5710f06c..ad3deb6e 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -734,7 +734,7 @@ void main() { expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); expect(context.fromUri(Uri.parse('/foo')), r'\foo'); - expect(() => context.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('https://dart.dev')), throwsArgumentError); }); @@ -776,8 +776,7 @@ void main() { }); test('with an http: URI', () { - expect(context.prettyUri('http://dartlang.org/a/b'), - 'http://dartlang.org/a/b'); + expect(context.prettyUri('https://dart.dev/a/b'), 'https://dart.dev/a/b'); }); test('with a relative URI', () { From aac330d9bef2d5878977adf74b3af2b269fd5b34 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 8 Apr 2020 09:11:06 -0700 Subject: [PATCH 122/183] Remove one last reference to pub.dartlang.org --- pkgs/path/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 7564f60e..faa22092 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -121,7 +121,7 @@ ## 1.2.2 * Remove the documentation link from the pubspec so this is linked to - pub.dartlang.org by default. + pub.dev by default. # 1.2.1 From 9451246405b63651023754e9aba8ddbe180ed07f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 8 Apr 2020 14:41:46 -0700 Subject: [PATCH 123/183] Fix a failing test on mac (dart-lang/path#75) `/var` is a symlink to `/private/var`, and when setting `Directory.current` symlinks are resolved. Explicitly resolve symlinks to find the canonical path to the newly created temp directory. --- pkgs/path/test/io_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index fdd152c2..3737e074 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -43,7 +43,7 @@ void main() { final dir = io.Directory.current.path; try { final temp = io.Directory.systemTemp.createTempSync('path_test'); - final tempPath = temp.path; + final tempPath = temp.resolveSymbolicLinksSync(); io.Directory.current = temp; // Call "current" once so that it can be cached. @@ -57,10 +57,8 @@ void main() { io.Directory.current = dir; } }, - //TODO(kevmoo): figure out why this is failing on windows/mac and fix! - skip: (io.Platform.isWindows || io.Platform.isMacOS) - ? 'Untriaged failure on Mac and Windows' - : null); + //TODO: Figure out why this is failing on windows and fix! + skip: io.Platform.isWindows ? 'Untriaged failure on Windows' : false); }); test('registers changes to the working directory', () { From a8bf57ef473b5d7705cafe415dbd53674d2e07f2 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 14 May 2020 10:28:52 -0700 Subject: [PATCH 124/183] Document that absolute does not normalize (dart-lang/path#80) Closes dart-lang/path#79 Add a sentences indicating that an absolute path is not guaranteed to be canonical or normalized. Add a blank line following the header sentence and change the first word to "Returns" to improve doc style. --- pkgs/path/CHANGELOG.md | 2 ++ pkgs/path/lib/path.dart | 5 ++++- pkgs/path/lib/src/context.dart | 6 ++++-- pkgs/path/pubspec.yaml | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index faa22092..5c962e1a 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.7.1-dev + ## 1.7.0 * Add support for multiple extension in `context.extension()`. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index d4f194bc..5249a5eb 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -109,10 +109,13 @@ String _current; /// and `/` on other platforms (including the browser). String get separator => context.separator; -/// Creates a new path by appending the given path parts to [current]. +/// Returns a new path with the given path parts appended to [current]. +/// /// Equivalent to [join()] with [current] as the first argument. Example: /// /// p.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +/// +/// Does not [normalize] or [cananicalize] paths. String absolute(String part1, [String part2, String part3, diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index df1c15d9..077ab600 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -65,13 +65,15 @@ class Context { /// this is `/`. On Windows, it's `\`. String get separator => style.separator; - /// Creates a new path by appending the given path parts to [current]. + /// Returns a new path with the given path parts appended to [current]. + /// /// Equivalent to [join()] with [current] as the first argument. Example: /// /// var context = Context(current: '/root'); /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' /// - /// If [current] isn't absolute, this won't return an absolute path. + /// If [current] isn't absolute, this won't return an absolute path. Does not + /// [normalize] or [cananicalize] paths. String absolute(String part1, [String part2, String part3, diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 6e1cd54e..92a54f9a 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.7.0 +version: 1.7.1-dev description: >- A string-based path manipulation library. All of the path operations you know From d1ef84318088e1df2dec8e50e437ca896a354e6d Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 18 May 2020 13:47:14 -0700 Subject: [PATCH 125/183] Enable and fix comment_references lint (dart-lang/path#81) Fixes typos from dart-lang/path#80 along with other references that were not working in the dart doc. --- pkgs/path/analysis_options.yaml | 1 + pkgs/path/lib/path.dart | 10 +++++----- pkgs/path/lib/src/context.dart | 10 +++++----- pkgs/path/lib/src/internal_style.dart | 2 +- pkgs/path/lib/src/parsed_path.dart | 2 +- pkgs/path/lib/src/path_map.dart | 2 +- pkgs/path/lib/src/path_set.dart | 2 +- pkgs/path/test/utils.dart | 2 +- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 47ffcd49..8a27e126 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -17,6 +17,7 @@ linter: - await_only_futures - camel_case_types - cancel_subscriptions + - comment_references - constant_identifier_names - control_flow_in_finally - directives_ordering diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 5249a5eb..5ce50d66 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -115,7 +115,7 @@ String get separator => context.separator; /// /// p.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' /// -/// Does not [normalize] or [cananicalize] paths. +/// Does not [normalize] or [canonicalize] paths. String absolute(String part1, [String part2, String part3, @@ -314,9 +314,9 @@ List split(String path) => context.split(path); /// /// Note that this does not resolve symlinks. /// -/// If you want a map that uses path keys, it's probably more efficient to -/// pass [equals] and [hash] to [new HashMap] than it is to canonicalize every -/// key. +/// If you want a map that uses path keys, it's probably more efficient to use a +/// Map with [equals] and [hash] specified as the callbacks to use for keys than +/// it is to canonicalize every key. String canonicalize(String path) => context.canonicalize(path); /// Normalizes [path], simplifying it by handling `..`, and `.`, and @@ -324,7 +324,7 @@ String canonicalize(String path) => context.canonicalize(path); /// /// Note that this is *not* guaranteed to return the same result for two /// equivalent input paths. For that, see [canonicalize]. Or, if you're using -/// paths as map keys, pass [equals] and [hash] to [new HashMap]. +/// paths as map keys use [equals] and [hash] as the key callbacks. /// /// p.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) => context.normalize(path); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 077ab600..21a7835e 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -73,7 +73,7 @@ class Context { /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' /// /// If [current] isn't absolute, this won't return an absolute path. Does not - /// [normalize] or [cananicalize] paths. + /// [normalize] or [canonicalize] paths. String absolute(String part1, [String part2, String part3, @@ -338,9 +338,9 @@ class Context { /// /// Note that this does not resolve symlinks. /// - /// If you want a map that uses path keys, it's probably more efficient to - /// pass [equals] and [hash] to [new HashMap] than it is to canonicalize every - /// key. + /// If you want a map that uses path keys, it's probably more efficient to use + /// a Map with [equals] and [hash] specified as the callbacks to use for keys + /// than it is to canonicalize every key. String canonicalize(String path) { path = absolute(path); if (style != Style.windows && !_needsNormalization(path)) return path; @@ -355,7 +355,7 @@ class Context { /// /// Note that this is *not* guaranteed to return the same result for two /// equivalent input paths. For that, see [canonicalize]. Or, if you're using - /// paths as map keys, pass [equals] and [hash] to [new HashMap]. + /// paths as map keys use [equals] and [hash] as the key callbacks. /// /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' String normalize(String path) { diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index b7f6e934..dbf90988 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -58,7 +58,7 @@ abstract class InternalStyle extends Style { @override String pathFromUri(Uri uri); - /// Returns the URI that represents the relative path made of [parts]. + /// Returns the URI that represents a relative path. @override Uri relativePathToUri(String path) { final segments = context.split(path); diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index dfef4bdc..45fb64b3 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -17,7 +17,7 @@ class ParsedPath { /// Whether this path is root-relative. /// - /// See [Context.isRootRelative]. + /// See `Context.isRootRelative`. bool isRootRelative; /// The path-separated parts of the path. All but the last will be diff --git a/pkgs/path/lib/src/path_map.dart b/pkgs/path/lib/src/path_map.dart index 7d560cdd..99bea346 100644 --- a/pkgs/path/lib/src/path_map.dart +++ b/pkgs/path/lib/src/path_map.dart @@ -6,7 +6,7 @@ import 'dart:collection'; import '../path.dart' as p; -/// A map whose keys are paths, compared using [equals] and [hash]. +/// A map whose keys are paths, compared using [p.equals] and [p.hash]. class PathMap extends MapView { /// Creates an empty [PathMap] whose keys are compared using `context.equals` /// and `context.hash`. diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index 01397c8c..96702390 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -6,7 +6,7 @@ import 'dart:collection'; import '../path.dart' as p; -/// A set containing paths, compared using [equals] and [hash]. +/// A set containing paths, compared using [p.equals] and [p.hash]. class PathSet extends IterableBase implements Set { /// The set to which we forward implementation methods. final Set _inner; diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index dfd62a86..96702bfc 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -5,7 +5,7 @@ import 'package:test/test.dart'; import 'package:path/path.dart' as p; -/// A matcher for a closure that throws a [path.PathException]. +/// A matcher for a closure that throws a [p.PathException]. final throwsPathException = throwsA(const TypeMatcher()); void expectEquals(p.Context context, String path1, String path2) { From 96990c0aaf4401e34648373770ff048ecfd1d3e9 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 3 Jun 2020 13:13:47 -0700 Subject: [PATCH 126/183] Remove avoid_redundant_argument_values lint (dart-lang/path#86) Fix the lint in one case --- pkgs/path/analysis_options.yaml | 1 - pkgs/path/lib/src/parsed_path.dart | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 8a27e126..ffabc484 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -9,7 +9,6 @@ linter: - avoid_catching_errors - avoid_function_literals_in_foreach_calls - avoid_private_typedef_functions - - avoid_redundant_argument_values - avoid_renaming_method_parameters - avoid_returning_null_for_void - avoid_unused_constructor_parameters diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 45fb64b3..8d96cd5e 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -128,9 +128,8 @@ class ParsedPath { } // Canonicalize separators. - final newSeparators = List.generate( - newParts.length, (_) => style.separator, - growable: true); + final newSeparators = + List.generate(newParts.length, (_) => style.separator); newSeparators.insert( 0, isAbsolute && newParts.isNotEmpty && style.needsSeparator(root) From 0610f1b1fc13580eefa66e730910212dcb27ff9b Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 4 Jun 2020 11:29:08 -0700 Subject: [PATCH 127/183] Refactor a List.generate to List.filled (dart-lang/path#87) The meaning is more clear without the indirection of a closure that always returns the same value, and it will have better performance. Also replace the `List.insert` by making the length longer to start with and conditionally setting the first value. --- pkgs/path/lib/src/parsed_path.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 8d96cd5e..799a3bbd 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -128,16 +128,12 @@ class ParsedPath { } // Canonicalize separators. - final newSeparators = - List.generate(newParts.length, (_) => style.separator); - newSeparators.insert( - 0, - isAbsolute && newParts.isNotEmpty && style.needsSeparator(root) - ? style.separator - : ''); - parts = newParts; - separators = newSeparators; + separators = + List.filled(newParts.length + 1, style.separator, growable: true); + if (!isAbsolute || newParts.isEmpty || !style.needsSeparator(root)) { + separators[0] = ''; + } // Normalize the Windows root if needed. if (root != null && style == Style.windows) { From 40f285e7d80bdf66991bbc1d89b3967c23b49aa7 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Tue, 14 Jul 2020 13:12:05 -0700 Subject: [PATCH 128/183] Merge null_safety branch into master (dart-lang/path#89) --- pkgs/path/.travis.yml | 30 +++++----- pkgs/path/CHANGELOG.md | 4 +- pkgs/path/analysis_options.yaml | 3 + pkgs/path/benchmark/benchmark.dart | 4 +- pkgs/path/build.yaml | 13 +++++ pkgs/path/lib/path.dart | 38 ++++++------ pkgs/path/lib/src/context.dart | 57 +++++++++--------- pkgs/path/lib/src/internal_style.dart | 2 +- pkgs/path/lib/src/parsed_path.dart | 14 ++--- pkgs/path/lib/src/path_map.dart | 12 ++-- pkgs/path/lib/src/path_set.dart | 44 +++++++------- pkgs/path/lib/src/style.dart | 6 +- pkgs/path/lib/src/style/posix.dart | 2 +- pkgs/path/lib/src/style/url.dart | 2 +- pkgs/path/lib/src/style/windows.dart | 8 +-- pkgs/path/pubspec.yaml | 84 ++++++++++++++++++++++++++- pkgs/path/test/posix_test.dart | 3 - pkgs/path/test/url_test.dart | 1 - pkgs/path/test/windows_test.dart | 3 - 19 files changed, 208 insertions(+), 122 deletions(-) create mode 100644 pkgs/path/build.yaml diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index 5afa17a9..fc70ed4a 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -1,34 +1,30 @@ language: dart dart: - - 2.0.0 - dev -dart_task: - - test: --platform vm,chrome - -matrix: +jobs: include: - # Run tests on mac and windows – but just dev SDKs, no browser - dart: dev - dart_task: test + script: pub run --enable-experiment=non-nullable test + os: linux + - dart: dev + script: pub run --enable-experiment=non-nullable test os: windows - dart: dev - dart_task: test + script: pub run --enable-experiment=non-nullable test os: osx - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt + - dart_task: dartfmt + - dart_task: + dartanalyzer: --enable-experiment=non-nullable --fatal-infos --fatal-warnings . - dart: dev - dart_task: - dartanalyzer: --fatal-infos --fatal-warnings . - - dart: 2.0.0 - dart_task: - dartanalyzer: --fatal-warnings . + script: pub run --enable-experiment=non-nullable test -p chrome + os: linux # Only building master means that we don't run two builds for each pull request. +# Temporarily adding `null_safety` branches: - only: [master] + only: [master, null_safety] cache: directories: diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 5c962e1a..5c64f668 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,4 +1,6 @@ -## 1.7.1-dev +## 1.8.0-nullsafety + +* Migrate to null safety. ## 1.7.0 diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index ffabc484..743c74b1 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -4,6 +4,9 @@ analyzer: strong-mode: implicit-casts: false + enable-experiment: + - non-nullable + linter: rules: - avoid_catching_errors diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index a7fbb7ad..fe1e522f 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -32,14 +32,14 @@ final platformPaths = { }; /// The command line arguments passed to this script. -List arguments; +late final List arguments; void main(List args) { arguments = args; for (var style in [p.Style.posix, p.Style.url, p.Style.windows]) { final context = p.Context(style: style); - final files = genericPaths.toList()..addAll(platformPaths[style]); + final files = [...genericPaths, ...platformPaths[style]!]; void benchmark(String name, Function function) { runBenchmark('${style.name}-$name', 100000, () { diff --git a/pkgs/path/build.yaml b/pkgs/path/build.yaml new file mode 100644 index 00000000..340d63c7 --- /dev/null +++ b/pkgs/path/build.yaml @@ -0,0 +1,13 @@ +# See https://github.com/dart-lang/build/tree/master/build_web_compilers#configuration +# Matches previous configuration in pubspec.yaml - transformers - $dart2js +targets: + $default: + builders: + build_web_compilers|entrypoint: + # These are globs for the entrypoints you want to compile. + generate_for: + include: + - test/**.dart + exclude: + - test/io_test.dart + - test/**vm_test.dart diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 5ce50d66..a4a8be91 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -72,13 +72,13 @@ String get current { try { uri = Uri.base; } on Exception { - if (_current != null) return _current; + if (_current != null) return _current!; rethrow; } // Converting the base URI to a file path is pretty slow, and the base URI // rarely changes in practice, so we cache the result here. - if (uri == _currentUriBase) return _current; + if (uri == _currentUriBase) return _current!; _currentUriBase = uri; if (Style.platform == Style.url) { @@ -91,19 +91,19 @@ String get current { assert(path[lastIndex] == '/' || path[lastIndex] == '\\'); _current = lastIndex == 0 ? path : path.substring(0, lastIndex); } - return _current; + return _current!; } /// The last value returned by [Uri.base]. /// /// This is used to cache the current working directory. -Uri _currentUriBase; +Uri? _currentUriBase; /// The last known value of the current working directory. /// /// This is cached because [current] is called frequently but rarely actually /// changes. -String _current; +String? _current; /// Gets the path separator for the current platform. This is `\` on Windows /// and `/` on other platforms (including the browser). @@ -117,12 +117,12 @@ String get separator => context.separator; /// /// Does not [normalize] or [canonicalize] paths. String absolute(String part1, - [String part2, - String part3, - String part4, - String part5, - String part6, - String part7]) => + [String? part2, + String? part3, + String? part4, + String? part5, + String? part6, + String? part7]) => context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. @@ -255,13 +255,13 @@ bool isRootRelative(String path) => context.isRootRelative(path); /// /// p.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, - [String part2, - String part3, - String part4, - String part5, - String part6, - String part7, - String part8]) => + [String? part2, + String? part3, + String? part4, + String? part5, + String? part6, + String? part7, + String? part8]) => context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's @@ -355,7 +355,7 @@ String normalize(String path) => context.normalize(path); /// // URL /// p.relative('https://dart.dev', from: 'https://pub.dev'); /// // -> 'https://dart.dev' -String relative(String path, {String from}) => +String relative(String path, {String? from}) => context.relative(path, from: from); /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 21a7835e..f0945e4c 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -25,7 +25,7 @@ class Context { /// /// On the browser, [style] defaults to [Style.url] and [current] defaults to /// the current URL. - factory Context({Style style, String current}) { + factory Context({Style? style, String? current}) { if (current == null) { if (style == null) { current = p.current; @@ -56,7 +56,7 @@ class Context { /// The current directory given when Context was created. If null, current /// directory is evaluated from 'p.current'. - final String _current; + final String? _current; /// The current directory that relative paths are relative to. String get current => _current ?? p.current; @@ -75,12 +75,12 @@ class Context { /// If [current] isn't absolute, this won't return an absolute path. Does not /// [normalize] or [canonicalize] paths. String absolute(String part1, - [String part2, - String part3, - String part4, - String part5, - String part6, - String part7]) { + [String? part2, + String? part3, + String? part4, + String? part5, + String? part6, + String? part7]) { _validateArgList( 'absolute', [part1, part2, part3, part4, part5, part6, part7]); @@ -222,14 +222,14 @@ class Context { /// context.join('path', '/to', 'foo'); // -> '/to/foo' /// String join(String part1, - [String part2, - String part3, - String part4, - String part5, - String part6, - String part7, - String part8]) { - final parts = [ + [String? part2, + String? part3, + String? part4, + String? part5, + String? part6, + String? part7, + String? part8]) { + final parts = [ part1, part2, part3, @@ -240,7 +240,7 @@ class Context { part8 ]; _validateArgList('join', parts); - return joinAll(parts.where((part) => part != null)); + return joinAll(parts.whereType()); } /// Joins the given path parts into a single path. Example: @@ -270,7 +270,7 @@ class Context { final path = buffer.toString(); parsed.root = path.substring(0, style.rootLength(path, withDrive: true)); - if (style.needsSeparator(parsed.root)) { + if (style.needsSeparator(parsed.root!)) { parsed.separators[0] = style.separator; } buffer.clear(); @@ -325,7 +325,7 @@ class Context { final parsed = _parse(path); // Filter out empty parts that exist due to multiple separators in a row. parsed.parts = parsed.parts.where((part) => part.isNotEmpty).toList(); - if (parsed.root != null) parsed.parts.insert(0, parsed.root); + if (parsed.root != null) parsed.parts.insert(0, parsed.root!); return parsed.parts; } @@ -370,8 +370,8 @@ class Context { bool _needsNormalization(String path) { var start = 0; final codeUnits = path.codeUnits; - int previousPrevious; - int previous; + int? previousPrevious; + int? previous; // Skip past the root before we start looking for snippets that need // normalization. We want to normalize "//", but not when it's part of @@ -464,7 +464,7 @@ class Context { /// [from] to [path]. For example, if [current] and [path] are "." and [from] /// is "/", no path can be determined. In this case, a [PathException] will be /// thrown. - String relative(String path, {String from}) { + String relative(String path, {String? from}) { // Avoid expensive computation if the path is already relative. if (from == null && isRelative(path)) return normalize(path); @@ -500,7 +500,7 @@ class Context { // calculation of relative paths, even if a path has not been normalized. if (fromParsed.root != pathParsed.root && ((fromParsed.root == null || pathParsed.root == null) || - !style.pathsEqual(fromParsed.root, pathParsed.root))) { + !style.pathsEqual(fromParsed.root!, pathParsed.root!))) { return pathParsed.toString(); } @@ -647,7 +647,7 @@ class Context { var lastCodeUnit = chars.slash; /// The index of the last separator in [parent]. - int lastParentSeparator; + int? lastParentSeparator; // Iterate through both paths as long as they're semantically identical. var parentIndex = parentRootLength; @@ -765,8 +765,7 @@ class Context { lastParentSeparator ??= math.max(0, parentRootLength - 1); } - final direction = - _pathDirection(parent, lastParentSeparator ?? parentRootLength - 1); + final direction = _pathDirection(parent, lastParentSeparator); if (direction == _PathDirection.atRoot) return _PathRelation.equal; return direction == _PathDirection.aboveRoot ? _PathRelation.inconclusive @@ -888,14 +887,14 @@ class Context { final parsed = _parse(path); parsed.normalize(); - return _hashFast(parsed.toString()); + return _hashFast(parsed.toString())!; } /// An optimized implementation of [hash] that doesn't handle internal `..` /// components. /// /// This will handle `..` components that appear at the beginning of the path. - int _hashFast(String path) { + int? _hashFast(String path) { var hash = 4603; var beginning = true; var wasSeparator = true; @@ -1082,7 +1081,7 @@ Uri _parseUri(uri) { /// Validates that there are no non-null arguments following a null one and /// throws an appropriate [ArgumentError] on failure. -void _validateArgList(String method, List args) { +void _validateArgList(String method, List args) { for (var i = 1; i < args.length; i++) { // Ignore nulls hanging off the end. if (args[i] == null || args[i - 1] != null) continue; diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index dbf90988..30411436 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -43,7 +43,7 @@ abstract class InternalStyle extends Style { /// Gets the root prefix of [path] if path is absolute. If [path] is relative, /// returns `null`. @override - String getRoot(String path) { + String? getRoot(String path) { final length = rootLength(path); if (length > 0) return path.substring(0, length); return isRootRelative(path) ? path[0] : null; diff --git a/pkgs/path/lib/src/parsed_path.dart b/pkgs/path/lib/src/parsed_path.dart index 799a3bbd..60fa8491 100644 --- a/pkgs/path/lib/src/parsed_path.dart +++ b/pkgs/path/lib/src/parsed_path.dart @@ -13,7 +13,7 @@ class ParsedPath { /// On POSIX systems, this will be `null` or "/". On Windows, it can be /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive /// letters. - String root; + String? root; /// Whether this path is root-relative. /// @@ -33,7 +33,7 @@ class ParsedPath { /// The file extension of the last non-empty part, or "" if it doesn't have /// one. - String extension([int level]) => _splitExtension(level)[1]; + String extension([int level = 1]) => _splitExtension(level)[1]; /// `true` if this is an absolute path. bool get isAbsolute => root != null; @@ -131,14 +131,14 @@ class ParsedPath { parts = newParts; separators = List.filled(newParts.length + 1, style.separator, growable: true); - if (!isAbsolute || newParts.isEmpty || !style.needsSeparator(root)) { + if (!isAbsolute || newParts.isEmpty || !style.needsSeparator(root!)) { separators[0] = ''; } // Normalize the Windows root if needed. if (root != null && style == Style.windows) { - if (canonicalize) root = root.toLowerCase(); - root = root.replaceAll('/', '\\'); + if (canonicalize) root = root!.toLowerCase(); + root = root!.replaceAll('/', '\\'); } removeTrailingSeparators(); } @@ -185,13 +185,13 @@ class ParsedPath { /// 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 _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); + final file = + parts.cast().lastWhere((p) => p != '', orElse: () => null); if (file == null) return ['', '']; if (file == '..') return ['..', '']; diff --git a/pkgs/path/lib/src/path_map.dart b/pkgs/path/lib/src/path_map.dart index 99bea346..50f4d7e4 100644 --- a/pkgs/path/lib/src/path_map.dart +++ b/pkgs/path/lib/src/path_map.dart @@ -7,12 +7,12 @@ import 'dart:collection'; import '../path.dart' as p; /// A map whose keys are paths, compared using [p.equals] and [p.hash]. -class PathMap extends MapView { +class PathMap extends MapView { /// Creates an empty [PathMap] whose keys are compared using `context.equals` /// and `context.hash`. /// /// The [context] defaults to the current path context. - PathMap({p.Context context}) : super(_create(context)); + PathMap({p.Context? context}) : super(_create(context)); /// Creates a [PathMap] with the same keys and values as [other] whose keys /// are compared using `context.equals` and `context.hash`. @@ -20,19 +20,19 @@ class PathMap extends MapView { /// The [context] defaults to the current path context. If multiple keys in /// [other] represent the same logical path, the last key's value will be /// used. - PathMap.of(Map other, {p.Context context}) + PathMap.of(Map other, {p.Context? context}) : super(_create(context)..addAll(other)); /// Creates a map that uses [context] for equality and hashing. - static Map _create(p.Context context) { + static Map _create(p.Context? context) { context ??= p.context; return LinkedHashMap( equals: (path1, path2) { if (path1 == null) return path2 == null; if (path2 == null) return false; - return context.equals(path1, path2); + return context!.equals(path1, path2); }, - hashCode: (path) => path == null ? 0 : context.hash(path), + hashCode: (path) => path == null ? 0 : context!.hash(path), isValidKey: (path) => path is String || path == null); } } diff --git a/pkgs/path/lib/src/path_set.dart b/pkgs/path/lib/src/path_set.dart index 96702390..424c8a1e 100644 --- a/pkgs/path/lib/src/path_set.dart +++ b/pkgs/path/lib/src/path_set.dart @@ -7,15 +7,15 @@ import 'dart:collection'; import '../path.dart' as p; /// A set containing paths, compared using [p.equals] and [p.hash]. -class PathSet extends IterableBase implements Set { +class PathSet extends IterableBase implements Set { /// The set to which we forward implementation methods. - final Set _inner; + final Set _inner; /// Creates an empty [PathSet] whose contents are compared using /// `context.equals` and `context.hash`. /// /// The [context] defaults to the current path context. - PathSet({p.Context context}) : _inner = _create(context); + PathSet({p.Context? context}) : _inner = _create(context); /// Creates a [PathSet] with the same contents as [other] whose elements are /// compared using `context.equals` and `context.hash`. @@ -23,19 +23,19 @@ class PathSet extends IterableBase implements Set { /// The [context] defaults to the current path context. If multiple elements /// in [other] represent the same logical path, the first value will be /// used. - PathSet.of(Iterable other, {p.Context context}) + PathSet.of(Iterable other, {p.Context? context}) : _inner = _create(context)..addAll(other); /// Creates a set that uses [context] for equality and hashing. - static Set _create(p.Context context) { + static Set _create(p.Context? context) { context ??= p.context; return LinkedHashSet( equals: (path1, path2) { if (path1 == null) return path2 == null; if (path2 == null) return false; - return context.equals(path1, path2); + return context!.equals(path1, path2); }, - hashCode: (path) => path == null ? 0 : context.hash(path), + hashCode: (path) => path == null ? 0 : context!.hash(path), isValidKey: (path) => path is String || path == null); } @@ -44,16 +44,16 @@ class PathSet extends IterableBase implements Set { // it's so widely used that even brief version skew can be very painful. @override - Iterator get iterator => _inner.iterator; + Iterator get iterator => _inner.iterator; @override int get length => _inner.length; @override - bool add(String value) => _inner.add(value); + bool add(String? value) => _inner.add(value); @override - void addAll(Iterable elements) => _inner.addAll(elements); + void addAll(Iterable elements) => _inner.addAll(elements); @override Set cast() => _inner.cast(); @@ -62,38 +62,38 @@ class PathSet extends IterableBase implements Set { void clear() => _inner.clear(); @override - bool contains(Object element) => _inner.contains(element); + bool contains(Object? element) => _inner.contains(element); @override - bool containsAll(Iterable other) => _inner.containsAll(other); + bool containsAll(Iterable other) => _inner.containsAll(other); @override - Set difference(Set other) => _inner.difference(other); + Set difference(Set other) => _inner.difference(other); @override - Set intersection(Set other) => _inner.intersection(other); + Set intersection(Set other) => _inner.intersection(other); @override - String lookup(Object element) => _inner.lookup(element); + String? lookup(Object? element) => _inner.lookup(element); @override - bool remove(Object value) => _inner.remove(value); + bool remove(Object? value) => _inner.remove(value); @override - void removeAll(Iterable elements) => _inner.removeAll(elements); + void removeAll(Iterable elements) => _inner.removeAll(elements); @override - void removeWhere(bool Function(String) test) => _inner.removeWhere(test); + void removeWhere(bool Function(String?) test) => _inner.removeWhere(test); @override - void retainAll(Iterable elements) => _inner.retainAll(elements); + void retainAll(Iterable elements) => _inner.retainAll(elements); @override - void retainWhere(bool Function(String) test) => _inner.retainWhere(test); + void retainWhere(bool Function(String?) test) => _inner.retainWhere(test); @override - Set union(Set other) => _inner.union(other); + Set union(Set other) => _inner.union(other); @override - Set toSet() => _inner.toSet(); + Set toSet() => _inner.toSet(); } diff --git a/pkgs/path/lib/src/style.dart b/pkgs/path/lib/src/style.dart index d8b8ff17..e1b4fece 100644 --- a/pkgs/path/lib/src/style.dart +++ b/pkgs/path/lib/src/style.dart @@ -63,13 +63,13 @@ abstract class Style { Pattern get rootPattern; @Deprecated('Most Style members will be removed in path 2.0.') - Pattern get relativeRootPattern; + Pattern? get relativeRootPattern; @Deprecated('Most style members will be removed in path 2.0.') - String getRoot(String path); + String? getRoot(String path); @Deprecated('Most style members will be removed in path 2.0.') - String getRelativeRoot(String path); + String? getRelativeRoot(String path); @Deprecated('Most style members will be removed in path 2.0.') String pathFromUri(Uri uri); diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index f8b7e781..59c8d329 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -47,7 +47,7 @@ class PosixStyle extends InternalStyle { bool isRootRelative(String path) => false; @override - String getRelativeRoot(String path) => null; + String? getRelativeRoot(String path) => null; @override String pathFromUri(Uri uri) { diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 1f99bd55..9daccd79 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -79,7 +79,7 @@ class UrlStyle extends InternalStyle { path.isNotEmpty && isSeparator(path.codeUnitAt(0)); @override - String getRelativeRoot(String path) => isRootRelative(path) ? '/' : null; + String? getRelativeRoot(String path) => isRootRelative(path) ? '/' : null; @override String pathFromUri(Uri uri) => uri.toString(); diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 64d5a3f3..3b7d98c6 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -76,7 +76,7 @@ class WindowsStyle extends InternalStyle { bool isRootRelative(String path) => rootLength(path) == 1; @override - String getRelativeRoot(String path) { + String? getRelativeRoot(String path) { final length = rootLength(path); if (length == 1) return path[0]; return null; @@ -106,12 +106,12 @@ class WindowsStyle extends InternalStyle { @override Uri absolutePathToUri(String path) { final parsed = ParsedPath.parse(path, this); - if (parsed.root.startsWith(r'\\')) { + if (parsed.root!.startsWith(r'\\')) { // Network paths become "file://server/share/path/to/file". // The root is of the form "\\server\share". We want "server" to be the // URI host, and "share" to be the first element of the path. - final rootParts = parsed.root.split('\\').where((part) => part != ''); + final rootParts = parsed.root!.split('\\').where((part) => part != ''); parsed.parts.insert(0, rootParts.last); if (parsed.hasTrailingSeparator) { @@ -136,7 +136,7 @@ class WindowsStyle extends InternalStyle { // Get rid of the trailing "\" in "C:\" because the URI constructor will // add a separator on its own. parsed.parts - .insert(0, parsed.root.replaceAll('/', '').replaceAll('\\', '')); + .insert(0, parsed.root!.replaceAll('/', '').replaceAll('\\', '')); return Uri(scheme: 'file', pathSegments: parsed.parts); } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 92a54f9a..e25589c4 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.7.1-dev +version: 1.8.0-nullsafety description: >- A string-based path manipulation library. All of the path operations you know @@ -8,8 +8,88 @@ description: >- homepage: https://github.com/dart-lang/path environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.9.0-18.0 <2.9.0' dev_dependencies: pedantic: ^1.0.0 test: '>=0.12.42 <2.0.0' + + build_runner: ^1.0.0 + build_test: ^0.10.0 + build_web_compilers: '>=1.2.0 <3.0.0' + +dependency_overrides: + async: + git: + url: git://github.com/dart-lang/async.git + ref: null_safety + boolean_selector: + git: + url: git://github.com/dart-lang/boolean_selector.git + ref: null_safety + charcode: + git: + url: git://github.com/dart-lang/charcode.git + ref: null_safety + collection: 1.15.0-nullsafety + js: + git: + url: git://github.com/dart-lang/sdk.git + ref: master + path: pkg/js + matcher: + git: + url: git://github.com/dart-lang/matcher.git + ref: null_safety + meta: 1.3.0-nullsafety + pedantic: + git: + url: git://github.com/dart-lang/pedantic.git + ref: null_safety + pool: + git: + url: git://github.com/dart-lang/pool.git + ref: null_safety + source_maps: + git: + url: git://github.com/dart-lang/source_maps.git + ref: null_safety + source_map_stack_trace: + git: + url: git://github.com/dart-lang/source_map_stack_trace.git + ref: null_safety + source_span: + git: + url: git://github.com/dart-lang/source_span.git + ref: null_safety + stack_trace: + git: + url: git://github.com/dart-lang/stack_trace.git + ref: null_safety + stream_channel: + git: + url: git://github.com/dart-lang/stream_channel.git + ref: null_safety + string_scanner: + git: + url: git://github.com/dart-lang/string_scanner.git + ref: null_safety + term_glyph: + git: + url: git://github.com/dart-lang/term_glyph.git + ref: null_safety + test_api: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_api + test_core: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_core + test: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 6b731457..8ade4039 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -32,8 +32,6 @@ void main() { 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', () { @@ -183,7 +181,6 @@ void main() { test('disallows intermediate nulls', () { expect(() => context.join('a', null, 'b'), throwsArgumentError); - expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 143b6024..cb2d4cb6 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -253,7 +253,6 @@ void main() { test('disallows intermediate nulls', () { expect(() => context.join('a', null, 'b'), throwsArgumentError); - expect(() => context.join(null, 'a'), throwsArgumentError); }); test('does not modify internal ., .., or trailing separators', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index ad3deb6e..d6587950 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -36,8 +36,6 @@ void main() { 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', () { @@ -248,7 +246,6 @@ void main() { test('disallows intermediate nulls', () { expect(() => context.join('a', null, 'b'), throwsArgumentError); - expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { From d567a47bff8ac7ffa4acea6bfac96916cd438e05 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Tue, 21 Jul 2020 19:40:37 -0700 Subject: [PATCH 129/183] update for the 2.10 dev sdk (dart-lang/path#90) This is in preparation for the actual 2.10 dev sdk release. --- pkgs/path/.travis.yml | 12 ++++---- pkgs/path/pubspec.yaml | 69 +++++++++++++++--------------------------- 2 files changed, 30 insertions(+), 51 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index fc70ed4a..95c8aa76 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -1,30 +1,30 @@ language: dart dart: - - dev + - preview/raw/2.10.0-0.2-dev jobs: include: - - dart: dev + - dart: preview/raw/2.10.0-0.2-dev script: pub run --enable-experiment=non-nullable test os: linux - - dart: dev + - dart: preview/raw/2.10.0-0.2-dev script: pub run --enable-experiment=non-nullable test os: windows - - dart: dev + - dart: preview/raw/2.10.0-0.2-dev script: pub run --enable-experiment=non-nullable test os: osx - dart_task: dartfmt - dart_task: dartanalyzer: --enable-experiment=non-nullable --fatal-infos --fatal-warnings . - - dart: dev + - dart: preview/raw/2.10.0-0.2-dev script: pub run --enable-experiment=non-nullable test -p chrome os: linux # Only building master means that we don't run two builds for each pull request. # Temporarily adding `null_safety` branches: - only: [master, null_safety] + only: [master] cache: directories: diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index e25589c4..97c37b36 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -8,7 +8,8 @@ description: >- homepage: https://github.com/dart-lang/path environment: - sdk: '>=2.9.0-18.0 <2.9.0' + # This must remain a tight constraint until nnbd is stable + sdk: '>=2.10.0-0 <2.10.0' dev_dependencies: pedantic: ^1.0.0 @@ -20,76 +21,54 @@ dev_dependencies: dependency_overrides: async: - git: - url: git://github.com/dart-lang/async.git - ref: null_safety + git: git://github.com/dart-lang/async.git boolean_selector: - git: - url: git://github.com/dart-lang/boolean_selector.git - ref: null_safety + git: git://github.com/dart-lang/boolean_selector.git charcode: - git: - url: git://github.com/dart-lang/charcode.git - ref: null_safety - collection: 1.15.0-nullsafety + git: git://github.com/dart-lang/charcode.git + collection: + git: git://github.com/dart-lang/collection.git js: git: url: git://github.com/dart-lang/sdk.git - ref: master path: pkg/js + ref: 2-10-pkgs matcher: + git: git://github.com/dart-lang/matcher.git + meta: git: - url: git://github.com/dart-lang/matcher.git - ref: null_safety - meta: 1.3.0-nullsafety + url: git://github.com/dart-lang/sdk.git + path: pkg/meta + ref: 2-10-pkgs pedantic: - git: - url: git://github.com/dart-lang/pedantic.git - ref: null_safety + git: git://github.com/dart-lang/pedantic.git pool: - git: - url: git://github.com/dart-lang/pool.git - ref: null_safety + git: git://github.com/dart-lang/pool.git source_maps: - git: - url: git://github.com/dart-lang/source_maps.git - ref: null_safety + git: git://github.com/dart-lang/source_maps.git source_map_stack_trace: - git: - url: git://github.com/dart-lang/source_map_stack_trace.git - ref: null_safety + git: git://github.com/dart-lang/source_map_stack_trace.git source_span: - git: - url: git://github.com/dart-lang/source_span.git - ref: null_safety + git: git://github.com/dart-lang/source_span.git stack_trace: - git: - url: git://github.com/dart-lang/stack_trace.git - ref: null_safety + git: git://github.com/dart-lang/stack_trace.git stream_channel: - git: - url: git://github.com/dart-lang/stream_channel.git - ref: null_safety + git: git://github.com/dart-lang/stream_channel.git string_scanner: - git: - url: git://github.com/dart-lang/string_scanner.git - ref: null_safety + git: git://github.com/dart-lang/string_scanner.git term_glyph: - git: - url: git://github.com/dart-lang/term_glyph.git - ref: null_safety + git: git://github.com/dart-lang/term_glyph.git test_api: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test_api test_core: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test_core test: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test + typed_data: + git: git://github.com/dart-lang/typed_data.git From 32182a61c05f02279eb6c32c1695ef9676b73e8c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Jul 2020 14:04:29 -0700 Subject: [PATCH 130/183] Travis-CI: switch to use dev SDK (dart-lang/path#91) --- pkgs/path/.travis.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml index 95c8aa76..60e6a3e7 100644 --- a/pkgs/path/.travis.yml +++ b/pkgs/path/.travis.yml @@ -1,24 +1,20 @@ language: dart dart: - - preview/raw/2.10.0-0.2-dev + - dev jobs: include: - - dart: preview/raw/2.10.0-0.2-dev - script: pub run --enable-experiment=non-nullable test + - script: pub run --enable-experiment=non-nullable test os: linux - - dart: preview/raw/2.10.0-0.2-dev - script: pub run --enable-experiment=non-nullable test + - script: pub run --enable-experiment=non-nullable test os: windows - - dart: preview/raw/2.10.0-0.2-dev - script: pub run --enable-experiment=non-nullable test + - script: pub run --enable-experiment=non-nullable test os: osx - dart_task: dartfmt - dart_task: dartanalyzer: --enable-experiment=non-nullable --fatal-infos --fatal-warnings . - - dart: preview/raw/2.10.0-0.2-dev - script: pub run --enable-experiment=non-nullable test -p chrome + - script: pub run --enable-experiment=non-nullable test -p chrome os: linux # Only building master means that we don't run two builds for each pull request. From 9c6e4bba193b64f3e996a6ae470d5f8d6de9e9dd Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 22 Sep 2020 08:52:58 -0700 Subject: [PATCH 131/183] Prepare for the 2.11 dev SDKs (dart-lang/path#93) Bump the upper bound to allow 2.10 stable and 2.11.0 dev SDK versions. --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 5c64f668..378cb915 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.0-nullsafety.1 + +* Allow 2.10 stable and 2.11.0 dev SDK versions. + ## 1.8.0-nullsafety * Migrate to null safety. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 97c37b36..3b1aafaf 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.0-nullsafety +version: 1.8.0-nullsafety.1 description: >- A string-based path manipulation library. All of the path operations you know @@ -9,7 +9,7 @@ homepage: https://github.com/dart-lang/path environment: # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.10.0' + sdk: '>=2.10.0-0 <2.11.0' dev_dependencies: pedantic: ^1.0.0 From c768afc98e18eb47def598fba602302197bae608 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 24 Sep 2020 06:53:10 -0700 Subject: [PATCH 132/183] Cleanup dependencies, remove build.yaml (dart-lang/path#94) Not verifying behavior with build system Moved from git overrides to published null-safe dependencies --- pkgs/path/build.yaml | 13 --------- pkgs/path/pubspec.yaml | 62 ++---------------------------------------- 2 files changed, 2 insertions(+), 73 deletions(-) delete mode 100644 pkgs/path/build.yaml diff --git a/pkgs/path/build.yaml b/pkgs/path/build.yaml deleted file mode 100644 index 340d63c7..00000000 --- a/pkgs/path/build.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# See https://github.com/dart-lang/build/tree/master/build_web_compilers#configuration -# Matches previous configuration in pubspec.yaml - transformers - $dart2js -targets: - $default: - builders: - build_web_compilers|entrypoint: - # These are globs for the entrypoints you want to compile. - generate_for: - include: - - test/**.dart - exclude: - - test/io_test.dart - - test/**vm_test.dart diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 3b1aafaf..59aa99ed 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -12,63 +12,5 @@ environment: sdk: '>=2.10.0-0 <2.11.0' dev_dependencies: - pedantic: ^1.0.0 - test: '>=0.12.42 <2.0.0' - - build_runner: ^1.0.0 - build_test: ^0.10.0 - build_web_compilers: '>=1.2.0 <3.0.0' - -dependency_overrides: - async: - git: git://github.com/dart-lang/async.git - boolean_selector: - git: git://github.com/dart-lang/boolean_selector.git - charcode: - git: git://github.com/dart-lang/charcode.git - collection: - git: git://github.com/dart-lang/collection.git - js: - git: - url: git://github.com/dart-lang/sdk.git - path: pkg/js - ref: 2-10-pkgs - matcher: - git: git://github.com/dart-lang/matcher.git - meta: - git: - url: git://github.com/dart-lang/sdk.git - path: pkg/meta - ref: 2-10-pkgs - pedantic: - git: git://github.com/dart-lang/pedantic.git - pool: - git: git://github.com/dart-lang/pool.git - source_maps: - git: git://github.com/dart-lang/source_maps.git - source_map_stack_trace: - git: git://github.com/dart-lang/source_map_stack_trace.git - source_span: - git: git://github.com/dart-lang/source_span.git - stack_trace: - git: git://github.com/dart-lang/stack_trace.git - stream_channel: - git: git://github.com/dart-lang/stream_channel.git - string_scanner: - git: git://github.com/dart-lang/string_scanner.git - term_glyph: - git: git://github.com/dart-lang/term_glyph.git - test_api: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test_api - test_core: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test_core - test: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test - typed_data: - git: git://github.com/dart-lang/typed_data.git + pedantic: ^1.10.0-nullsafety + test: ^1.16.0-nullsafety.4 From b11bd00e8fefb11e231ab762f78068d62d96e4fe Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 23 Oct 2020 13:03:02 -0700 Subject: [PATCH 133/183] allow the 2.12 prerelease sdks (dart-lang/path#95) --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 378cb915..8cba09eb 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.0-nullsafety.2 + +* Allow prerelease versions of the 2.12 sdk. + ## 1.8.0-nullsafety.1 * Allow 2.10 stable and 2.11.0 dev SDK versions. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 59aa99ed..c0e758e0 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.0-nullsafety.1 +version: 1.8.0-nullsafety.2 description: >- A string-based path manipulation library. All of the path operations you know @@ -9,7 +9,7 @@ homepage: https://github.com/dart-lang/path environment: # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.11.0' + sdk: '>=2.10.0-0 <2.12.0' dev_dependencies: pedantic: ^1.10.0-nullsafety From bca81dd152576ac0a566895713cc6af0c17f6e61 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 3 Nov 2020 14:15:16 -0800 Subject: [PATCH 134/183] Bump SDK constraints for pub (dart-lang/path#96) Use a 2.12.0 lower bound since pub does not understand allowed experiments for earlier versions. Use a 3.0.0 upper bound to avoid a warning in pub and to give some flexibility in publishing for stable. --- pkgs/path/CHANGELOG.md | 5 +++++ pkgs/path/pubspec.yaml | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 8cba09eb..b0675ed2 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.8.0-nullsafety.3 + +* Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release + guidelines. + ## 1.8.0-nullsafety.2 * Allow prerelease versions of the 2.12 sdk. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index c0e758e0..36b7da82 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.0-nullsafety.2 +version: 1.8.0-nullsafety.3 description: >- A string-based path manipulation library. All of the path operations you know @@ -8,8 +8,7 @@ description: >- homepage: https://github.com/dart-lang/path environment: - # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.12.0' + sdk: ">=2.12.0-0 <3.0.0" dev_dependencies: pedantic: ^1.10.0-nullsafety From e6da0d97c618987df7745a1128dc5cd5f54db658 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 12 Nov 2020 17:41:58 -0800 Subject: [PATCH 135/183] Move from Travis to GitHub actions for CI (dart-lang/path#98) Also delete the unneeded .test_config file --- pkgs/path/.github/workflows/ci.yml | 46 ++++++++++++++++++++++++++++++ pkgs/path/.test_config | 3 -- pkgs/path/.travis.yml | 27 ------------------ 3 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 pkgs/path/.github/workflows/ci.yml delete mode 100644 pkgs/path/.test_config delete mode 100644 pkgs/path/.travis.yml diff --git a/pkgs/path/.github/workflows/ci.yml b/pkgs/path/.github/workflows/ci.yml new file mode 100644 index 00000000..67946acf --- /dev/null +++ b/pkgs/path/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: ci + +on: + push: + branches: [ master ] + pull_request: + schedule: + # “At 00:00 (UTC) on Sunday.” + - cron: '0 0 * * 0' + +jobs: + analyze_format: + runs-on: ubuntu-latest + steps: + - uses: cedx/setup-dart@v2 + with: + release-channel: dev + - uses: actions/checkout@v2 + + - run: pub get + + - run: dart format --output=none --set-exit-if-changed . + - run: dart analyze --fatal-infos . + test_web: + runs-on: ubuntu-latest + steps: + - uses: cedx/setup-dart@v2 + with: + release-channel: dev + - uses: actions/checkout@v2 + + - run: pub get + - run: dart test -p chrome + test_vm: + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: cedx/setup-dart@v2 + with: + release-channel: dev + - uses: actions/checkout@v2 + + - run: pub get + - run: dart test -p vm diff --git a/pkgs/path/.test_config b/pkgs/path/.test_config deleted file mode 100644 index 412fc5c5..00000000 --- a/pkgs/path/.test_config +++ /dev/null @@ -1,3 +0,0 @@ -{ - "test_package": true -} \ No newline at end of file diff --git a/pkgs/path/.travis.yml b/pkgs/path/.travis.yml deleted file mode 100644 index 60e6a3e7..00000000 --- a/pkgs/path/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: dart - -dart: - - dev - -jobs: - include: - - script: pub run --enable-experiment=non-nullable test - os: linux - - script: pub run --enable-experiment=non-nullable test - os: windows - - script: pub run --enable-experiment=non-nullable test - os: osx - - dart_task: dartfmt - - dart_task: - dartanalyzer: --enable-experiment=non-nullable --fatal-infos --fatal-warnings . - - script: pub run --enable-experiment=non-nullable test -p chrome - os: linux - -# Only building master means that we don't run two builds for each pull request. -# Temporarily adding `null_safety` -branches: - only: [master] - -cache: - directories: - - $HOME/.pub-cache From 4793abe9bfa94b450ad7e52f17572dfe1e98265c Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 16 Nov 2020 08:40:08 -0800 Subject: [PATCH 136/183] GH actions: use pub run test, print dart version (dart-lang/path#99) --- pkgs/path/.github/workflows/ci.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/ci.yml b/pkgs/path/.github/workflows/ci.yml index 67946acf..1608e168 100644 --- a/pkgs/path/.github/workflows/ci.yml +++ b/pkgs/path/.github/workflows/ci.yml @@ -15,6 +15,7 @@ jobs: - uses: cedx/setup-dart@v2 with: release-channel: dev + - run: dart --version - uses: actions/checkout@v2 - run: pub get @@ -27,10 +28,11 @@ jobs: - uses: cedx/setup-dart@v2 with: release-channel: dev + - run: dart --version - uses: actions/checkout@v2 - run: pub get - - run: dart test -p chrome + - run: pub run test -p chrome test_vm: strategy: matrix: @@ -40,7 +42,8 @@ jobs: - uses: cedx/setup-dart@v2 with: release-channel: dev + - run: dart --version - uses: actions/checkout@v2 - run: pub get - - run: dart test -p vm + - run: pub run test -p vm From f13a94769ee464be57d23fb2ec77bb85e35bd9aa Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 2 Feb 2021 12:15:50 -0800 Subject: [PATCH 137/183] Prepare to publish for stable null safety (dart-lang/path#104) --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index b0675ed2..7c15e3e5 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.0 + +* Stable release for null safety. + ## 1.8.0-nullsafety.3 * Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 36b7da82..67a0d51b 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.0-nullsafety.3 +version: 1.8.0 description: >- A string-based path manipulation library. All of the path operations you know From 193ae8260676961ba45edbc61f5daef85ff0bac8 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 8 Feb 2021 16:38:48 -0800 Subject: [PATCH 138/183] Drop unnecessary constructors (dart-lang/path#105) When a class has no other constructors it is unnecessary and non-idiomatic to define the default empty constructor. --- pkgs/path/CHANGELOG.md | 2 ++ pkgs/path/lib/src/style/posix.dart | 2 -- pkgs/path/lib/src/style/url.dart | 2 -- pkgs/path/lib/src/style/windows.dart | 2 -- pkgs/path/pubspec.yaml | 2 +- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 7c15e3e5..264eb6a4 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.8.1-dev + ## 1.8.0 * Stable release for null safety. diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 59c8d329..70918655 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -8,8 +8,6 @@ import '../parsed_path.dart'; /// The style for POSIX paths. class PosixStyle extends InternalStyle { - PosixStyle(); - @override final name = 'posix'; @override diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 9daccd79..3ff87ced 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -8,8 +8,6 @@ import '../utils.dart'; /// The style for URL paths. class UrlStyle extends InternalStyle { - UrlStyle(); - @override final name = 'url'; @override diff --git a/pkgs/path/lib/src/style/windows.dart b/pkgs/path/lib/src/style/windows.dart index 3b7d98c6..b7542c6d 100644 --- a/pkgs/path/lib/src/style/windows.dart +++ b/pkgs/path/lib/src/style/windows.dart @@ -13,8 +13,6 @@ const _asciiCaseBit = 0x20; /// The style for Windows paths. class WindowsStyle extends InternalStyle { - WindowsStyle(); - @override final name = 'windows'; @override diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 67a0d51b..4fe70bc8 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.0 +version: 1.8.1-dev description: >- A string-based path manipulation library. All of the path operations you know From 25106a8471ec1038e9eab53ad06035e40aeb89c8 Mon Sep 17 00:00:00 2001 From: Franklin Yow <58489007+franklinyow@users.noreply.github.com> Date: Fri, 2 Apr 2021 16:40:17 -0700 Subject: [PATCH 139/183] Update LICENSE (dart-lang/path#107) Changes to comply with internal review --- pkgs/path/LICENSE | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/path/LICENSE b/pkgs/path/LICENSE index 5c60afea..000cd7be 100644 --- a/pkgs/path/LICENSE +++ b/pkgs/path/LICENSE @@ -1,4 +1,5 @@ -Copyright 2014, the Dart project authors. All rights reserved. +Copyright 2014, the Dart project authors. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -9,7 +10,7 @@ met: copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. From 25d6f9ab97e29a3d3b780618a0d8a6512c30a9f0 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 23 Apr 2021 10:52:16 -0700 Subject: [PATCH 140/183] Update ci.yml (dart-lang/path#109) Use latest CI setup script Fix directive ordering --- pkgs/path/.github/workflows/ci.yml | 71 ++++++++++++++++-------------- pkgs/path/test/browser_test.dart | 3 +- pkgs/path/test/path_map_test.dart | 3 +- pkgs/path/test/path_set_test.dart | 3 +- pkgs/path/test/path_test.dart | 2 +- pkgs/path/test/posix_test.dart | 2 +- pkgs/path/test/url_test.dart | 2 +- pkgs/path/test/utils.dart | 2 +- pkgs/path/test/windows_test.dart | 2 +- 9 files changed, 45 insertions(+), 45 deletions(-) diff --git a/pkgs/path/.github/workflows/ci.yml b/pkgs/path/.github/workflows/ci.yml index 1608e168..6cb37f72 100644 --- a/pkgs/path/.github/workflows/ci.yml +++ b/pkgs/path/.github/workflows/ci.yml @@ -1,49 +1,52 @@ name: ci on: + # Run on PRs and pushes to the default branch. push: branches: [ master ] pull_request: + branches: [ master ] schedule: - # “At 00:00 (UTC) on Sunday.” - - cron: '0 0 * * 0' - -jobs: - analyze_format: - runs-on: ubuntu-latest - steps: - - uses: cedx/setup-dart@v2 - with: - release-channel: dev - - run: dart --version - - uses: actions/checkout@v2 + - cron: "0 0 * * 0" - - run: pub get +env: + PUB_ENVIRONMENT: bot.github - - run: dart format --output=none --set-exit-if-changed . - - run: dart analyze --fatal-infos . - test_web: +jobs: + # Check code formatting and static analysis on a single OS (linux) + # against Dart dev and stable. + analyze: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [dev] steps: - - uses: cedx/setup-dart@v2 - with: - release-channel: dev - - run: dart --version - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v1.0 + with: + sdk: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + run: dart analyze --fatal-infos + if: always() && steps.install.outcome == 'success' - - run: pub get - - run: pub run test -p chrome - test_vm: + test: + needs: analyze + runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, windows-latest, ubuntu-latest] - runs-on: ${{ matrix.os }} + os: [ubuntu-latest] + sdk: [2.12.0, dev] steps: - - uses: cedx/setup-dart@v2 - with: - release-channel: dev - - run: dart --version - - uses: actions/checkout@v2 - - - run: pub get - - run: pub run test -p vm + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v1.0 + with: + sdk: ${{ matrix.sdk }} + - run: dart pub get + - run: dart test --platform vm,chrome diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index ac25dbe2..24b36a73 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -3,11 +3,10 @@ // BSD-style license that can be found in the LICENSE file. @TestOn('browser') - import 'dart:html'; -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; void main() { group('new Context()', () { diff --git a/pkgs/path/test/path_map_test.dart b/pkgs/path/test/path_map_test.dart index 127d7c6f..11c4a3e0 100644 --- a/pkgs/path/test/path_map_test.dart +++ b/pkgs/path/test/path_map_test.dart @@ -2,9 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; - import 'package:path/path.dart'; +import 'package:test/test.dart'; void main() { group('considers equal', () { diff --git a/pkgs/path/test/path_set_test.dart b/pkgs/path/test/path_set_test.dart index 3214e19e..135f9def 100644 --- a/pkgs/path/test/path_set_test.dart +++ b/pkgs/path/test/path_set_test.dart @@ -2,9 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; - import 'package:path/path.dart'; +import 'package:test/test.dart'; void main() { group('considers equal', () { diff --git a/pkgs/path/test/path_test.dart b/pkgs/path/test/path_test.dart index 6625ac4b..b99b78b0 100644 --- a/pkgs/path/test/path_test.dart +++ b/pkgs/path/test/path_test.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; void main() { group('path.Style', () { diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 8ade4039..53b18f22 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; import 'utils.dart'; diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index cb2d4cb6..96f86b57 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; import 'utils.dart'; diff --git a/pkgs/path/test/utils.dart b/pkgs/path/test/utils.dart index 96702bfc..08d4a524 100644 --- a/pkgs/path/test/utils.dart +++ b/pkgs/path/test/utils.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; import 'package:path/path.dart' as p; +import 'package:test/test.dart'; /// A matcher for a closure that throws a [p.PathException]. final throwsPathException = throwsA(const TypeMatcher()); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index d6587950..1e8c374e 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:test/test.dart'; import 'package:path/path.dart' as path; +import 'package:test/test.dart'; import 'utils.dart'; From e089806cfe738ef1c473bcaaae0644c3b88a6735 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 22 Jul 2021 12:06:10 -0700 Subject: [PATCH 141/183] Move from pedantic to lints package (dart-lang/path#111) Fix existing violations of the lint `unnecessary_string_escapes` --- pkgs/path/analysis_options.yaml | 5 +---- pkgs/path/pubspec.yaml | 6 +++--- pkgs/path/test/url_test.dart | 4 ++-- pkgs/path/test/windows_test.dart | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 743c74b1..92ae0642 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -1,12 +1,9 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml analyzer: strong-mode: implicit-casts: false - enable-experiment: - - non-nullable - linter: rules: - avoid_catching_errors diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 4fe70bc8..310675d9 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -8,8 +8,8 @@ description: >- homepage: https://github.com/dart-lang/path environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dev_dependencies: - pedantic: ^1.10.0-nullsafety - test: ^1.16.0-nullsafety.4 + lints: ^1.0.0 + test: ^1.16.0 diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 96f86b57..16eca4c6 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -419,8 +419,8 @@ void main() { expect(context.normalize('a/./../b'), 'b'); expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); expect(context.normalize('a/b/../../../../c'), '../../c'); - expect(context.normalize('z/a/b/../../..\../c'), 'z/..\../c'); - expect(context.normalize('a/b\c/../d'), 'a/d'); + expect(context.normalize('z/a/b/../../..../c'), 'z/..../c'); + expect(context.normalize('a/bc/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 1e8c374e..e59b207f 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -22,7 +22,7 @@ void main() { expect(context.extension('a/..'), ''); expect(context.extension('foo.dart'), '.dart'); expect(context.extension('foo.dart.js'), '.js'); - expect(context.extension('foo bar\gule fisk.dart.js'), '.js'); + expect(context.extension('foo bargule fisk.dart.js'), '.js'); expect(context.extension(r'a.b\c'), ''); expect(context.extension('a.b/c.d'), '.d'); expect(context.extension(r'~\.bashrc'), ''); From b28f107357588b44fa6beb8f62c672a95d8a5ec2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 14 Dec 2021 14:48:51 -0800 Subject: [PATCH 142/183] Don't crash on p.toUri('') (dart-lang/path#114) --- pkgs/path/CHANGELOG.md | 4 +++- pkgs/path/lib/src/internal_style.dart | 1 + pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 1 + pkgs/path/test/url_test.dart | 1 + pkgs/path/test/windows_test.dart | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 264eb6a4..f6d69ba3 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,4 +1,6 @@ -## 1.8.1-dev +## 1.8.1 + +* Don't crash when an empty string is passed to `toUri()`. ## 1.8.0 diff --git a/pkgs/path/lib/src/internal_style.dart b/pkgs/path/lib/src/internal_style.dart index 30411436..71762c16 100644 --- a/pkgs/path/lib/src/internal_style.dart +++ b/pkgs/path/lib/src/internal_style.dart @@ -61,6 +61,7 @@ abstract class InternalStyle extends Style { /// Returns the URI that represents a relative path. @override Uri relativePathToUri(String path) { + if (path.isEmpty) return Uri(); final segments = context.split(path); // Ensure that a trailing slash in the path produces a trailing slash in the diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 310675d9..1bd006e4 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.1-dev +version: 1.8.1 description: >- A string-based path manipulation library. All of the path operations you know diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 53b18f22..cc64a541 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -590,6 +590,7 @@ void main() { Uri.parse('file:///_%7B_%7D_%60_%5E_%20_%22_%25_')); expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(context.toUri(''), Uri.parse('')); }); group('prettyUri', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 16eca4c6..e0b59030 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -871,6 +871,7 @@ void main() { Uri.parse('http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_')); expect(context.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(context.toUri(''), Uri.parse('')); }); group('prettyUri', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index e59b207f..be9a790f 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -759,6 +759,7 @@ void main() { Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_')); expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); + expect(context.toUri(''), Uri.parse('')); }); group('prettyUri', () { From b94c6d780bf609f048808d46c150d034b80f946a Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 15 Feb 2022 17:24:18 -0800 Subject: [PATCH 143/183] enable the avoid_dynamic_calls lint (dart-lang/path#119) --- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/analysis_options.yaml | 1 + pkgs/path/benchmark/benchmark.dart | 4 ++-- pkgs/path/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index f6d69ba3..faa5e7e5 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.2-dev + +* Enable the `avoid_dynamic_calls` lint. + ## 1.8.1 * Don't crash when an empty string is passed to `toUri()`. diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 92ae0642..cc7a1dbc 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -7,6 +7,7 @@ analyzer: linter: rules: - avoid_catching_errors + - avoid_dynamic_calls - avoid_function_literals_in_foreach_calls - avoid_private_typedef_functions - avoid_renaming_method_parameters diff --git a/pkgs/path/benchmark/benchmark.dart b/pkgs/path/benchmark/benchmark.dart index fe1e522f..b6647bb2 100644 --- a/pkgs/path/benchmark/benchmark.dart +++ b/pkgs/path/benchmark/benchmark.dart @@ -41,7 +41,7 @@ void main(List args) { final context = p.Context(style: style); final files = [...genericPaths, ...platformPaths[style]!]; - void benchmark(String name, Function function) { + void benchmark(String name, void Function(String) function) { runBenchmark('${style.name}-$name', 100000, () { for (var file in files) { function(file); @@ -85,7 +85,7 @@ void main(List args) { runBenchmark('current', 100000, () => p.current); } -void runBenchmark(String name, int count, Function function) { +void runBenchmark(String name, int count, void Function() function) { // If names are passed on the command-line, they select which benchmarks are // run. if (arguments.isNotEmpty && !arguments.contains(name)) return; diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 1bd006e4..e7037f06 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.1 +version: 1.8.2-dev description: >- A string-based path manipulation library. All of the path operations you know From 0f04acb967f2a04c20f8046fe8781f4b956a14e0 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 20 Apr 2022 14:42:44 -0700 Subject: [PATCH 144/183] Switch from homepage to repository in pubspec (dart-lang/path#122) --- pkgs/path/pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index e7037f06..1ab6976e 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,11 +1,10 @@ name: path version: 1.8.2-dev - description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the web. -homepage: https://github.com/dart-lang/path +repository: https://github.com/dart-lang/path environment: sdk: ">=2.12.0 <3.0.0" From 3ea385c110a2f609b8bfad9b476a29a9dc87c450 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 18 May 2022 12:42:59 -0700 Subject: [PATCH 145/183] Prep for publishing 1.8.2 (dart-lang/path#123) prep for publishing 1.8.2 --- pkgs/path/.github/workflows/{ci.yml => dart.yml} | 2 +- pkgs/path/CHANGELOG.md | 3 ++- pkgs/path/README.md | 4 ++++ pkgs/path/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) rename pkgs/path/.github/workflows/{ci.yml => dart.yml} (98%) diff --git a/pkgs/path/.github/workflows/ci.yml b/pkgs/path/.github/workflows/dart.yml similarity index 98% rename from pkgs/path/.github/workflows/ci.yml rename to pkgs/path/.github/workflows/dart.yml index 6cb37f72..94b7fc34 100644 --- a/pkgs/path/.github/workflows/ci.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -1,4 +1,4 @@ -name: ci +name: Dart CI on: # Run on PRs and pushes to the default branch. diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index faa5e7e5..2cf8eaea 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,6 +1,7 @@ -## 1.8.2-dev +## 1.8.2 * Enable the `avoid_dynamic_calls` lint. +* Popuate the pubspec `repository` field. ## 1.8.1 diff --git a/pkgs/path/README.md b/pkgs/path/README.md index b4ac3e29..bd317bc9 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -1,3 +1,7 @@ +[![Dart CI](https://github.com/dart-lang/path/actions/workflows/ci.yml/badge.svg)](https://github.com/dart-lang/path/actions/workflows/ci.yml) +[![pub package](https://img.shields.io/pub/v/path.svg)](https://pub.dev/packages/path) +[![package publisher](https://img.shields.io/pub/publisher/path.svg)](https://pub.dev/packages/path/publisher) + A comprehensive, cross-platform path manipulation library for Dart. The path package provides common operations for manipulating paths: diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 1ab6976e..78d3cc05 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.2-dev +version: 1.8.2 description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the From 5590c8cb2db06e0910677354334bbe952cd0f4e1 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 31 May 2022 02:42:37 -0700 Subject: [PATCH 146/183] Update README.md (dart-lang/path#125) --- pkgs/path/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index bd317bc9..395e4966 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -1,4 +1,4 @@ -[![Dart CI](https://github.com/dart-lang/path/actions/workflows/ci.yml/badge.svg)](https://github.com/dart-lang/path/actions/workflows/ci.yml) +[![Dart CI](https://github.com/dart-lang/path/actions/workflows/dart.yml/badge.svg)](https://github.com/dart-lang/path/actions/workflows/dart.yml) [![pub package](https://img.shields.io/pub/v/path.svg)](https://pub.dev/packages/path) [![package publisher](https://img.shields.io/pub/publisher/path.svg)](https://pub.dev/packages/path/publisher) From aa2f7da6d7e96f1bd795b90e4b41ec66b69aabcb Mon Sep 17 00:00:00 2001 From: Saint Gabriel <53136855+chineduG@users.noreply.github.com> Date: Mon, 6 Jun 2022 20:29:44 +0100 Subject: [PATCH 147/183] Update README.md (dart-lang/path#126) Corrections of misspelled words. --- pkgs/path/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkgs/path/README.md b/pkgs/path/README.md index 395e4966..d0a99111 100644 --- a/pkgs/path/README.md +++ b/pkgs/path/README.md @@ -32,11 +32,10 @@ the path style (POSIX, Windows, or URLs) of the host platform. For example: p.join('directory', 'file.txt'); ``` -This calls the top-level `join()` function to join "directory" and +This calls the top-level `join()` function to join the "directory" and "file.txt" using the current platform's directory separator. -If you want to work with paths for a specific platform regardless of the -underlying platform that the program is running on, you can create a +If you want to work with paths for a specific platform regardless of the underlying platform that the program is running on, you can create a [Context] and give it an explicit [Style]: ```dart @@ -55,8 +54,8 @@ major version would probably cause a lot of versioning pain, so some flexibility is necessary. We try to guarantee that **operations with valid inputs and correct output will -not change**. Operations where one or more inputs are invalid according to the -semantics of the corresponding platform may produce different output over time. +not change**. Operations, where one or more inputs are invalid according to the +semantics of the corresponding platform, may produce different outputs over time. Operations for which `path` produces incorrect output will also change so that we can fix bugs. @@ -118,3 +117,4 @@ We believe this library handles most of the corner cases of Windows paths If you use this package in a browser, then it considers the "platform" to be the browser itself and uses URL strings to represent "browser paths". + From d5a0af5c1d980908b1c75fddc446ebefa6e72cda Mon Sep 17 00:00:00 2001 From: mihiro Date: Tue, 19 Jul 2022 01:51:11 +0900 Subject: [PATCH 148/183] Fix typo (dart-lang/path#127) --- pkgs/path/lib/path.dart | 4 ++-- pkgs/path/lib/src/context.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index a4a8be91..57f87650 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -249,7 +249,7 @@ bool isRootRelative(String path) => context.isRootRelative(path); /// If any part ends in a path separator, then a redundant separator will not /// be added: /// -/// p.join('path/', 'to', 'foo'); // -> 'path/to/foo +/// p.join('path/', 'to', 'foo'); // -> 'path/to/foo' /// /// If a part is an absolute path, then anything before that will be ignored: /// @@ -272,7 +272,7 @@ String join(String part1, /// If any part ends in a path separator, then a redundant separator will not /// be added: /// -/// p.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo +/// p.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo' /// /// If a part is an absolute path, then anything before that will be ignored: /// diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index f0945e4c..45376b68 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -215,7 +215,7 @@ class Context { /// If any part ends in a path separator, then a redundant separator will not /// be added: /// - /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo' /// /// If a part is an absolute path, then anything before that will be ignored: /// @@ -250,7 +250,7 @@ class Context { /// If any part ends in a path separator, then a redundant separator will not /// be added: /// - /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo' /// /// If a part is an absolute path, then anything before that will be ignored: /// From d7052f41b673563710c809dc583a1f96ab67083e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 9 Nov 2022 15:47:48 -0800 Subject: [PATCH 149/183] blast_repo fixes (dart-lang/path#129) Dependabot GitHub Action --- pkgs/path/.github/dependabot.yml | 9 +++++++++ pkgs/path/.github/workflows/dart.yml | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 pkgs/path/.github/dependabot.yml diff --git a/pkgs/path/.github/dependabot.yml b/pkgs/path/.github/dependabot.yml new file mode 100644 index 00000000..1603cdd9 --- /dev/null +++ b/pkgs/path/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Dependabot configuration file. +# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 94b7fc34..224f42bb 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install @@ -44,8 +44,8 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - run: dart pub get From 0e6830dfc0c4271a3dbd64b75a62120962270eb7 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:21:27 -0800 Subject: [PATCH 150/183] Support more arguments in path.join API (dart-lang/path#130) Add additional optional positional arguments to some methods. --- pkgs/path/CHANGELOG.md | 4 ++ pkgs/path/lib/path.dart | 26 ++++++++-- pkgs/path/lib/src/context.dart | 52 +++++++++++++++++--- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/posix_test.dart | 82 ++++++++++++++++++++++++++++++-- pkgs/path/test/url_test.dart | 82 ++++++++++++++++++++++++++++++-- pkgs/path/test/windows_test.dart | 82 ++++++++++++++++++++++++++++++-- 7 files changed, 304 insertions(+), 26 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 2cf8eaea..13c285b5 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.3 + +* Support up to 16 arguments in join function and up to 15 arguments in absolute function. + ## 1.8.2 * Enable the `avoid_dynamic_calls` lint. diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 57f87650..98b63d8c 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -122,8 +122,17 @@ String absolute(String part1, String? part4, String? part5, String? part6, - String? part7]) => - context.absolute(part1, part2, part3, part4, part5, part6, part7); + String? part7, + String? part8, + String? part9, + String? part10, + String? part11, + String? part12, + String? part13, + String? part14, + String? part15]) => + context.absolute(part1, part2, part3, part4, part5, part6, part7, part8, + part9, part10, part11, part12, part13, part14, part15); /// Gets the part of [path] after the last separator. /// @@ -261,8 +270,17 @@ String join(String part1, String? part5, String? part6, String? part7, - String? part8]) => - context.join(part1, part2, part3, part4, part5, part6, part7, part8); + String? part8, + String? part9, + String? part10, + String? part11, + String? part12, + String? part13, + String? part14, + String? part15, + String? part16]) => + context.join(part1, part2, part3, part4, part5, part6, part7, part8, part9, + part10, part11, part12, part13, part14, part15, part16); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 45376b68..3b2cc59f 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -80,9 +80,32 @@ class Context { String? part4, String? part5, String? part6, - String? part7]) { - _validateArgList( - 'absolute', [part1, part2, part3, part4, part5, part6, part7]); + String? part7, + String? part8, + String? part9, + String? part10, + String? part11, + String? part12, + String? part13, + String? part14, + String? part15]) { + _validateArgList('absolute', [ + part1, + part2, + part3, + part4, + part5, + part6, + part7, + part8, + part9, + part10, + part11, + part12, + part13, + part14, + part15 + ]); // If there's a single absolute path, just return it. This is a lot faster // for the common case of `p.absolute(path)`. @@ -90,7 +113,8 @@ class Context { return part1; } - return join(current, part1, part2, part3, part4, part5, part6, part7); + return join(current, part1, part2, part3, part4, part5, part6, part7, part8, + part9, part10, part11, part12, part13, part14, part15); } /// Gets the part of [path] after the last separator on the context's @@ -228,7 +252,15 @@ class Context { String? part5, String? part6, String? part7, - String? part8]) { + String? part8, + String? part9, + String? part10, + String? part11, + String? part12, + String? part13, + String? part14, + String? part15, + String? part16]) { final parts = [ part1, part2, @@ -237,7 +269,15 @@ class Context { part5, part6, part7, - part8 + part8, + part9, + part10, + part11, + part12, + part13, + part14, + part15, + part16, ]; _validateArgList('join', parts); return joinAll(parts.whereType()); diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 78d3cc05..7afd2d35 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.2 +version: 1.8.3 description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index cc64a541..7f1a552f 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -142,7 +142,7 @@ void main() { }); group('join', () { - test('allows up to eight parts', () { + test('allows up to sixteen parts', () { expect(context.join('a'), 'a'); expect(context.join('a', 'b'), 'a/b'); expect(context.join('a', 'b', 'c'), 'a/b/c'); @@ -152,6 +152,33 @@ void main() { expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + 'a/b/c/d/e/f/g/h/i'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + 'a/b/c/d/e/f/g/h/i/j'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + 'a/b/c/d/e/f/g/h/i/j/k'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + 'a/b/c/d/e/f/g/h/i/j/k/l'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p'); }); test('does not add separator if a part ends in one', () { @@ -193,9 +220,28 @@ void main() { }); group('joinAll', () { - test('allows more than eight parts', () { - expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), - 'a/b/c/d/e/f/g/h/i'); + test('allows more than sixteen parts', () { + expect( + context.joinAll([ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q' + ]), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q'); }); test('does not add separator if a part ends in one', () { @@ -493,7 +539,7 @@ void main() { }); group('absolute', () { - test('allows up to seven parts', () { + test('allows up to fifteen parts', () { expect(context.absolute('a'), '/root/path/a'); expect(context.absolute('a', 'b'), '/root/path/a/b'); expect(context.absolute('a', 'b', 'c'), '/root/path/a/b/c'); @@ -503,6 +549,32 @@ void main() { '/root/path/a/b/c/d/e/f'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), '/root/path/a/b/c/d/e/f/g'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + '/root/path/a/b/c/d/e/f/g/h'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + '/root/path/a/b/c/d/e/f/g/h/i'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + '/root/path/a/b/c/d/e/f/g/h/i/j'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + '/root/path/a/b/c/d/e/f/g/h/i/j/k'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + '/root/path/a/b/c/d/e/f/g/h/i/j/k/l'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + '/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n'), + '/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m/n'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o'), + '/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o'); }); test('does not add separator if a part ends in one', () { diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index e0b59030..53a24047 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -195,7 +195,7 @@ void main() { }); group('join', () { - test('allows up to eight parts', () { + test('allows up to sixteen parts', () { expect(context.join('a'), 'a'); expect(context.join('a', 'b'), 'a/b'); expect(context.join('a', 'b', 'c'), 'a/b/c'); @@ -205,6 +205,33 @@ void main() { expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + 'a/b/c/d/e/f/g/h/i'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + 'a/b/c/d/e/f/g/h/i/j'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + 'a/b/c/d/e/f/g/h/i/j/k'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + 'a/b/c/d/e/f/g/h/i/j/k/l'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p'), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p'); }); test('does not add separator if a part ends in one', () { @@ -285,9 +312,28 @@ void main() { }); group('joinAll', () { - test('allows more than eight parts', () { - expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), - 'a/b/c/d/e/f/g/h/i'); + test('allows more than sixteen parts', () { + expect( + context.joinAll([ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q' + ]), + 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q'); }); test('ignores parts before an absolute path', () { @@ -763,7 +809,7 @@ void main() { }); group('absolute', () { - test('allows up to seven parts', () { + test('allows up to fifteen parts', () { expect(context.absolute('a'), 'https://dart.dev/root/path/a'); expect(context.absolute('a', 'b'), 'https://dart.dev/root/path/a/b'); expect( @@ -776,6 +822,32 @@ void main() { 'https://dart.dev/root/path/a/b/c/d/e/f'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'https://dart.dev/root/path/a/b/c/d/e/f/g'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j/k'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j/k/l'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m/n'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o'), + 'https://dart.dev/root/path/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o'); }); test('does not add separator if a part ends in one', () { diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index be9a790f..4036fc66 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -205,7 +205,7 @@ void main() { }); group('join', () { - test('allows up to eight parts', () { + test('allows up to sixteen parts', () { expect(context.join('a'), 'a'); expect(context.join('a', 'b'), r'a\b'); expect(context.join('a', 'b', 'c'), r'a\b\c'); @@ -215,6 +215,33 @@ void main() { expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), r'a\b\c\d\e\f\g\h'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + r'a\b\c\d\e\f\g\h\i'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + r'a\b\c\d\e\f\g\h\i\j'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + r'a\b\c\d\e\f\g\h\i\j\k'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + r'a\b\c\d\e\f\g\h\i\j\k\l'); + expect( + context.join( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + r'a\b\c\d\e\f\g\h\i\j\k\l\m'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n'), + r'a\b\c\d\e\f\g\h\i\j\k\l\m\n'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o'), + r'a\b\c\d\e\f\g\h\i\j\k\l\m\n\o'); + expect( + context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p'), + r'a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p'); }); test('does not add separator if a part ends or begins in one', () { @@ -258,9 +285,28 @@ void main() { }); group('joinAll', () { - test('allows more than eight parts', () { - expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), - r'a\b\c\d\e\f\g\h\i'); + test('allows more than sixteen parts', () { + expect( + context.joinAll([ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q' + ]), + r'a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q'); }); test('does not add separator if a part ends or begins in one', () { @@ -644,7 +690,7 @@ void main() { }); group('absolute', () { - test('allows up to seven parts', () { + test('allows up to fifteen parts', () { expect(context.absolute('a'), r'C:\root\path\a'); expect(context.absolute('a', 'b'), r'C:\root\path\a\b'); expect(context.absolute('a', 'b', 'c'), r'C:\root\path\a\b\c'); @@ -655,6 +701,32 @@ void main() { r'C:\root\path\a\b\c\d\e\f'); expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'C:\root\path\a\b\c\d\e\f\g'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + r'C:\root\path\a\b\c\d\e\f\g\h'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), + r'C:\root\path\a\b\c\d\e\f\g\h\i'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j\k'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j\k\l'); + expect( + context.absolute( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j\k\l\m'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j\k\l\m\n'); + expect( + context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o'), + r'C:\root\path\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o'); }); test('does not add separator if a part ends in one', () { From 8bffec0f5391015f97d64a87b535501c3ac64ca0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:30:43 -0800 Subject: [PATCH 151/183] Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/path#132) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8...755da8c3cf115ac066823e79a1e1788f8940201b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 224f42bb..9a94b668 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From 1bd3f6a15126daf3b3c187dfe2f287d362e8262d Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 9 Jan 2023 15:02:52 -0800 Subject: [PATCH 152/183] Migrate from no-implicit-casts to strict-casts (dart-lang/path#133) --- pkgs/path/analysis_options.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index cc7a1dbc..8b594b4f 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -1,8 +1,8 @@ include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false + language: + strict-casts: true linter: rules: From 6f4249bd0fb9f4035e194099f105353446857d03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 21:54:30 -0800 Subject: [PATCH 153/183] Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/path#135) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.3 to 1.4. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/6a218f2413a3e78e9087f638a238f6b40893203d...a57a6c04cf7d4840e88432aad6281d1e125f0d46) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 9a94b668..fc61a714 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [2.12.0, dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - run: dart pub get From 1049416c81440a6fecc1bb051515dd6a5aba7052 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 23:37:20 -0800 Subject: [PATCH 154/183] Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/path#134) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/755da8c3cf115ac066823e79a1e1788f8940201b...ac593985615ec2ede58e132d2e21d2b1cbd6127c) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index fc61a714..052c9a70 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} From c4ed711fc6108af7e1aebabf896adf7b9b6a9963 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 18 Feb 2023 12:54:56 -0800 Subject: [PATCH 155/183] blast_repo fixes (dart-lang/path#137) auto-publish no-response --- pkgs/path/.github/workflows/no-response.yml | 34 +++++++++++++++++++++ pkgs/path/.github/workflows/publish.yaml | 14 +++++++++ 2 files changed, 48 insertions(+) create mode 100644 pkgs/path/.github/workflows/no-response.yml create mode 100644 pkgs/path/.github/workflows/publish.yaml diff --git a/pkgs/path/.github/workflows/no-response.yml b/pkgs/path/.github/workflows/no-response.yml new file mode 100644 index 00000000..ac3e456e --- /dev/null +++ b/pkgs/path/.github/workflows/no-response.yml @@ -0,0 +1,34 @@ +# A workflow to close issues where the author hasn't responded to a request for +# more information; see https://github.com/godofredoc/no-response for docs. + +name: No Response + +# Both `issue_comment` and `scheduled` event types are required. +on: + issue_comment: + types: [created] + schedule: + # Every day at 8am + - cron: '0 8 * * *' + +# All permissions not specified are set to 'none'. +permissions: + issues: write + +jobs: + noResponse: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'dart-lang' }} + steps: + - uses: godofredoc/no-response@0ce2dc0e63e1c7d2b87752ceed091f6d32c9df09 + with: + responseRequiredLabel: "needs-info" + responseRequiredColor: 4774bc + daysUntilClose: 14 + # Comment to post when closing an Issue for lack of response. + closeComment: > + Without additional information we're not able to resolve this issue, + so it will be closed at this time. You're still free to add more + info and respond to any questions above, though. We'll reopen the + issue if you do. Thanks for your contribution! + token: ${{ github.token }} diff --git a/pkgs/path/.github/workflows/publish.yaml b/pkgs/path/.github/workflows/publish.yaml new file mode 100644 index 00000000..fcb7ccb8 --- /dev/null +++ b/pkgs/path/.github/workflows/publish.yaml @@ -0,0 +1,14 @@ +# A CI configuration to auto-publish pub packages. + +name: Publish + +on: + pull_request: + branches: [ master ] + push: + tags: [ 'v[0-9]+.[0-9]+.[0-9]+*' ] + +jobs: + publish: + if: ${{ github.repository_owner == 'dart-lang' }} + uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main From 2eb37a36a8d03d67c780bc6c4e2178758ccd12fa Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 20 Feb 2023 09:19:03 -0800 Subject: [PATCH 156/183] Move to team lints, require Dart 2.19 (dart-lang/path#138) --- pkgs/path/.github/workflows/dart.yml | 2 +- pkgs/path/CHANGELOG.md | 8 +++-- pkgs/path/analysis_options.yaml | 53 ++++++---------------------- pkgs/path/lib/path.dart | 8 +++-- pkgs/path/lib/src/context.dart | 8 ++--- pkgs/path/lib/src/style/posix.dart | 2 +- pkgs/path/pubspec.yaml | 6 ++-- pkgs/path/test/browser_test.dart | 2 ++ pkgs/path/test/io_test.dart | 2 ++ 9 files changed, 34 insertions(+), 57 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 052c9a70..ffcf55a1 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -42,7 +42,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - sdk: [2.12.0, dev] + sdk: [2.19.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 13c285b5..5b6b0eaa 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.8.4-dev + +* Require Dart 2.19 + ## 1.8.3 * Support up to 16 arguments in join function and up to 15 arguments in absolute function. @@ -5,7 +9,7 @@ ## 1.8.2 * Enable the `avoid_dynamic_calls` lint. -* Popuate the pubspec `repository` field. +* Populate the pubspec `repository` field. ## 1.8.1 @@ -45,7 +49,7 @@ ## 1.6.3 * Don't throw a FileSystemException from `current` if the working directory has - been deleted but we have a cached one we can use. + been deleted, but we have a cached one we can use. ## 1.6.2 diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 8b594b4f..1f2c0242 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:lints/recommended.yaml +include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: language: @@ -6,61 +6,28 @@ analyzer: linter: rules: - - avoid_catching_errors - - avoid_dynamic_calls - - avoid_function_literals_in_foreach_calls - avoid_private_typedef_functions - - avoid_renaming_method_parameters - - avoid_returning_null_for_void - avoid_unused_constructor_parameters - avoid_void_async - - await_only_futures - - camel_case_types - cancel_subscriptions + - collection_methods_unrelated_type + - combinators_ordering - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type + - dangling_library_doc_comments + - implicit_call_tearoffs - join_return_with_assignment - - lines_longer_than_80_chars - - list_remove_unrelated_type + - library_annotations - missing_whitespace_between_adjacent_strings - no_runtimeType_toString - - non_constant_identifier_names - - only_throw_errors - - overridden_fields - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_asserts_in_initializer_lists - prefer_const_constructors - prefer_const_declarations - prefer_expression_function_bodies - prefer_final_locals - - prefer_function_declarations_over_variables - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_interpolation_to_compose_strings - - prefer_is_not_operator - - prefer_null_aware_operators - prefer_relative_imports - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - sort_pub_dependencies - test_types_in_equals - - throw_in_finally - - unnecessary_brace_in_string_interps - - unnecessary_lambdas - - unnecessary_null_aware_assignments - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_string_interpolations + - unnecessary_library_directive + - unreachable_from_main - use_string_buffers - - void_checks + - use_string_in_part_of_directives + - use_super_parameters diff --git a/pkgs/path/lib/path.dart b/pkgs/path/lib/path.dart index 98b63d8c..40f1ea3c 100644 --- a/pkgs/path/lib/path.dart +++ b/pkgs/path/lib/path.dart @@ -27,6 +27,8 @@ /// /// This will join "directory" and "file.txt" using the Windows path separator, /// even when the program is run on a POSIX machine. +library; + import 'src/context.dart'; import 'src/style.dart'; @@ -50,7 +52,7 @@ final Context url = Context(style: Style.url); /// The system path context. /// -/// This differs from a context created with [new Context] in that its +/// This differs from a context created with [Context.new] in that its /// [Context.current] is always the current working directory, rather than being /// set once when the context is created. final Context context = createInternal(); @@ -432,7 +434,7 @@ String setExtension(String path, String extension) => /// If [uri] is relative, a relative path will be returned. /// /// p.fromUri('path/to/foo'); // -> 'path/to/foo' -String fromUri(uri) => context.fromUri(uri); +String fromUri(Object? uri) => context.fromUri(uri!); /// Returns the URI that represents [path]. /// @@ -476,4 +478,4 @@ Uri toUri(String path) => context.toUri(path); /// // URL at "https://dart.dev/root/path" /// p.prettyUri('https://dart.dev/root/path/a/b.dart'); // -> r'a/b.dart' /// p.prettyUri('file:///root/path'); // -> 'file:///root/path' -String prettyUri(uri) => context.prettyUri(uri); +String prettyUri(Object? uri) => context.prettyUri(uri!); diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index 3b2cc59f..cc7dc159 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -1037,7 +1037,7 @@ class Context { /// If [uri] is relative, a relative path will be returned. /// /// path.fromUri('path/to/foo'); // -> 'path/to/foo' - String fromUri(uri) => style.pathFromUri(_parseUri(uri)); + String fromUri(Object? uri) => style.pathFromUri(_parseUri(uri!)); /// Returns the URI that represents [path]. /// @@ -1088,8 +1088,8 @@ class Context { /// context.prettyUri('https://dart.dev/root/path/a/b.dart'); /// // -> r'a/b.dart' /// context.prettyUri('file:///root/path'); // -> 'file:///root/path' - String prettyUri(uri) { - final typedUri = _parseUri(uri); + String prettyUri(Object? uri) { + final typedUri = _parseUri(uri!); if (typedUri.scheme == 'file' && style == Style.url) { return typedUri.toString(); } else if (typedUri.scheme != 'file' && @@ -1113,7 +1113,7 @@ class Context { /// Parses argument if it's a [String] or returns it intact if it's a [Uri]. /// /// Throws an [ArgumentError] otherwise. -Uri _parseUri(uri) { +Uri _parseUri(Object uri) { if (uri is String) return Uri.parse(uri); if (uri is Uri) return uri; throw ArgumentError.value(uri, 'uri', 'Value must be a String or a Uri'); diff --git a/pkgs/path/lib/src/style/posix.dart b/pkgs/path/lib/src/style/posix.dart index 70918655..f88e335c 100644 --- a/pkgs/path/lib/src/style/posix.dart +++ b/pkgs/path/lib/src/style/posix.dart @@ -23,7 +23,7 @@ class PosixStyle extends InternalStyle { @override final rootPattern = RegExp(r'^/'); @override - final relativeRootPattern = null; + Pattern? get relativeRootPattern => null; @override bool containsSeparator(String path) => path.contains('/'); diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 7afd2d35..0723334b 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.3 +version: 1.8.4-dev description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the @@ -7,8 +7,8 @@ description: >- repository: https://github.com/dart-lang/path environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.19.0 <3.0.0" dev_dependencies: - lints: ^1.0.0 + dart_flutter_team_lints: ^0.1.0 test: ^1.16.0 diff --git a/pkgs/path/test/browser_test.dart b/pkgs/path/test/browser_test.dart index 24b36a73..cddd846d 100644 --- a/pkgs/path/test/browser_test.dart +++ b/pkgs/path/test/browser_test.dart @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. @TestOn('browser') +library; + import 'dart:html'; import 'package:path/path.dart' as path; diff --git a/pkgs/path/test/io_test.dart b/pkgs/path/test/io_test.dart index 3737e074..de22caf7 100644 --- a/pkgs/path/test/io_test.dart +++ b/pkgs/path/test/io_test.dart @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. @TestOn('vm') +library; + import 'dart:io' as io; import 'package:path/path.dart' as path; From 165e5ada262f64e682d17eab285dc1356fcb04bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Apr 2023 21:54:00 -0700 Subject: [PATCH 157/183] Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/path#139) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/a57a6c04cf7d4840e88432aad6281d1e125f0d46...d6a63dab3335f427404425de0fbfed4686d93c4f) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index ffcf55a1..a1de13c6 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [2.19.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - run: dart pub get From c6ee49fbb8a2e9269e0d4f470f73cfb7f761d819 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 14:35:16 -0700 Subject: [PATCH 158/183] Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/path#140) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.5.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/ac593985615ec2ede58e132d2e21d2b1cbd6127c...8f4b7f84864484a7bf31766abe9204da3cbe65b3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index a1de13c6..f3da1536 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [2.19.0, dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 292d73c1ea8b31d3d3375e95c15886363798a417 Mon Sep 17 00:00:00 2001 From: Jonathan <75943909+TheUltimateOptimist@users.noreply.github.com> Date: Mon, 1 May 2023 20:58:29 +0200 Subject: [PATCH 159/183] fixed mistake in split method doc comment (dart-lang/path#141) * fixed mistake in split method doc comment The doc comment of the split method in context.dart showed the wrong result for splitting r'\\server\share\path\to\foo' on windows. * Updated CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: Devon Carew --- pkgs/path/CHANGELOG.md | 1 + pkgs/path/lib/src/context.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 5b6b0eaa..0134e946 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.8.4-dev * Require Dart 2.19 +* Fixed an issue with the `split` method doc comment. ## 1.8.3 diff --git a/pkgs/path/lib/src/context.dart b/pkgs/path/lib/src/context.dart index cc7dc159..45233321 100644 --- a/pkgs/path/lib/src/context.dart +++ b/pkgs/path/lib/src/context.dart @@ -356,7 +356,7 @@ class Context { /// // Windows /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] /// context.split(r'\\server\share\path\to\foo'); - /// // -> [r'\\server\share', 'foo', 'bar', 'baz'] + /// // -> [r'\\server\share', 'path', 'to', 'foo'] /// /// // Browser /// context.split('https://dart.dev/path/to/foo'); From c59fa93676ead0cead169c82f05db9398b32447a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 11:58:45 -0700 Subject: [PATCH 160/183] Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/path#142) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8f4b7f84864484a7bf31766abe9204da3cbe65b3...8e5e7e5ab8b370d6c329ec480221332ada57f0ab) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index f3da1536..fdca0eb2 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [2.19.0, dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 099fb5b1f0538be37e631f6f6e071ae9a5fc6d6d Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 10 May 2023 14:28:31 -0700 Subject: [PATCH 161/183] blast_repo fixes (dart-lang/path#144) dependabot --- pkgs/path/.github/dependabot.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/path/.github/dependabot.yml b/pkgs/path/.github/dependabot.yml index 1603cdd9..725f03af 100644 --- a/pkgs/path/.github/dependabot.yml +++ b/pkgs/path/.github/dependabot.yml @@ -3,7 +3,9 @@ version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: github-actions + directory: / schedule: - interval: "monthly" + interval: monthly + labels: + - autosubmit From b62bb6501d724579c6c7620a299544226d70eb77 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 7 Jun 2023 17:31:51 -0700 Subject: [PATCH 162/183] Require Dart 3.0, update lints (dart-lang/path#146) --- pkgs/path/.github/workflows/dart.yml | 2 +- pkgs/path/CHANGELOG.md | 4 ++-- pkgs/path/analysis_options.yaml | 9 +-------- pkgs/path/pubspec.yaml | 6 +++--- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index fdca0eb2..d1d735a3 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -42,7 +42,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - sdk: [2.19.0, dev] + sdk: [3.0.0, dev] steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 0134e946..f1073b0e 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,6 +1,6 @@ -## 1.8.4-dev +## 1.8.4-wip -* Require Dart 2.19 +* Require Dart 3.0 * Fixed an issue with the `split` method doc comment. ## 1.8.3 diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index 1f2c0242..e0c9b27d 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -10,13 +10,8 @@ linter: - avoid_unused_constructor_parameters - avoid_void_async - cancel_subscriptions - - collection_methods_unrelated_type - - combinators_ordering - comment_references - - dangling_library_doc_comments - - implicit_call_tearoffs - join_return_with_assignment - - library_annotations - missing_whitespace_between_adjacent_strings - no_runtimeType_toString - package_api_docs @@ -26,8 +21,6 @@ linter: - prefer_final_locals - prefer_relative_imports - test_types_in_equals - - unnecessary_library_directive - - unreachable_from_main + - unnecessary_breaks - use_string_buffers - - use_string_in_part_of_directives - use_super_parameters diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 0723334b..65ceee56 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.4-dev +version: 1.8.4-wip description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the @@ -7,8 +7,8 @@ description: >- repository: https://github.com/dart-lang/path environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ^3.0.0 dev_dependencies: - dart_flutter_team_lints: ^0.1.0 + dart_flutter_team_lints: ^1.0.0 test: ^1.16.0 From 9bd8dd9b3d087bc84097513e74796d9b9fad210f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 03:33:47 +0000 Subject: [PATCH 163/183] Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/path#147) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3.
Release notes

Sourced from actions/checkout's releases.

v3.5.3

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v3.5.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

v2.3.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.2&new-version=3.5.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index d1d735a3..23375b13 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From f571c4a6c3cf31ac39c1dcc399881b4f08fbd52d Mon Sep 17 00:00:00 2001 From: Danny Tuppeny Date: Wed, 9 Aug 2023 16:55:32 +0100 Subject: [PATCH 164/183] Handle escaped colons in Windows file:// URIs (dart-lang/path#149) Fixes dart-lang/path#148 Using `Uri().toFilePath()` handles escaped colons in drive letters, but `Context.fromUri()` currently does not. Allow the percent encoded colon by checking both formats and adjusting to the correct index for the following character when the percent encoding makes the drive letter longer than the typical two characters. --- pkgs/path/CHANGELOG.md | 3 ++- pkgs/path/lib/src/style/url.dart | 3 +-- pkgs/path/lib/src/utils.dart | 36 ++++++++++++++++++++----- pkgs/path/pubspec.yaml | 2 +- pkgs/path/test/url_test.dart | 15 ++++++++++- pkgs/path/test/windows_test.dart | 46 ++++++++++++++++++++++++++++++-- 6 files changed, 92 insertions(+), 13 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index f1073b0e..88a3d942 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,7 +1,8 @@ -## 1.8.4-wip +## 1.9.0-wip * Require Dart 3.0 * Fixed an issue with the `split` method doc comment. +* Allow percent-encoded colons (`%3a`) in drive letters in `fromUri`. ## 1.8.3 diff --git a/pkgs/path/lib/src/style/url.dart b/pkgs/path/lib/src/style/url.dart index 3ff87ced..a2d3b0ce 100644 --- a/pkgs/path/lib/src/style/url.dart +++ b/pkgs/path/lib/src/style/url.dart @@ -64,8 +64,7 @@ class UrlStyle extends InternalStyle { // See https://url.spec.whatwg.org/#file-slash-state. if (!withDrive || path.length < index + 3) return index; if (!path.startsWith('file://')) return index; - if (!isDriveLetter(path, index + 1)) return index; - return path.length == index + 3 ? index + 3 : index + 4; + return driveLetterEnd(path, index + 1) ?? index; } } diff --git a/pkgs/path/lib/src/utils.dart b/pkgs/path/lib/src/utils.dart index 2443ccd4..7c01312d 100644 --- a/pkgs/path/lib/src/utils.dart +++ b/pkgs/path/lib/src/utils.dart @@ -15,10 +15,34 @@ bool isNumeric(int char) => char >= chars.zero && char <= chars.nine; /// Returns whether [path] has a URL-formatted Windows drive letter beginning at /// [index]. -bool isDriveLetter(String path, int index) { - if (path.length < index + 2) return false; - if (!isAlphabetic(path.codeUnitAt(index))) return false; - if (path.codeUnitAt(index + 1) != chars.colon) return false; - if (path.length == index + 2) return true; - return path.codeUnitAt(index + 2) == chars.slash; +bool isDriveLetter(String path, int index) => + driveLetterEnd(path, index) != null; + +/// Returns the index of the first character after the drive letter or a +/// URL-formatted path, or `null` if [index] is not the start of a drive letter. +/// A valid drive letter must be followed by a colon and then either a `/` or +/// the end of string. +/// +/// ``` +/// d:/abc => 3 +/// d:/ => 3 +/// d: => 2 +/// d => null +/// ``` +int? driveLetterEnd(String path, int index) { + if (path.length < index + 2) return null; + if (!isAlphabetic(path.codeUnitAt(index))) return null; + if (path.codeUnitAt(index + 1) != chars.colon) { + // If not a raw colon, check for escaped colon + if (path.length < index + 4) return null; + if (path.substring(index + 1, index + 4).toLowerCase() != '%3a') { + return null; + } + // Offset the index to account for the extra 2 characters from the + // colon encoding. + index += 2; + } + if (path.length == index + 2) return index + 2; + if (path.codeUnitAt(index + 2) != chars.slash) return null; + return index + 3; } diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 65ceee56..63bbeccf 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.8.4-wip +version: 1.9.0-wip description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 53a24047..8a043dc5 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -301,6 +301,19 @@ void main() { 'file://host/c:/baz/qux'); }); + test( + 'treats drive letters as part of the root for file: URLs ' + 'with encoded colons', () { + expect(context.join('file:///c%3A/foo/bar', '/baz/qux'), + 'file:///c%3A/baz/qux'); + expect(context.join('file:///D%3A/foo/bar', '/baz/qux'), + 'file:///D%3A/baz/qux'); + expect(context.join('file:///c%3A/', '/baz/qux'), 'file:///c%3A/baz/qux'); + expect(context.join('file:///c%3A', '/baz/qux'), 'file:///c%3A/baz/qux'); + expect(context.join('file://host/c%3A/foo/bar', '/baz/qux'), + 'file://host/c%3A/baz/qux'); + }); + test('treats drive letters as normal components for non-file: URLs', () { expect(context.join('http://foo.com/c:/foo/bar', '/baz/qux'), 'http://foo.com/baz/qux'); @@ -884,7 +897,7 @@ void main() { expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); - test('withoutExtension', () { + test('setExtension', () { expect(context.setExtension('', '.x'), '.x'); expect(context.setExtension('a', '.x'), 'a.x'); expect(context.setExtension('.a', '.x'), '.a.x'); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 4036fc66..0bc2d4eb 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:path/path.dart' as path; +import 'package:path/src/utils.dart'; import 'package:test/test.dart'; import 'utils.dart'; @@ -43,7 +44,7 @@ void main() { expect(context.rootPrefix('a'), ''); expect(context.rootPrefix(r'a\b'), ''); expect(context.rootPrefix(r'C:\a\c'), r'C:\'); - expect(context.rootPrefix('C:\\'), r'C:\'); + expect(context.rootPrefix(r'C:\'), r'C:\'); expect(context.rootPrefix('C:/'), 'C:/'); expect(context.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); expect(context.rootPrefix(r'\\server\share'), r'\\server\share'); @@ -761,7 +762,7 @@ void main() { expect(context.withoutExtension(r'a\b.c\'), r'a\b\'); }); - test('withoutExtension', () { + test('setExtension', () { expect(context.setExtension('', '.x'), '.x'); expect(context.setExtension('a', '.x'), 'a.x'); expect(context.setExtension('.a', '.x'), '.a.x'); @@ -784,9 +785,12 @@ void main() { test('with a URI', () { expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('file:///C%3A/path/to/foo')), + r'C:\path\to\foo'); expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), r'\\server\share\path\to\foo'); expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); + expect(context.fromUri(Uri.parse('file:///C%3A/')), r'C:\'); expect( context.fromUri(Uri.parse('file://server/share')), r'\\server\share'); expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); @@ -797,6 +801,8 @@ void main() { r'\\server\share\path\to\foo'); expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), r'C:\path\to\foo#bar'); + expect(context.fromUri(Uri.parse('file:///C%3A/path/to/foo%23bar')), + r'C:\path\to\foo#bar'); expect( context.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), r'\\server\share\path\to\foo#bar'); @@ -809,6 +815,7 @@ void main() { test('with a string', () { expect(context.fromUri('file:///C:/path/to/foo'), r'C:\path\to\foo'); + expect(context.fromUri('file:///C%3A/path/to/foo'), r'C:\path\to\foo'); }); }); @@ -845,6 +852,16 @@ void main() { expect(context.prettyUri('file:///C:/root/other'), r'..\other'); }); + test('with a file: URI with encoded colons', () { + expect(context.prettyUri('file:///C%3A/root/path/a/b'), r'a\b'); + expect(context.prettyUri('file:///C%3A/root/path/a/../b'), r'b'); + expect(context.prettyUri('file:///C%3A/other/path/a/b'), + r'C:\other\path\a\b'); + expect( + context.prettyUri('file:///D%3A/root/path/a/b'), r'D:\root\path\a\b'); + expect(context.prettyUri('file:///C%3A/root/other'), r'..\other'); + }); + test('with an http: URI', () { expect(context.prettyUri('https://dart.dev/a/b'), 'https://dart.dev/a/b'); }); @@ -861,4 +878,29 @@ void main() { expect(context.prettyUri(Uri.parse('a/b')), r'a\b'); }); }); + + test('driveLetterEnd', () { + expect(driveLetterEnd('', 0), null); + expect(driveLetterEnd('foo.dart', 0), null); + expect(driveLetterEnd('@', 0), null); + + expect(driveLetterEnd('c:', 0), 2); + + // colons + expect(driveLetterEnd('c:/', 0), 3); + expect(driveLetterEnd('c:/a', 0), 3); + + // escaped colons lowercase + expect(driveLetterEnd('c%3a/', 0), 5); + expect(driveLetterEnd('c%3a/a', 0), 5); + + // escaped colons uppercase + expect(driveLetterEnd('c%3A/', 0), 5); + expect(driveLetterEnd('c%3A/a', 0), 5); + + // non-drive letter + expect(driveLetterEnd('ab:/c', 0), null); + expect(driveLetterEnd('ab%3a/c', 0), null); + expect(driveLetterEnd('ab%3A/c', 0), null); + }); } From 904ad9ed0ef9a6ae7b10d592f985a5cd077df633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 03:42:51 +0000 Subject: [PATCH 165/183] Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/path#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
Release notes

Sourced from actions/checkout's releases.

v3.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.3...v3.6.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.3&new-version=3.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 23375b13..067ada14 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From b260653daaf78653eb17807bdd59332593ae6918 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 03:22:20 +0000 Subject: [PATCH 166/183] Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/path#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.0 to 1.5.1.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/path#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/path#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.0&new-version=1.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 067ada14..5ad7b1d3 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - run: dart pub get From 969e348a505e17782a793ae90e2ec518629172c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 07:15:01 -0700 Subject: [PATCH 167/183] Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/path#152) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/f43a0e5ff2bd294095638e18286ca9a3d1956744...8ade135a41bc03ea155e62e844d188df1ea18608) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 5ad7b1d3..5adce772 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From 9206565f484b6ab1617a58211f01f60d88302b76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 03:48:28 +0000 Subject: [PATCH 168/183] Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/path#154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
Release notes

Sourced from actions/checkout's releases.

v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.0...v4.1.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 5adce772..e3dce6c9 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From 541f0ccf816190c13f6b39c041679ea02446d779 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 04:01:29 +0000 Subject: [PATCH 169/183] Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/path#153) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.1 to 1.6.0.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/path#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/path#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.1&new-version=1.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index e3dce6c9..5607f4f0 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - run: dart pub get From 484f29d79d5424defc62deb9cf3dbc2bb72402fa Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Tue, 5 Dec 2023 12:53:16 -0800 Subject: [PATCH 170/183] rev the version of lints used; prep for publishing (dart-lang/path#155) rev the version of lints used; prep for publishing --- pkgs/path/CHANGELOG.md | 6 +++--- pkgs/path/analysis_options.yaml | 9 --------- pkgs/path/pubspec.yaml | 4 ++-- pkgs/path/test/posix_test.dart | 2 +- pkgs/path/test/url_test.dart | 2 +- pkgs/path/test/windows_test.dart | 2 +- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 88a3d942..7e77e68c 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,8 +1,8 @@ -## 1.9.0-wip +## 1.9.0 -* Require Dart 3.0 -* Fixed an issue with the `split` method doc comment. * Allow percent-encoded colons (`%3a`) in drive letters in `fromUri`. +* Fixed an issue with the `split` method doc comment. +* Require Dart 3.0 ## 1.8.3 diff --git a/pkgs/path/analysis_options.yaml b/pkgs/path/analysis_options.yaml index e0c9b27d..0b63e947 100644 --- a/pkgs/path/analysis_options.yaml +++ b/pkgs/path/analysis_options.yaml @@ -1,26 +1,17 @@ include: package:dart_flutter_team_lints/analysis_options.yaml -analyzer: - language: - strict-casts: true - linter: rules: - avoid_private_typedef_functions - avoid_unused_constructor_parameters - avoid_void_async - cancel_subscriptions - - comment_references - join_return_with_assignment - missing_whitespace_between_adjacent_strings - no_runtimeType_toString - package_api_docs - - prefer_const_constructors - prefer_const_declarations - prefer_expression_function_bodies - prefer_final_locals - - prefer_relative_imports - - test_types_in_equals - unnecessary_breaks - use_string_buffers - - use_super_parameters diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 63bbeccf..b6e2a915 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.9.0-wip +version: 1.9.0 description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the @@ -10,5 +10,5 @@ environment: sdk: ^3.0.0 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 test: ^1.16.0 diff --git a/pkgs/path/test/posix_test.dart b/pkgs/path/test/posix_test.dart index 7f1a552f..121b4e3c 100644 --- a/pkgs/path/test/posix_test.dart +++ b/pkgs/path/test/posix_test.dart @@ -259,7 +259,7 @@ void main() { group('split', () { test('simple cases', () { - expect(context.split(''), []); + expect(context.split(''), []); expect(context.split('.'), ['.']); expect(context.split('..'), ['..']); expect(context.split('foo'), equals(['foo'])); diff --git a/pkgs/path/test/url_test.dart b/pkgs/path/test/url_test.dart index 8a043dc5..df4e5823 100644 --- a/pkgs/path/test/url_test.dart +++ b/pkgs/path/test/url_test.dart @@ -377,7 +377,7 @@ void main() { group('split', () { test('simple cases', () { - expect(context.split(''), []); + expect(context.split(''), []); expect(context.split('.'), ['.']); expect(context.split('..'), ['..']); expect(context.split('foo'), equals(['foo'])); diff --git a/pkgs/path/test/windows_test.dart b/pkgs/path/test/windows_test.dart index 0bc2d4eb..180f5600 100644 --- a/pkgs/path/test/windows_test.dart +++ b/pkgs/path/test/windows_test.dart @@ -327,7 +327,7 @@ void main() { group('split', () { test('simple cases', () { - expect(context.split(''), []); + expect(context.split(''), []); expect(context.split('.'), ['.']); expect(context.split('..'), ['..']); expect(context.split('foo'), equals(['foo'])); From 31f3e1dc789b12abd38d9aa0582842f4651b29c5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 20 Dec 2023 09:57:01 -0800 Subject: [PATCH 171/183] blast_repo fixes (dart-lang/path#156) no-response --- pkgs/path/.github/workflows/no-response.yml | 35 +++++++++++---------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/pkgs/path/.github/workflows/no-response.yml b/pkgs/path/.github/workflows/no-response.yml index ac3e456e..8e5ed57c 100644 --- a/pkgs/path/.github/workflows/no-response.yml +++ b/pkgs/path/.github/workflows/no-response.yml @@ -1,12 +1,10 @@ # A workflow to close issues where the author hasn't responded to a request for -# more information; see https://github.com/godofredoc/no-response for docs. +# more information; see https://github.com/actions/stale. name: No Response -# Both `issue_comment` and `scheduled` event types are required. +# Run as a daily cron. on: - issue_comment: - types: [created] schedule: # Every day at 8am - cron: '0 8 * * *' @@ -14,21 +12,26 @@ on: # All permissions not specified are set to 'none'. permissions: issues: write + pull-requests: write jobs: - noResponse: + no-response: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'dart-lang' }} steps: - - uses: godofredoc/no-response@0ce2dc0e63e1c7d2b87752ceed091f6d32c9df09 + - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 with: - responseRequiredLabel: "needs-info" - responseRequiredColor: 4774bc - daysUntilClose: 14 - # Comment to post when closing an Issue for lack of response. - closeComment: > - Without additional information we're not able to resolve this issue, - so it will be closed at this time. You're still free to add more - info and respond to any questions above, though. We'll reopen the - issue if you do. Thanks for your contribution! - token: ${{ github.token }} + # Don't automatically mark inactive issues+PRs as stale. + days-before-stale: -1 + # Close needs-info issues and PRs after 14 days of inactivity. + days-before-close: 14 + stale-issue-label: "needs-info" + close-issue-message: > + Without additional information we're not able to resolve this issue. + Feel free to add more info or respond to any questions above and we + can reopen the case. Thanks for your contribution! + stale-pr-label: "needs-info" + close-pr-message: > + Without additional information we're not able to resolve this PR. + Feel free to add more info or respond to any questions above. + Thanks for your contribution! From 115ed35deb879b24807fa9554d2b9c7975401730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 03:19:23 +0000 Subject: [PATCH 172/183] Bump actions/stale from 8.0.0 to 9.0.0 (dart-lang/path#157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/stale](https://github.com/actions/stale) from 8.0.0 to 9.0.0.
Release notes

Sourced from actions/stale's releases.

v9.0.0

Breaking Changes

  1. Action is now stateful: If the action ends because of operations-per-run then the next run will start from the first unprocessed issue skipping the issues processed during the previous run(s). The state is reset when all the issues are processed. This should be considered for scheduling workflow runs.
  2. Version 9 of this action updated the runtime to Node.js 20. All scripts are now run with Node.js 20 instead of Node.js 16 and are affected by any breaking changes between Node.js 16 and 20.

What Else Changed

  1. Performance optimization that removes unnecessary API calls by @​dsame dart-lang/path#1033 fixes dart-lang/path#792
  2. Logs displaying current github API rate limit by @​dsame dart-lang/path#1032 addresses dart-lang/path#1029

For more information, please read the action documentation and its section about statefulness

New Contributors

Full Changelog: https://github.com/actions/stale/compare/v8...v9.0.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/stale&package-manager=github_actions&previous-version=8.0.0&new-version=9.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/no-response.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/path/.github/workflows/no-response.yml b/pkgs/path/.github/workflows/no-response.yml index 8e5ed57c..ab1ac498 100644 --- a/pkgs/path/.github/workflows/no-response.yml +++ b/pkgs/path/.github/workflows/no-response.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.repository_owner == 'dart-lang' }} steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e with: # Don't automatically mark inactive issues+PRs as stale. days-before-stale: -1 From d2eebb69eca3d3e3476483e52059cbe317b74345 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 03:17:34 +0000 Subject: [PATCH 173/183] Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (dart-lang/path#158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.0 to 1.6.2.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/path#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/path#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.6.0&new-version=1.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 5607f4f0..7719ef28 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - run: dart pub get From 16566c157de6e5cacb30612a6995d3c9edce4b8e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 1 Feb 2024 11:55:26 -0800 Subject: [PATCH 174/183] Test on dart2wasm (dart-lang/path#159) --- pkgs/path/.github/workflows/dart.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 7719ef28..5785f7e9 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -50,3 +50,7 @@ jobs: sdk: ${{ matrix.sdk }} - run: dart pub get - run: dart test --platform vm,chrome + - name: Run Chrome tests - wasm + run: dart test --platform chrome --compiler dart2wasm + # TODO: drop `dev` filter when dart2wasm is working on stable + if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' From 07e454f7601de6a537716f32552bdafd33f45d7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 03:52:03 +0000 Subject: [PATCH 175/183] Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/path#160) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
Release notes

Sourced from actions/checkout's releases.

v4.1.2

We are investigating the following issue with this release and have rolled-back the v4 tag to point to v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.1...v4.1.2

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.1&new-version=4.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 5785f7e9..06cde691 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 9f8badbe700c469606d55721160356ee0ed3c69d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 03:46:42 +0000 Subject: [PATCH 176/183] Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/path#161) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.2 to 1.6.4.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.4

  • Rebuild JS code to include changes from v1.6.3

v1.6.3

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.4

  • Rebuild JS code.

v1.6.3

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.6.2&new-version=1.6.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 06cde691..321b4a83 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install @@ -45,7 +45,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - run: dart pub get From 99e6a887550d3ae2091356e2070c05614946f615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 03:49:52 +0000 Subject: [PATCH 177/183] Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/path#162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.4.
Release notes

Sourced from actions/checkout's releases.

v4.1.4

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.3...v4.1.4

v4.1.3

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.2...v4.1.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.2&new-version=4.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 321b4a83..894cec5e 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 027faa0359d1f4f6247ca89847e1775162217457 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Thu, 9 May 2024 13:45:42 -0700 Subject: [PATCH 178/183] blast_repo fixes (dart-lang/path#163) dependabot --- pkgs/path/.github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/path/.github/dependabot.yml b/pkgs/path/.github/dependabot.yml index 725f03af..cde02ad6 100644 --- a/pkgs/path/.github/dependabot.yml +++ b/pkgs/path/.github/dependabot.yml @@ -9,3 +9,7 @@ updates: interval: monthly labels: - autosubmit + groups: + github-actions: + patterns: + - "*" From 314385305f3f42941eeee4e1a3106a3de4087708 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 21:06:52 +0000 Subject: [PATCH 179/183] Bump actions/checkout from 4.1.4 to 4.1.5 in the github-actions group (dart-lang/path#164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.4 to 4.1.5
Release notes

Sourced from actions/checkout's releases.

v4.1.5

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.4...v4.1.5

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.4&new-version=4.1.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 894cec5e..1e8fa056 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 137bd5d7bff5288b2507690bed82ac1af4407033 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Tue, 21 May 2024 12:24:31 +0200 Subject: [PATCH 180/183] Add `topics` to `pubspec.yaml` (dart-lang/path#165) --- pkgs/path/pubspec.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index b6e2a915..91bbca2c 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -6,6 +6,9 @@ description: >- web. repository: https://github.com/dart-lang/path +topics: + - file-system + environment: sdk: ^3.0.0 From c8ff1d5df13deab5b3548109949fcd9aea772795 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 03:16:18 +0000 Subject: [PATCH 181/183] Bump actions/checkout from 4.1.5 to 4.1.6 in the github-actions group (dart-lang/path#167) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.5 to 4.1.6
Release notes

Sourced from actions/checkout's releases.

v4.1.6

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.5...v4.1.6

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.5&new-version=4.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/path/.github/workflows/dart.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index 1e8fa056..a8c87c6e 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -44,7 +44,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From bb41c3401908be0a1b14330de2812db4ec32299b Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Jun 2024 09:30:49 -0700 Subject: [PATCH 182/183] Update to latest lints, bump min SDK to 3.4, test wasm on 3.4 (dart-lang/path#168) --- pkgs/path/.github/workflows/dart.yml | 5 ++--- pkgs/path/CHANGELOG.md | 4 ++++ pkgs/path/pubspec.yaml | 8 ++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index a8c87c6e..d66dbe8a 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -42,7 +42,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - sdk: [3.0.0, dev] + sdk: [3.4, dev] steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 @@ -52,5 +52,4 @@ jobs: - run: dart test --platform vm,chrome - name: Run Chrome tests - wasm run: dart test --platform chrome --compiler dart2wasm - # TODO: drop `dev` filter when dart2wasm is working on stable - if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/path/CHANGELOG.md b/pkgs/path/CHANGELOG.md index 7e77e68c..c8b0057f 100644 --- a/pkgs/path/CHANGELOG.md +++ b/pkgs/path/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.9.1-wip + +- Require Dart 3.4 + ## 1.9.0 * Allow percent-encoded colons (`%3a`) in drive letters in `fromUri`. diff --git a/pkgs/path/pubspec.yaml b/pkgs/path/pubspec.yaml index 91bbca2c..a7bdeeb6 100644 --- a/pkgs/path/pubspec.yaml +++ b/pkgs/path/pubspec.yaml @@ -1,5 +1,5 @@ name: path -version: 1.9.0 +version: 1.9.1-wip description: >- A string-based path manipulation library. All of the path operations you know and love, with solid support for Windows, POSIX (Linux and Mac OS X), and the @@ -10,8 +10,8 @@ topics: - file-system environment: - sdk: ^3.0.0 + sdk: ^3.4.0 dev_dependencies: - dart_flutter_team_lints: ^2.0.0 - test: ^1.16.0 + dart_flutter_team_lints: ^3.0.0 + test: ^1.16.6 From 209875579222aee0e23ea242173b62192930fb55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 03:43:46 +0000 Subject: [PATCH 183/183] Bump the github-actions group with 2 updates (dart-lang/path#169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart). Updates `actions/checkout` from 4.1.6 to 4.1.7
Release notes

Sourced from actions/checkout's releases.

v4.1.7

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.6...v4.1.7

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

... (truncated)

Commits

Updates `dart-lang/setup-dart` from 1.6.4 to 1.6.5
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.5

dart-lang/path#118: dart-lang/setup-dartdart-lang/path#118

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.5

dart-lang/path#118: dart-lang/setup-dartdart-lang/path#118

v1.6.4

  • Rebuild JS code.

v1.6.3

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

... (truncated)

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/path/.github/workflows/dart.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/path/.github/workflows/dart.yml b/pkgs/path/.github/workflows/dart.yml index d66dbe8a..56621b75 100644 --- a/pkgs/path/.github/workflows/dart.yml +++ b/pkgs/path/.github/workflows/dart.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - id: install @@ -44,8 +44,8 @@ jobs: os: [ubuntu-latest] sdk: [3.4, dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - run: dart pub get