mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-02-01 14:55:47 +01:00
Asyncify File object.
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
defineModule("storage", {
|
defineModule("storage", {
|
||||||
exports: ["File", "Storage", "storage"],
|
exports: ["AsyncFile", "File", "Storage", "storage"],
|
||||||
require: ["promises", "services", "util"]
|
require: ["promises", "services", "util"]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -398,36 +398,62 @@ var Storage = Module("Storage", {
|
|||||||
* @param {string} charset The charset of the file. @default File.defaultEncoding
|
* @param {string} charset The charset of the file. @default File.defaultEncoding
|
||||||
*/
|
*/
|
||||||
var File = Class("File", {
|
var File = Class("File", {
|
||||||
init: function (path, checkPWD, charset) {
|
init: function init(path, checkPWD, charset) {
|
||||||
let file = services.File();
|
|
||||||
|
|
||||||
if (charset)
|
if (charset)
|
||||||
this.charset = 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)
|
if (path instanceof Ci.nsIFileURL)
|
||||||
path = path.file;
|
path = path.file;
|
||||||
|
|
||||||
if (path instanceof Ci.nsIFile || path instanceof File)
|
if (path instanceof Ci.nsIFile || path instanceof File)
|
||||||
file = path.clone();
|
file = path.clone();
|
||||||
else if (/file:\/\//.test(path))
|
else if (path.startsWith("file://"))
|
||||||
file = services["file:"].getFileFromURLSpec(path);
|
file = services["file:"].getFileFromURLSpec(path);
|
||||||
else {
|
else {
|
||||||
try {
|
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)
|
if (!File.isAbsolutePath(path))
|
||||||
file = checkPWD.child(expandedPath);
|
file = checkPWD.child(path);
|
||||||
else
|
else
|
||||||
file.initWithPath(expandedPath);
|
file.initWithPath(path);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
util.reportError(e);
|
util.reportError(e);
|
||||||
return File.DoesNotExist(path, 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),
|
charset: Class.Memoize(() => File.defaultEncoding),
|
||||||
|
|
||||||
@@ -450,20 +476,28 @@ var File = Class("File", {
|
|||||||
if (!this.isDirectory())
|
if (!this.isDirectory())
|
||||||
throw Error(_("io.eNotDir"));
|
throw Error(_("io.eNotDir"));
|
||||||
for (let file of iter(this.directoryEntries))
|
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.
|
* Returns a new file for the given child of this directory entry.
|
||||||
*/
|
*/
|
||||||
child: function child(...args) {
|
child: function child(...args) {
|
||||||
let f = this.constructor(this);
|
let path = this.path;
|
||||||
for (let name of args)
|
|
||||||
for (let elem of name.split(File.pathSplit))
|
args = args.map(p => p.replace(File.pathSplit, File.PATH_SEP));
|
||||||
f.append(elem);
|
util.assert(!args.some(File.isAbsolutePath),
|
||||||
return f;
|
"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.
|
* Returns an iterator for all lines in a file.
|
||||||
*/
|
*/
|
||||||
@@ -507,7 +541,7 @@ var File = Class("File", {
|
|||||||
*
|
*
|
||||||
* @returns {nsIFileURL}
|
* @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.
|
* Writes the string *buf* to this file.
|
||||||
@@ -657,15 +691,12 @@ var File = Class("File", {
|
|||||||
/**
|
/**
|
||||||
* @property {string} The current platform's path separator.
|
* @property {string} The current platform's path separator.
|
||||||
*/
|
*/
|
||||||
PATH_SEP: Class.Memoize(function () {
|
PATH_SEP: Class.Memoize(function () /foo(.)bar/.exec(OS.Path.join("foo", "bar"))[1]),
|
||||||
let f = services.directory.get("CurProcD", Ci.nsIFile);
|
|
||||||
f.append("foo");
|
|
||||||
return f.path.substr(f.parent.path.length, 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) ({
|
DoesNotExist: function DoesNotExist(path, error) ({
|
||||||
|
__proto__: DoesNotExist.prototype,
|
||||||
path: path,
|
path: path,
|
||||||
exists: function () false,
|
exists: function () false,
|
||||||
__noSuchMethod__: function () { throw error || Error("Does not exist"); }
|
__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).
|
// TODO: Vim does not expand variables set to an empty string (and documents it).
|
||||||
// Kris reckons we shouldn't replicate this 'bug'. --djk
|
// Kris reckons we shouldn't replicate this 'bug'. --djk
|
||||||
// TODO: should we be doing this for all paths?
|
// TODO: should we be doing this for all paths?
|
||||||
|
// No.
|
||||||
function expand(path) path.replace(
|
function expand(path) path.replace(
|
||||||
win32 ? /\$(\w+)\b|\${(\w+)}|%(\w+)%/g
|
win32 ? /\$(\w+)\b|\${(\w+)}|%(\w+)%/g
|
||||||
: /\$(\w+)\b|\${(\w+)}/g,
|
: /\$(\w+)\b|\${(\w+)}/g,
|
||||||
@@ -702,23 +734,11 @@ var File = Class("File", {
|
|||||||
path = expand(path);
|
path = expand(path);
|
||||||
|
|
||||||
// expand ~
|
// expand ~
|
||||||
// Yuck.
|
if (!relative && RegExp("~(?:$|[/" + util.regexp.escape(File.PATH_SEP) + "])").test(path))
|
||||||
if (!relative && RegExp("~(?:$|[/" + util.regexp.escape(File.PATH_SEP) + "])").test(path)) {
|
path = OS.Path.join(OS.Constants.Path.homeDir,
|
||||||
// Try $HOME first, on all systems
|
path.substr(2));
|
||||||
let home = getenv("HOME");
|
|
||||||
|
|
||||||
// Windows has its own idiosyncratic $HOME variables.
|
return OS.Path.normalize(path.replace("/", File.PATH_SEP, "g"));
|
||||||
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");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
expandPathList: function (list) list.map(this.expandPath),
|
expandPathList: function (list) list.map(this.expandPath),
|
||||||
@@ -765,14 +785,15 @@ var File = Class("File", {
|
|||||||
|
|
||||||
isAbsolutePath: function isAbsolutePath(path) {
|
isAbsolutePath: function isAbsolutePath(path) {
|
||||||
try {
|
try {
|
||||||
services.File().initWithPath(path);
|
return OS.Path.split(path).absolute;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
replacePathSep: function replacePathSep(path) path.split("/").join(File.PATH_SEP),
|
||||||
|
|
||||||
joinPaths: function joinPaths(head, tail, cwd) {
|
joinPaths: function joinPaths(head, tail, cwd) {
|
||||||
let path = this(head, cwd);
|
let path = this(head, cwd);
|
||||||
try {
|
try {
|
||||||
@@ -784,12 +805,10 @@ var File = Class("File", {
|
|||||||
}
|
}
|
||||||
return path;
|
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) {
|
Object.keys(file).forEach(function (prop) {
|
||||||
if (!(prop in File.prototype)) {
|
if (!(prop in File.prototype)) {
|
||||||
let isFunction;
|
let isFunction;
|
||||||
@@ -811,6 +830,119 @@ var File = Class("File", {
|
|||||||
file = null;
|
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();
|
endModule();
|
||||||
|
|
||||||
// catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) }
|
// catch(e){ dump(e + "\n" + (e.stack || Error().stack)); Components.utils.reportError(e) }
|
||||||
|
|||||||
Reference in New Issue
Block a user