]> git.donarmstrong.com Git - dactyl.git/blobdiff - common/modules/io.jsm
Import r6976 from upstream hg supporting Firefox up to 25.*
[dactyl.git] / common / modules / io.jsm
index 5eefcc17b6bb6aeaceecae247a49df9035a36846..4a61d67313b74d7e1c3a2092a44ef7ea027c1125 100644 (file)
@@ -1,22 +1,24 @@
 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
-// Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
-// Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
+// Copyright (c) 2007-2012 by Doug Kearns <dougkearns@gmail.com>
+// Copyright (c) 2008-2013 Kris Maglione <maglione.k@gmail.com>
 // Some code based on Venkman
 //
 // This work is licensed for reuse under an MIT license. Details are
 // given in the LICENSE.txt file included with this file.
-/* use strict */
+"use strict";
 
 try {
 
-Components.utils.import("resource://dactyl/bootstrap.jsm");
 defineModule("io", {
     exports: ["IO", "io"],
     require: ["services"]
-}, this);
+});
 
-this.lazyRequire("config", ["config"]);
-this.lazyRequire("contexts", ["Contexts", "contexts"]);
+lazyRequire("config", ["config"]);
+lazyRequire("contexts", ["Contexts", "contexts"]);
+lazyRequire("storage", ["File", "storage"]);
+lazyRequire("styles", ["styles"]);
+lazyRequire("template", ["template"]);
 
 // TODO: why are we passing around strings rather than file objects?
 /**
@@ -41,26 +43,6 @@ var IO = Module("io", {
 
             this._lastRunCommand = ""; // updated whenever the users runs a command with :!
             this._scriptNames = [];
-
-            this.downloadListener = {
-                onDownloadStateChange: function (state, download) {
-                    if (download.state == services.downloadManager.DOWNLOAD_FINISHED) {
-                        let url   = download.source.spec;
-                        let title = download.displayName;
-                        let file  = download.targetFile.path;
-                        let size  = download.size;
-
-                        dactyl.echomsg({ domains: [util.getHost(url)], message: _("io.downloadFinished", title, file) },
-                                       1, modules.commandline.ACTIVE_WINDOW);
-                        modules.autocommands.trigger("DownloadPost", { url: url, title: title, file: file, size: size });
-                    }
-                },
-                onStateChange:    function () {},
-                onProgressChange: function () {},
-                onSecurityChange: function () {}
-            };
-
-            services.downloadManager.addListener(this.downloadListener);
         },
 
         CommandFileMode: Class("CommandFileMode", modules.CommandMode, {
@@ -84,10 +66,6 @@ var IO = Module("io", {
             }
         }),
 
-        destroy: function destroy() {
-            services.downloadManager.removeListener(this.downloadListener);
-        },
-
         /**
          * Returns all directories named *name* in 'runtimepath'.
          *
@@ -96,8 +74,8 @@ var IO = Module("io", {
          */
         getRuntimeDirectories: function getRuntimeDirectories(name) {
             return modules.options.get("runtimepath").files
-                .map(function (dir) dir.child(name))
-                .filter(function (dir) dir.exists() && dir.isDirectory() && dir.isReadable());
+                .map(dir => dir.child(name))
+                .filter(dir => (dir.exists() && dir.isDirectory() && dir.isReadable()));
         },
 
         // FIXME: multiple paths?
