1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2025-12-28 02:02:28 +01:00

Asyncify File object.

This commit is contained in:
Kris Maglione
2015-03-03 19:59:05 -08:00
parent 29e614ab74
commit 313fa1c333

View File

@@ -5,7 +5,7 @@
"use strict";
defineModule("storage", {
exports: ["File", "Storage", "storage"],
exports: ["AsyncFile", "File", "Storage", "storage"],
require: ["promises", "services", "util"]
});
@@ -398,36 +398,62 @@ var Storage = Module("Storage", {
* @param {string} charset The charset of the file. @default File.defaultEncoding
*/
var File = Class("File", {
init: function (path, checkPWD, charset) {
let file = services.File();
init: function init(path, checkPWD, charset) {
if (charset)
this.charset = charset;
if (isString(path))
path = File.expandPath(path);
this._path = path;
this._checkPWD = checkPWD;
return this;
},
get path() {
if (!this.fileified && isString(this._path) && File.isAbsolutePath(this._path))
return this._path;
return this.file.path;
},
set path(path) {
return this.file.path = path;
},
file: Class.Memoize(function () {
let path = this._path;
let file = services.File();
if (path instanceof Ci.nsIFileURL)
path = path.file;
if (path instanceof Ci.nsIFile || path instanceof File)
file = path.clone();
else if (/file:\/\//.test(path))
else if (path.startsWith("file://"))
file = services["file:"].getFileFromURLSpec(path);
else {
try {
let expandedPath = File.expandPath(path);
let checkPWD = this._checkPWD;
if (!File.isAbsolutePath(path) && !(checkPWD instanceof Ci.nsIFile))
checkPWD = File(services.directory.get("CurWorkD", Ci.nsIFile));
if (!File.isAbsolutePath(expandedPath) && checkPWD)
file = checkPWD.child(expandedPath);
if (!File.isAbsolutePath(path))
file = checkPWD.child(path);
else
file.initWithPath(expandedPath);
file.initWithPath(path);
}
catch (e) {
util.reportError(e);
return File.DoesNotExist(path, e);
}
}
this.file = file.QueryInterface(Ci.nsILocalFile);
return this;
},
this.fileified = true;
return file.QueryInterface(Ci.nsILocalFile);
}),
get async() AsyncFile(this),
charset: Class.Memoize(() => File.defaultEncoding),
@@ -450,20 +476,28 @@ var File = Class("File", {
if (!this.isDirectory())
throw Error(_("io.eNotDir"));
for (let file of iter(this.directoryEntries))
yield File(file);
yield this.constructor(file);
},
/**
* Returns a new file for the given child of this directory entry.
*/
child: function child(...args) {
let f = this.constructor(this);
for (let name of args)
for (let elem of name.split(File.pathSplit))
f.append(elem);
return f;
let path = this.path;
args = args.map(p => p.replace(File.pathSplit, File.PATH_SEP));
util.assert(!args.some(File.isAbsolutePath),
"Absolute paths not allowed", false);
let newPath = OS.Path.join.apply(OS.Path, [this.path].concat(args));
return this.constructor(newPath, null, this.charset);
},
get fileName() OS.Path.basename(this.path),
get parent() this.constructor(OS.Path.dirname(this.path), null, this.charset),
/**
* Returns an iterator for all lines in a file.
*/
@@ -507,7 +541,7 @@ var File = Class("File", {
*
* @returns {nsIFileURL}
*/
toURI: function toURI() services.io.newFileURI(this.file),
toURI: deprecated("#URI", function toURI() services.io.newFileURI(this.file)),
/**
* Writes the string *buf* to this file.
@@ -657,15 +691,12 @@ var File = Class("File", {
/**
* @property {string} The current platform's path separator.
*/
PATH_SEP: Class.Memoize(function () {
let f = services.directory.get("CurProcD", Ci.nsIFile);
f.append("foo");
return f.path.substr(f.parent.path.length, 1);
}),
PATH_SEP: Class.Memoize(function () /foo(.)bar/.exec(OS.Path.join("foo", "bar"))[1]),
pathSplit: Class.Memoize(function () util.regexp("(?:/|" + util.regexp.escape(this.PATH_SEP) + ")", "g")),
pathSplit: Class.Memoize(function () util.regexp("[/" + util.regexp.escape(this.PATH_SEP) + "]", "g")),
DoesNotExist: function DoesNotExist(path, error) ({
__proto__: DoesNotExist.prototype,
path: path,
exists: function () false,
__noSuchMethod__: function () { throw error || Error("Does not exist"); }
@@ -695,6 +726,7 @@ var File = Class("File", {
// TODO: Vim does not expand variables set to an empty string (and documents it).
// Kris reckons we shouldn't replicate this 'bug'. --djk
// TODO: should we be doing this for all paths?
// No.
function expand(path) path.replace(
win32 ? /\$(\w+)\b|\${(\w+)}|%(\w+)%/g
: /\$(\w+)\b|\${(\w+)}/g,
@@ -702,23 +734,11 @@ var File = Class("File", {
path = expand(path);
// expand ~
// Yuck.
if (!relative && RegExp("~(?:$|[/" + util.regexp.escape(File.PATH_SEP) + "])").test(path)) {
// Try $HOME first, on all systems
let home = getenv("HOME");
if (!relative && RegExp("~(?:$|[/" + util.regexp.escape(File.PATH_SEP) + "])").test(path))
path = OS.Path.join(OS.Constants.Path.homeDir,
path.substr(2));
// Windows has its own idiosyncratic $HOME variables.
if (win32 && (!home || !File(home).exists()))
home = getenv("USERPROFILE") ||
getenv("HOMEDRIVE") + getenv("HOMEPATH");
path = home + path.substr(1);
}
// TODO: Vim expands paths twice, once before checking for ~, once
// after, but doesn't document it. Is this just a bug? --Kris
path = expand(path);
return path.replace("/", File.PATH_SEP, "g");
return OS.Path.normalize(path.replace("/", File.PATH_SEP, "g"));
},
expandPathList: function (list) list.map(this.expandPath),
@@ -765,14 +785,15 @@ var File = Class("File", {
isAbsolutePath: function isAbsolutePath(path) {
try {
services.File().initWithPath(path);
return true;
return OS.Path.split(path).absolute;
}
catch (e) {
return false;
}
},
replacePathSep: function replacePathSep(path) path.split("/").join(File.PATH_SEP),
joinPaths: function joinPaths(head, tail, cwd) {
let path = this(head, cwd);
try {
@@ -784,12 +805,10 @@ var File = Class("File", {
}
return path;
},
replacePathSep: function (path) path.replace("/", File.PATH_SEP, "g")
});
{
let file = services.directory.get("ProfD", Ci.nsIFile);
let file = services.File();
Object.keys(file).forEach(function (prop) {
if (!(prop in File.prototype)) {
let isFunction;
@@ -811,6 +830,119 @@ var File = Class("File", {
file = null;
}
var AsyncFile = Class("AsyncFile", File, {
get async() this,
/*
* Creates a new directory, along with any parent directories which
* do not currently exist.
*
* @param {string} path The path of the directory to create.
* @param {object} options Options for directory creation. As in
* `OS.File.makeDir`
* @returns {Promise}
*/
mkdir: promises.task(function* mkdir(path, options) {
let split = OS.Path.split(path);
util.assert(split.absolute);
let file = File(split.winDrive ? split.winDrive + File.PATH_SEP
: File.PATH_SEP);
for (let component of split.components) {
let f = file.child(component);
try {
var stat = yield OS.File.stat(f.path);
}
catch (e) {
// Does not exist, or other error.
break;
}
if (!stat.isDir)
throw new Error("Component in path is not a directory");
file = f;
}
options = update({},
options || {},
{ ignoreExisting: true,
from: file.path });
yield OS.File.makeDir(path, options);
}),
/**
* Iterates over the objects in this directory.
*/
iterDirectory: function* iterDirectory() {
if (!this.exists())
throw Error(_("io.noSuchFile"));
if (!this.isDirectory())
throw Error(_("io.eNotDir"));
for (let file of iter(this.directoryEntries))
yield File(file);
},
/**
* Returns an iterator for all lines in a file.
*/
get lines() File.readLines(services.FileInStream(this.file, -1, 0, 0),
this.charset),
_setEncoding: function _setEncoding(options) {
if (this.encoding != null && !("encoding" in options))
options = update({}, options,
{ encoding: this.encoding });
return options;
},
/**
* Reads this file's entire contents in "text" mode and returns the
* content as a string.
*/
read: function read(options={}) {
return OS.File.read(this.path, this._setEncoding(options));
},
/**
* Returns the list of files in this directory.
*/
readDirectory: function readDirectory(callback) {
let iter = new OS.File.DirectoryIterator(dir);
let close = () => { iter.close() };
return iter.forEach(callback)
.then(close, close);
},
/**
* Writes the string *buf* to this file.
*/
write: function write(buf, options={}) {
return OS.File.writeAtomic(this.path, this._setEncoding(options));
},
copyTo: function copyTo(path, options) {
return OS.File.copy(this.path, path, options);
},
moveTo: function moveTo(path, options) {
return OS.File.move(this.path, path, options);
},
});
for (let m of ["makeDir",
"stat",
"remove",
"removeDir",
"removeEmptyDir"]) {
let method = m;
AsyncFile.prototype[method] = function (options) {
return OS.File[method](this.path, options);
};
}
endModule();
// catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) }