diff --git a/NEWS b/NEWS index 6a7cadaf..6b7a0b83 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@
 2008-XX-XX:
     * version 2.0 (probably)
+    * IMPORTANT: command actions now take an args object, returned from
+      commands.parseArgs, as their first argument. This will break any commands
+      not using the args parser explicitly.  The old string value is now
+      available via args.string.
     * IMPORTANT: 'verbose' is now used for message levels. Logging is
       controlled by the extensions.liberator.loglevel preference.
     * IMPORTANT: :viusage and :exusage now jump to the help index, use the
diff --git a/content/addressbook.js b/content/addressbook.js
index 2cdc3b56..3b104b26 100644
--- a/content/addressbook.js
+++ b/content/addressbook.js
@@ -137,7 +137,7 @@ function Addressbook() //{{{
 
     commands.add(["contacts", "addr[essbook]"],
         "List or open multiple addresses",
-        function (args, special) { addressbook.list(args, special); },
+        function (args, special) { addressbook.list(args.string, special); },
         { bang: true });
 
     /////////////////////////////////////////////////////////////////////////////}}}
diff --git a/content/bookmarks.js b/content/bookmarks.js
index 549707a3..a2d65f34 100644
--- a/content/bookmarks.js
+++ b/content/bookmarks.js
@@ -299,7 +299,7 @@ function Bookmarks() //{{{
         "Delete a bookmark",
         function (args)
         {
-            let url = args || buffer.URL;
+            let url = args.string || buffer.URL;
             let deletedCount = bookmarks.remove(url);
 
             liberator.echo(deletedCount + " bookmark(s) with url `" + url + "' deleted", commandline.FORCE_SINGLELINE);
@@ -610,6 +610,8 @@ function History() //{{{
         "Go back in the browser history",
         function (args, special, count)
         {
+            args = args.string;
+
             if (special)
             {
                 history.goToStart();
@@ -658,6 +660,8 @@ function History() //{{{
         "Go forward in the browser history",
         function (args, special, count)
         {
+            args = args.string;
+
             if (special)
             {
                 history.goToEnd();
@@ -704,7 +708,7 @@ function History() //{{{
 
     commands.add(["hist[ory]", "hs"],
         "Show recently visited URLs",
-        function (args, special) { history.list(args, special); },
+        function (args, special) { history.list(args.string, special); },
         {
             bang: true,
             completer: function (filter) completion.history(filter)
@@ -876,6 +880,8 @@ function QuickMarks() //{{{
         "Delete the specified QuickMarks",
         function (args, special)
         {
+            args = args.string;
+
             // TODO: finish arg parsing - we really need a proper way to do this. :)
             if (!special && !args)
             {
@@ -914,6 +920,8 @@ function QuickMarks() //{{{
         "Show all QuickMarks",
         function (args)
         {
+            args = args.string;
+
             // ignore invalid qmark characters unless there are no valid qmark chars
             if (args && !/[a-zA-Z0-9]/.test(args))
             {
diff --git a/content/buffer.js b/content/buffer.js
index bb1a2ac1..36758b40 100644
--- a/content/buffer.js
+++ b/content/buffer.js
@@ -770,6 +770,8 @@ function Buffer() //{{{
         "Select the author style sheet to apply",
         function (args)
         {
+            args = args.string;
+
             var titles = buffer.alternateStyleSheets.map(function (stylesheet) stylesheet.title);
 
             if (args && titles.indexOf(args) == -1)
@@ -797,6 +799,8 @@ function Buffer() //{{{
         "Save current document to disk",
         function (args, special)
         {
+            args = args.string;
+
             let doc = window.content.document;
             let file = io.getFile(args || "");
             if (args && file.exists() && !special)
@@ -930,6 +934,8 @@ function Buffer() //{{{
         "Set zoom value of current web page",
         function (args, special)
         {
+            args = args.string;
+
             var level;
 
             if (!args)
@@ -1870,6 +1876,8 @@ function Marks() //{{{
         "Delete the specified marks",
         function (args, special)
         {
+            args = args.string;
+
             if (!special && !args)
             {
                 liberator.echoerr("E471: Argument required");
@@ -1935,6 +1943,8 @@ function Marks() //{{{
         "Show all location marks of current web page",
         function (args)
         {
+            args = args.string;
+
             // ignore invalid mark characters unless there are no valid mark chars
             if (args && !/[a-zA-Z]/.test(args))
             {
diff --git a/content/commands.js b/content/commands.js
index e24adf2a..69bf805e 100644
--- a/content/commands.js
+++ b/content/commands.js
@@ -94,28 +94,32 @@ Command.prototype = {
         special = !!special;
         count = (count === undefined) ? -1 : count;
         modifiers = modifiers || {};
+
         let self = this;
 
-        // whenever the user specifies special options or fixed number of arguments
-        // we use our args parser instead of passing a string to the callback
-        if (this.options.length > 0 || this.argCount)
-        {
-            args = commands.parseArgs(args, this.options, this.argCount, false, this.literal);
-            if (args == null)
-                return false;
-        }
-        else if (this.hereDoc)
+        function parseArgs(args) commands.parseArgs(args, this.options, this.argCount, false, this.literal);
+
+        if (this.hereDoc)
         {
             let matches = args.match(/(.*)<<\s*(\S+)$/);
             if (matches && matches[2])
             {
                 commandline.inputMultiline(new RegExp("^" + matches[2] + "$", "m"),
-                    function (args) self.action.call(self, matches[1] + "\n" + args, special, count, modifiers));
+                    function (args)
+                    {
+                        args = parseArgs.call(self, matches[1] + "\n" + args);
+
+                        if (args)
+                            self.action.call(self, args, special, count, modifiers);
+                    });
                 return;
             }
         }
 
-        return this.action.call(this, args, special, count, modifiers);
+        args = parseArgs.call(this, args);
+
+        if (args)
+            this.action.call(this, args, special, count, modifiers);
     },
 
     // return true if the candidate name matches one of the command's aliases
@@ -464,7 +468,8 @@ function Commands() //{{{
             args.literalArg = "";
 
             var invalid = false;
-            var onlyArgumentsRemaining = allowUnknownOptions || false; // after a -- has been found
+            // FIXME: best way to specify these requirements?
+            var onlyArgumentsRemaining = allowUnknownOptions || options.length == 0 || false; // after a -- has been found
             var arg = null;
             var count = 0; // the length of the argument
             var i = 0;
diff --git a/content/editor.js b/content/editor.js
index b29a11d9..5974a96b 100644
--- a/content/editor.js
+++ b/content/editor.js
@@ -157,6 +157,8 @@ function Editor() //{{{
             "Abbreviate a key sequence" + modeDescription,
             function (args)
             {
+                args = args.string;
+
                 if (!args)
                 {
                     editor.listAbbreviations(mode, "");
@@ -173,7 +175,7 @@ function Editor() //{{{
 
         commands.add([ch ? ch + "una[bbrev]" : "una[bbreviate]"],
             "Remove an abbreviation" + modeDescription,
-            function (args) { editor.removeAbbreviation(mode, args); });
+            function (args) { editor.removeAbbreviation(mode, args.string); });
 
         commands.add([ch + "abc[lear]"],
             "Remove all abbreviations" + modeDescription,
diff --git a/content/events.js b/content/events.js
index 1298b44d..b0229f48 100644
--- a/content/events.js
+++ b/content/events.js
@@ -122,7 +122,7 @@ function AutoCommands() //{{{
         "Apply the autocommands matching the specified URL pattern to all buffers",
         function (args)
         {
-            commands.get("doautocmd").action.call(this, args);
+            commands.get("doautocmd").action.call(this, args.string);
         },
         {
             argCount: "+",
@@ -696,6 +696,8 @@ function Events() //{{{
         "Delete macros",
         function (args, special)
         {
+            args = args.string;
+
             if (special)
                 args = ".*"; // XXX
 
@@ -711,7 +713,7 @@ function Events() //{{{
         function (args)
         {
             XML.prettyPrinting = false;
-            var str = template.tabular(["Macro", "Keys"], [], events.getMacros(args));
+            var str = template.tabular(["Macro", "Keys"], [], events.getMacros(args.string));
             liberator.echo(str, commandline.FORCE_MULTILINE);
         },
         { completer: function (filter) completion.macro(filter) });
diff --git a/content/io.js b/content/io.js
index 41cd316f..ce4a7fd8 100644
--- a/content/io.js
+++ b/content/io.js
@@ -139,6 +139,8 @@ function IO() //{{{
         "Change the current directory",
         function (args)
         {
+            args = args.string;
+
             if (!args)
             {
                 args = "~";
@@ -216,6 +218,8 @@ function IO() //{{{
         "Write current key mappings and changed options to the config file",
         function (args, special)
         {
+            args = args.string;
+
             // TODO: "E172: Only one file name allowed"
             var filename;
             if (args)
@@ -344,6 +348,8 @@ function IO() //{{{
         "Read Ex commands from a file",
         function (args, special)
         {
+            args = args.string;
+
             // FIXME: implement proper filename quoting - "E172: Only one file name allowed"
             if (!args)
             {
@@ -362,6 +368,8 @@ function IO() //{{{
         "Run a command",
         function (args, special)
         {
+            args = args.string;
+
             // :!! needs to be treated specially as the command parser sets the
             // special flag but removes the ! from args
             if (special)
diff --git a/content/liberator.js b/content/liberator.js
index 7552dc50..f68cb907 100644
--- a/content/liberator.js
+++ b/content/liberator.js
@@ -261,7 +261,7 @@ const liberator = (function () //{{{
             {
                 try
                 {
-                    var cmd = liberator.eval(args);
+                    var cmd = liberator.eval(args.string);
                     liberator.execute(cmd);
                 }
                 catch (e)
@@ -301,7 +301,7 @@ const liberator = (function () //{{{
                     return;
                 }
 
-                liberator.help(args);
+                liberator.help(args.string);
             },
             {
                 bang: true,
@@ -322,7 +322,7 @@ const liberator = (function () //{{{
                 {
                     try
                     {
-                        liberator.eval(args);
+                        liberator.eval(args.string);
                     }
                     catch (e)
                     {
@@ -392,6 +392,7 @@ const liberator = (function () //{{{
             function (args, special, count)
             {
                 args = args.string;
+
                 let method = args[0] == ":" ? "execute" : "eval";
 
                 try
diff --git a/content/mail.js b/content/mail.js
index e6bef11a..e27b4206 100644
--- a/content/mail.js
+++ b/content/mail.js
@@ -720,12 +720,12 @@ function Mail() //{{{
 
     commands.add(["copy[to]"],
         "Copy selected messages",
-        function (args) { moveOrCopy(true, args); },
+        function (args) { moveOrCopy(true, args.string); },
         { completer: function (filter) getFolderCompletions(filter) });
 
     commands.add(["move[to]"],
         "Move selected messages",
-        function (args) { moveOrCopy(false, args); },
+        function (args) { moveOrCopy(false, args.string); },
         { completer: function (filter) getFolderCompletions(filter) });
 
     commands.add(["empty[trash]"],
diff --git a/content/mappings.js b/content/mappings.js
index 3eed6226..b6f2eea7 100644
--- a/content/mappings.js
+++ b/content/mappings.js
@@ -234,6 +234,8 @@ function Mappings() //{{{
             "Remove a mapping" + modeDescription,
             function (args)
             {
+                args = args.string;
+
                 if (!args)
                 {
                     liberator.echoerr("E474: Invalid argument");
diff --git a/content/options.js b/content/options.js
index 3cf08798..5c190368 100644
--- a/content/options.js
+++ b/content/options.js
@@ -279,6 +279,8 @@ function Options() //{{{
         "Set or list a variable",
         function (args)
         {
+            args = args.string;
+
             if (!args)
             {
                 var str =
@@ -387,7 +389,7 @@ function Options() //{{{
         "Set local option",
         function (args, special, count)
         {
-            commands.get("set").execute(args, special, count, { scope: options.OPTION_SCOPE_LOCAL });
+            commands.get("set").execute(args.string, special, count, { scope: options.OPTION_SCOPE_LOCAL });
         },
         {
             bang: true,
@@ -403,7 +405,7 @@ function Options() //{{{
         "Set global option",
         function (args, special, count)
         {
-            commands.get("set").execute(args, special, count, { scope: options.OPTION_SCOPE_GLOBAL });
+            commands.get("set").execute(args.string, special, count, { scope: options.OPTION_SCOPE_GLOBAL });
         },
         {
             bang: true,
@@ -474,6 +476,8 @@ function Options() //{{{
         "Set an option",
         function (args, special, count, modifiers)
         {
+            args = args.string;
+
             if (special)
             {
                 var onlyNonDefault = false;
diff --git a/content/tabs.js b/content/tabs.js
index 9d03259f..fd3e8e1a 100644
--- a/content/tabs.js
+++ b/content/tabs.js
@@ -307,6 +307,8 @@ function Tabs() //{{{
         "Delete current buffer",
         function (args, special, count)
         {
+            args = args.string;
+
             if (args)
             {
                 args = args.toLowerCase();
@@ -373,6 +375,8 @@ function Tabs() //{{{
         "Switch to the previous tab or go [count] tabs back",
         function (args, special, count)
         {
+            args = args.string;
+
             // count is ignored if an arg is specified, as per Vim
             if (args)
             {
@@ -397,6 +401,8 @@ function Tabs() //{{{
         "Switch to the next or [count]th tab",
         function (args, special, count)
         {
+            args = args.string;
+
             if (args || count > 0)
             {
                 var index;
@@ -443,6 +449,8 @@ function Tabs() //{{{
             "Switch to a buffer",
             function (args, special, count)
             {
+                args = args.string;
+
                 // if a numeric arg is specified any count is ignored; if a
                 // count and non-numeric arg are both specified then E488
                 if (args && count > 0)
@@ -493,6 +501,8 @@ function Tabs() //{{{
             "Move the current tab after tab N",
             function (args, special)
             {
+                args = args.string;
+
                 // FIXME: tabmove! N should probably produce an error
                 if (!/^([+-]?\d+|)$/.test(args))
                 {
@@ -516,6 +526,8 @@ function Tabs() //{{{
             "Open one or more URLs in a new tab",
             function (args, special)
             {
+                args = args.string;
+
                 var where = special ? liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB;
                 if (/\btabopen\b/.test(options["activate"]))
                     where = special ? liberator.NEW_BACKGROUND_TAB : liberator.NEW_TAB;
@@ -565,6 +577,8 @@ function Tabs() //{{{
             "Undo closing of a tab",
             function (args, special, count)
             {
+                args = args.string;
+
                 if (count < 1)
                     count = 1;
 
diff --git a/content/ui.js b/content/ui.js
index 88700e7e..c70bf9ff 100644
--- a/content/ui.js
+++ b/content/ui.js
@@ -493,7 +493,7 @@ function CommandLine() //{{{
             command.description,
             function (args)
             {
-                var str = echoArgumentToString(args, true);
+                var str = echoArgumentToString(args.string, true);
                 if (str != null)
                     command.action(str);
             },
diff --git a/content/vimperator.js b/content/vimperator.js
index 295b7e18..ec6ad584 100644
--- a/content/vimperator.js
+++ b/content/vimperator.js
@@ -276,6 +276,8 @@ const config = { //{{{
             "Open one or more URLs in the current tab",
             function (args, special)
             {
+                args = args.string;
+
                 if (args)
                 {
                     liberator.open(args);
@@ -318,8 +320,10 @@ const config = { //{{{
             "Open the sidebar window",
             function (args)
             {
+                args = args.string;
+
                 // do nothing if the requested sidebar is already open
-                if (document.getElementById("sidebar-title").value == args.string)
+                if (document.getElementById("sidebar-title").value == args)
                 {
                     document.getElementById("sidebar-box").contentWindow.focus();
                     return;
@@ -328,13 +332,13 @@ const config = { //{{{
                 var menu = document.getElementById("viewSidebarMenu");
                 for (let i = 0; i < menu.childNodes.length; i++)
                 {
-                    if (menu.childNodes[i].label == args.string)
+                    if (menu.childNodes[i].label == args)
                     {
                         menu.childNodes[i].doCommand();
                         return;
                     }
                 }
-                liberator.echoerr("No sidebar " + args.string + " found");
+                liberator.echoerr("No sidebar " + args + " found");
             },
             {
                 argCount: "+",
@@ -350,6 +354,8 @@ const config = { //{{{
             "Open one or more URLs in a new window",
             function (args)
             {
+                args = args.string;
+
                 if (args)
                     liberator.open(args, liberator.NEW_WINDOW);
                 else