@@ -171,9 +149,9 @@ var IO = Module("io", {
                     let sourceJSM = function sourceJSM() {
                         context = contexts.Module(uri);
                         dactyl.triggerObserver("io.source", context, file, file.lastModifiedTime);
-                    }
+                    };
 
-                    if (/\.js,$/.test(filename))
+                    if (/\.jsm$/.test(filename))
                         sourceJSM();
                     else if (/\.js$/.test(filename)) {
                         try {
@@ -190,12 +168,14 @@ var IO = Module("io", {
                                 sourceJSM();
                             }
                             else {
+                                if (e instanceof Finished)
+                                    return;
                                 if (e.fileName && !(e instanceof FailedAssertion))
                                     try {
                                         e.fileName = util.fixURI(e.fileName);
                                         if (e.fileName == uri.spec)
                                             e.fileName = filename;
-                                        e.echoerr = <>{e.fileName}:{e.lineNumber}: {e}</>;
+                                        e.echoerr = [e.fileName, ":", e.lineNumber, ": ", e].join("");
                                     }
                                     catch (e) {}
                                 throw e;
@@ -261,11 +241,13 @@ var IO = Module("io", {
     /**
      * Sets the current working directory.
      *
-     * @param {string} newDir The new CWD. This may be a relative or
+     * @param {File|string} newDir The new CWD. This may be a relative or
      *     absolute path and is expanded by {@link #expandPath}.
+     *     @optional
+     *     @default = "~"
      */
-    set cwd(newDir) {
-        newDir = newDir && newDir.path || newDir || "~";
+    set cwd(newDir = "~") {
+        newDir = newDir.path || newDir;
 
         if (newDir == "-") {
             util.assert(this._oldcwd != null, _("io.noPrevDir"));
@@ -286,8 +268,8 @@ var IO = Module("io", {
      */
     File: Class.Memoize(function () let (io = this)
         Class("File", File, {
-            init: function init(path, checkCWD)
-                init.supercall(this, path, (arguments.length < 2 || checkCWD) && io.cwd)
+            init: function init(path, checkCWD=true)
+                init.supercall(this, path, checkCWD && io.cwd)
         })),
 
     /**
@@ -327,18 +309,20 @@ var IO = Module("io", {
     },
 
     /**
-     * Creates a temporary file.
+     * Returns a temporary file.
      *
+     * @param {string} ext The filename extension.
+     *     @default "txt"
+     * @param {string} label A metadata string appended to the filename. Useful
+     *     for identifying the file, beyond its extension, to external
+     *     applications.
+     *     @default ""
      * @returns {File}
      */
-    createTempFile: function createTempFile(name) {
-        if (name instanceof Ci.nsIFile)
-            var file = name.clone();
-        else {
-            file = services.directory.get("TmpD", Ci.nsIFile);
-            file.append(this.config.tempFile + (name ? "." + name : ""));
-        }
-        file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(600));
+    createTempFile: function createTempFile(ext = "txt", label = "") {
+        let file = services.directory.get("TmpD", Ci.nsIFile);
+        file.append(config.name + label + "." + ext);
+        file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, octal(666));
 
         services.externalApp.deleteTemporaryFileOnExit(file);
 
@@ -378,7 +362,7 @@ var IO = Module("io", {
         file = util.getFile(file);
         if (file && file.exists() && file.isFile() && file.isReadable()) {
             // let jar = services.zipReader.getZip(file); Crashes.
-            let jar = services.ZipReader(file);
+            let jar = services.ZipReader(file.file);
             try {
                 let filter = RegExp("^" + util.regexp.escape(decodeURI(path))
                                     + "[^/]*/?$");
@@ -414,7 +398,8 @@ var IO = Module("io", {
         if (bin instanceof File || File.isAbsolutePath(bin))
             return this.File(bin);
 
-        let dirs = services.environment.get("PATH").split(config.OS.isWindows ? ";" : ":");
+        let dirs = services.environment.get("PATH")
+                           .split(config.OS.pathListSep);
         // Windows tries the CWD first TODO: desirable?
         if (config.OS.isWindows)
             dirs = [io.cwd].concat(dirs);
@@ -460,7 +445,7 @@ var IO = Module("io", {
             return -1;
         }
 
-        let process = services.Process(file);
+        let process = services.Process(file.file);
         process.run(false, args.map(String), args.length);
         try {
             if (callable(blocking))
@@ -487,10 +472,12 @@ var IO = Module("io", {
     // TODO: when https://bugzilla.mozilla.org/show_bug.cgi?id=68702 is
     // fixed use that instead of a tmpfile
     /**
-     * Runs *command* in a subshell and returns the output in a string. The
-     * shell used is that specified by the 'shell' option.
+     * Runs *command* in a subshell and returns the output. The shell used is
+     * that specified by the 'shell' option.
      *
-     * @param {string} command The command to run.
+     * @param {string|[string]} command The command to run. This can be a shell
+     *      command string or an array of strings (a command and arguments)
+     *      which will be escaped and concatenated.
      * @param {string} input Any input to be provided to the command on stdin.
      * @param {function(object)} callback A callback to be called when
      *      the command completes. @optional
@@ -517,7 +504,9 @@ var IO = Module("io", {
 
             function async(status) {
                 let output = stdout.read();
-                [stdin, stdout, cmd].forEach(function (f) f.exists() && f.remove(false));
+                for (let f of [stdin, stdout, cmd])
+                    if (f.exists())
+                        f.remove(false);
                 callback(result(status, output));
             }
 
@@ -555,8 +544,9 @@ var IO = Module("io", {
      * @returns {boolean} false if temp files couldn't be created,
      *     otherwise, the return value of *func*.
      */
-    withTempFiles: function withTempFiles(func, self, checked, ext) {
-        let args = array(util.range(0, func.length)).map(bind("createTempFile", this, ext)).array;
+    withTempFiles: function withTempFiles(func, self, checked, ext, label) {
+        let args = array(util.range(0, func.length))
+                    .map(bind("createTempFile", this, ext, label)).array;
         try {
             if (!args.every(util.identity))
                 return false;
@@ -564,7 +554,7 @@ var IO = Module("io", {
         }
         finally {
             if (!checked || res !== true)
-                args.forEach(function (f) f.remove(false));
+                args.forEach(f => { f.remove(false); });
         }
         return res;
     }
@@ -588,7 +578,7 @@ var IO = Module("io", {
      */
     PATH_SEP: deprecated("File.PATH_SEP", { get: function PATH_SEP() File.PATH_SEP })
 }, {
-    commands: function init_commands(dactyl, modules, window) {
+    commands: function initCommands(dactyl, modules, window) {
         const { commands, completion, io } = modules;
 
         commands.add(["cd", "chd[ir]"],
@@ -650,7 +640,8 @@ var IO = Module("io", {
                 lines.push("\n\" vim: set ft=" + config.name + ":");
 
                 try {
-                    file.write(lines.join("\n"));
+                    file.write(lines.join("\n").concat("\n"));
+                    dactyl.echomsg(_("io.writing", file.path.quote()), 2);
                 }
                 catch (e) {
                     dactyl.echoerr(_("io.notWriteable", file.path.quote()));
@@ -662,24 +653,65 @@ var IO = Module("io", {
                 completer: function (context) completion.file(context, true)
             });
 
-        commands.add(["mks[yntax]"],
-            "Generate a Vim syntax file",
+        commands.add(["mkv[imruntime]"],
+            "Create and install Vim runtime files for " + config.appName,
             function (args) {
-                let runtime = config.OS.isWindows ? "~/vimfiles/" : "~/.vim/";
-                let file = io.File(runtime + "syntax/" + config.name + ".vim");
-                if (args.length)
-                    file = io.File(args[0]);
+                dactyl.assert(args.length <= 1, _("io.oneFileAllowed"));
+
+                if (args.length) {
+                    var rtDir = io.File(args[0]);
+                    dactyl.assert(rtDir.exists(), _("io.noSuchDir", rtDir.path.quote()));
+                }
+                else
+                    rtDir = io.File(config.OS.isWindows ? "~/vimfiles/" : "~/.vim/");
+
+                dactyl.assert(!rtDir.exists() || rtDir.isDirectory(), _("io.eNotDir", rtDir.path.quote()));
+
+                let rtItems = { ftdetect: {}, ftplugin: {}, syntax: {} };
+
+                // require bang if any of the paths exist
+                for (let [type, item] in iter(rtItems)) {
+                    let file = io.File(rtDir).child(type, config.name + ".vim");
+                    dactyl.assert(!file.exists() || args.bang, _("io.exists", file.path.quote()));
+                    item.file = file;
+                }
+
+                rtItems.ftdetect.template = //{{{
+literal(/*" Vim filetype detection file
+<header>
+
+au BufNewFile,BufRead *<name>rc*,*.<fileext> set filetype=<name>
+*/);//}}}
+                rtItems.ftplugin.template = //{{{
+literal(/*" Vim filetype plugin file
+<header>
 
-                if (file.exists() && file.isDirectory() || args[0] && /\/$/.test(args[0]))
-                    file.append(config.name + ".vim");
-                dactyl.assert(!file.exists() || args.bang, _("io.exists"));
+if exists("b:did_ftplugin")
+  finish
+endif
+let b:did_ftplugin = 1
+
+let s:cpo_save = &cpo
+set cpo&vim
 
-                let template = util.compileMacro(<![CDATA[
-" Vim syntax file
-" Language:         Pentadactyl configuration file
-" Maintainer:       Doug Kearns <dougkearns@gmail.com>
+let b:undo_ftplugin = "setl com< cms< fo< ofu< | unlet! b:browsefilter"
 
-" TODO: make this <name> specific - shared dactyl config?
+setlocal comments=:\"
+setlocal commentstring=\"%s
+setlocal formatoptions-=t formatoptions+=croql
+setlocal omnifunc=syntaxcomplete#Complete
+
+if has("gui_win32") && !exists("b:browsefilter")
+    let b:browsefilter = "<appname> Config Files (*.<fileext>)\t*.<fileext>\n" .
+        \ "All Files (*.*)\t*.*\n"
+endif
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
+*/);//}}}
+                rtItems.syntax.template = //{{{
+literal(/*" Vim syntax file
+<header>
 
 if exists("b:current_syntax")
   finish
@@ -756,11 +788,13 @@ let b:current_syntax = "<name>"
 let &cpo = s:cpo_save
 unlet s:cpo_save
 
-" vim: tw=130 et ts=4 sw=4:
-]]>, true);
+" vim: tw=130 et ts=8 sts=4 sw=4:
+*/);//}}}
+
+                const { options } = modules;
 
                 const WIDTH = 80;
-                function wrap(prefix, items, sep) {
+                function wrap(prefix, items, sep) {//{{{
                     sep = sep || " ";
                     let width = 0;
                     let lines = [];
@@ -776,12 +810,19 @@ unlet s:cpo_save
                         lines.last.push(item, sep);
                     }
                     lines.last.pop();
-                    return lines.map(function (l) l.join("")).join("\n").replace(/\s+\n/gm, "\n");
-                }
-
-                const { commands, options } = modules;
-                file.write(template({
+                    return lines.map(l => l.join(""))
+                                .join("\n")
+                                .replace(/\s+\n/gm, "\n");
+                }//}}}
+
+                let params = { //{{{
+                    header: ['" Language:    ' + config.appName + ' configuration file',
+                             '" Maintainer:  Doug Kearns <dougkearns@gmail.com>',
+                             '" Version:     ' + config.version].join("\n"),
                     name: config.name,
+                    appname: config.appName,
+                    fileext: config.fileExtension,
+                    maintainer: "Doug Kearns <dougkearns@gmail.com>",
                     autocommands: wrap("syn keyword " + config.name + "AutoEvent ",
                                        keys(config.autocommands)),
                     commands: wrap("syn keyword " + config.name + "Command ",
@@ -792,11 +833,22 @@ unlet s:cpo_save
                                         array(o.realNames for (o in options) if (o.type == "boolean"))
                                             .flatten().map(String.quote),
                                         ", ") + "]"
-                }));
+                }; //}}}
+
+                for (let { file, template } in values(rtItems)) {
+                    try {
+                        file.write(util.compileMacro(template, true)(params));
+                        dactyl.echomsg(_("io.writing", file.path.quote()), 2);
+                    }
+                    catch (e) {
+                        dactyl.echoerr(_("io.notWriteable", file.path.quote()));
+                        dactyl.log(_("error.notWriteable", file.path, e.message));
+                    }
+                }
             }, {
                 argCount: "?",
                 bang: true,
-                completer: function (context) completion.file(context, true),
+                completer: function (context) completion.directory(context, true),
                 literal: 1
             });
 
@@ -858,8 +910,8 @@ unlet s:cpo_save
                         _("command.run.noPrevious"));
 
                     arg = arg.replace(/(\\)*!/g,
-                        function (m) /^\\(\\\\)*!$/.test(m) ? m.replace("\\!", "!") : m.replace("!", io._lastRunCommand)
-                    );
+                                      m => (/^\\(\\\\)*!$/.test(m) ? m.replace("\\!", "!")
+                                                                   : m.replace("!", io._lastRunCommand)));
                 }
 
                 io._lastRunCommand = arg;
@@ -869,7 +921,7 @@ unlet s:cpo_save
                     result.output += "\n" + _("io.shellReturn", result.returnValue);
 
                 modules.commandline.command = args.commandName.replace("run", "$& ") + arg;
-                modules.commandline.commandOutput(<span highlight="CmdOutput">{result.output}</span>);
+                modules.commandline.commandOutput(["span", { highlight: "CmdOutput" }, result.output]);
 
                 modules.autocommands.trigger("ShellCmdPost", {});
             }, {
@@ -880,7 +932,7 @@ unlet s:cpo_save
                 literal: 0
             });
     },
-    completion: function init_completion(dactyl, modules, window) {
+    completion: function initCompletion(dactyl, modules, window) {
         const { completion, io } = modules;
 
         completion.charset = function (context) {
@@ -896,21 +948,21 @@ unlet s:cpo_save
                     }
                 }
             };
-            context.generate = function () iter(services.charset.getDecoderList());
+            context.generate = () => iter(services.charset.getDecoderList());
         };
 
         completion.directory = function directory(context, full) {
             this.file(context, full);
-            context.filters.push(function (item) item.isdir);
+            context.filters.push(item => item.isdir);
         };
 
         completion.environment = function environment(context) {
             context.title = ["Environment Variable", "Value"];
-            context.generate = function ()
+            context.generate = () =>
                 io.system(config.OS.isWindows ? "set" : "env")
                   .output.split("\n")
-                  .filter(function (line) line.indexOf("=") > 0)
-                  .map(function (line) line.match(/([^=]+)=(.*)/).slice(1));
+                  .filter(line => line.indexOf("=") > 0)
+                  .map(line => line.match(/([^=]+)=(.*)/).slice(1));
         };
 
         completion.file = function file(context, full, dir) {
@@ -937,10 +989,10 @@ unlet s:cpo_save
                 icon: function (f) this.isdir ? "resource://gre/res/html/folder.png"
                                               : "moz-icon://" + f.leafName
             };
-            context.compare = function (a, b) b.isdir - a.isdir || String.localeCompare(a.text, b.text);
+            context.compare = (a, b) => b.isdir - a.isdir || String.localeCompare(a.text, b.text);
 
             if (modules.options["wildignore"])
-                context.filters.push(function (item) !modules.options.get("wildignore").getKey(item.path));
+                context.filters.push(item => !modules.options.get("wildignore").getKey(item.path));
 
             // context.background = true;
             context.key = dir;
@@ -992,7 +1044,7 @@ unlet s:cpo_save
         };
 
         completion.addUrlCompleter("file", "Local files", function (context, full) {
-            let match = util.regexp(<![CDATA[
+            let match = util.regexp(literal(/*
                 ^
                 (?P<prefix>
                     (?P<proto>
@@ -1003,12 +1055,12 @@ unlet s:cpo_save
                 )
                 (?P<path> \/[^\/]* )?
                 $
-            ]]>, "x").exec(context.filter);
+            */), "x").exec(context.filter);
             if (match) {
                 if (!match.path) {
                     context.key = match.proto;
                     context.advance(match.proto.length);
-                    context.generate = function () config.chromePackages.map(function (p) [p, match.proto + p + "/"]);
+                    context.generate = () => config.chromePackages.map(p => [p, match.proto + p + "/"]);
                 }
                 else if (match.scheme === "chrome") {
                     context.key = match.prefix;
@@ -1028,7 +1080,7 @@ unlet s:cpo_save
                     completion.file(context, full);
         });
     },
-    javascript: function init_javascript(dactyl, modules, window) {
+    javascript: function initJavascript(dactyl, modules, window) {
         modules.JavaScript.setCompleter([File, File.expandPath],
             [function (context, obj, args) {
                 context.quote[2] = "";
@@ -1047,7 +1099,7 @@ unlet s:cpo_save
             input: true
         });
     },
-    options: function init_options(dactyl, modules, window) {
+    options: function initOptions(dactyl, modules, window) {
         const { completion, options } = modules;
 
         var shell, shellcmdflag;
@@ -1075,8 +1127,8 @@ unlet s:cpo_save
             "List of directories searched when executing :cd",
             "stringlist", ["."].concat(services.environment.get("CDPATH").split(/[:;]/).filter(util.identity)).join(","),
             {
-                get files() this.value.map(function (path) File(path, modules.io.cwd))
-                                .filter(function (dir) dir.exists()),
+                get files() this.value.map(path => File(path, modules.io.cwd))
+                                .filter(dir => dir.exists()),
                 setter: function (value) File.expandPathList(value)
             });
 
@@ -1084,8 +1136,8 @@ unlet s:cpo_save
             "List of directories searched for runtime files",
             "stringlist", IO.runtimePath,
             {
-                get files() this.value.map(function (path) File(path, modules.io.cwd))
-                                .filter(function (dir) dir.exists())
+                get files() this.value.map(path => File(path, modules.io.cwd))
+                                .filter(dir => dir.exists())
             });
 
         options.add(["shell", "sh"],
@@ -1116,4 +1168,4 @@ endModule();
 
 } catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
 
-// vim: set fdm=marker sw=4 ts=4 et ft=javascript:
+// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: