diff --git a/content/bookmarks.js b/content/bookmarks.js index 187ad810..e9982a6e 100644 --- a/content/bookmarks.js +++ b/content/bookmarks.js @@ -338,7 +338,10 @@ function Bookmarks() //{{{ liberator.echo(deletedCount + " bookmark(s) with url `" + url + "' deleted", commandline.FORCE_SINGLELINE); }, - { completer: function (context) completion.bookmark(context.filter) }); + { + completer: function (context) completion.bookmark(context.filter), + literal: true + }); /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// @@ -631,7 +634,7 @@ function History() //{{{ let filter = context.filter; var sh = getWebNavigation().sessionHistory; var completions = []; - for (let i = sh.index - 1; i >= 0; i--) + for (let i in util.range(sh.index, 0, true)) { var entry = sh.getEntryAtIndex(i, false); var url = entry.URI.spec; @@ -641,7 +644,8 @@ function History() //{{{ } return [0, completions]; }, - count: true + count: true, + literal: true }); commands.add(["fo[rward]", "fw"], @@ -692,7 +696,8 @@ function History() //{{{ } return [0, completions]; }, - count: true + count: true, + literal: true }); commands.add(["hist[ory]", "hs"], diff --git a/content/buffer.js b/content/buffer.js index edf158b3..e32f2a4c 100644 --- a/content/buffer.js +++ b/content/buffer.js @@ -512,7 +512,10 @@ function Buffer() //{{{ stylesheetSwitchAll(window.content, args); }, - { completer: function (context) completion.alternateStylesheet(context.filter) }); + { + completer: function (context) completion.alternateStylesheet(context.filter), + literal: true + }); commands.add(["re[load]"], "Reload current page", diff --git a/content/commands.js b/content/commands.js index b8b72ba5..bf75a814 100644 --- a/content/commands.js +++ b/content/commands.js @@ -80,7 +80,7 @@ function Command(specs, description, action, extraInfo) //{{{ this.names = expandedSpecs.names; // return all command name aliases this.description = description || ""; this.action = action; - this.argCount = extraInfo.argCount || ""; + this.argCount = extraInfo.argCount || 0; this.completer = extraInfo.completer || null; this.hereDoc = extraInfo.hereDoc || false; this.options = extraInfo.options || []; @@ -425,7 +425,7 @@ function Commands() //{{{ argCount = "*"; if (literal) - var literalIndex = parseInt(argCount) - 1 || 0; + var literalIndex = argCount == "+" ? 0 : Math.max(argCount - 1, 0); var args = {}; // parsed options args.arguments = []; // remaining arguments @@ -499,36 +499,19 @@ function Commands() //{{{ arg = null; quote = null; count = 0; - // no value to the option - if (optname.length >= sub.length) - { - count = 0; - } - else if (sub[optname.length] == "=") + let sep = sub[optname.length]; + if (sep == "=" || /\s/.test(sep) && opt[1] != this.OPTION_NOARG) { [count, arg, quote] = getNextArg(sub.substr(optname.length + 1)); + // if we add the argument to an option after a space, it MUST not be empty + if (sep != "=" && !quote && arg.length == 0) + arg = null; + count++; // to compensate the "=" character } - else if (/\s/.test(sub[optname.length])) + else if (sep != null) // this isn't really an option as it has trailing characters, parse it as an argument { - if (opt[1] != this.OPTION_NOARG) - { - [count, arg, quote] = getNextArg(sub.substr(optname.length + 1)); - if (count == -1) - - // if we add the argument to an option after a space, it MUST not be empty - if (!quote && arg.length == 0) - arg = null; - - count++; // to compensate the " " character - } - else - count = 1; // the space - } - else - { - // this isn't really an option as it has trailing characters, parse it as an argument invalid = true; } @@ -589,6 +572,12 @@ function Commands() //{{{ } } + if (complete) + { + if (argCount == "0" || args.arguments.length > 0 && (argCount == "1" || argCount == "?")) + complete.highlight(i, sub.length, "SPELLCHECK") + } + if (literal && args.arguments.length == literalIndex) { args.literalArg = sub; @@ -643,7 +632,7 @@ function Commands() //{{{ } // check for correct number of arguments - if (args.arguments.length == 0 && (argCount == "1" || argCount == "+") && !complete) + if (!complete && (args.arguments.length == 0 && (argCount == "1" || argCount == "+") || literal && argCount == "+" && /^\s*$/.test(literalArg))) { liberator.echoerr("E471: Argument required"); return null; @@ -835,7 +824,7 @@ function Commands() //{{{ } }, { - argCount: "2", + argCount: 2, bang: true, completer: function (context) completion.userCommand(context.filter), options: [ diff --git a/content/completion.js b/content/completion.js index b6718dd7..b8aa1e84 100644 --- a/content/completion.js +++ b/content/completion.js @@ -53,11 +53,12 @@ function CompletionContext(editor, name, offset) this.parent = parent; this.editor = parent.editor; this.offset = parent.offset + (offset || 0); - this.__defineGetter__("tabPressed", function () this.top.tabPressed); + this.__defineGetter__("contextList", function () this.top.contextList); this.__defineGetter__("onUpdate", function () this.top.onUpdate); + this.__defineGetter__("selectionTypes", function () this.top.selectionTypes); + this.__defineGetter__("tabPressed", function () this.top.tabPressed); this.__defineGetter__("updateAsync", function () this.top.updateAsync); this.__defineGetter__("value", function () this.top.value); - this.__defineGetter__("selectionTypes", function () this.top.selectionTypes); this.incomplete = false; } else @@ -105,8 +106,6 @@ CompletionContext.prototype = { get caret() (this.editor ? this.editor.selection.getRangeAt(0).startOffset : this.value.length) - this.offset, - get contextList() [v for ([k, v] in Iterator(this.contexts))], - get createRow() this._createRow || template.completionRow, // XXX set createRow(createRow) this._createRow = createRow, @@ -132,6 +131,7 @@ CompletionContext.prototype = { fork: function fork(name, offset, completer, self) { let context = new CompletionContext(this, name, offset); + this.contextList.push(context); if (completer) return completer.apply(self, [context].concat(Array.slice(arguments, 4))); return context; @@ -146,16 +146,14 @@ CompletionContext.prototype = { const editor = this.editor; let sel = editor.selectionController.getSelection(selType); if (length == 0) - { sel.removeAllRanges(); - editor.selectionController.repaintSelection(selType); - return; + else + { + let range = editor.selection.getRangeAt(0).cloneRange(); + range.setStart(range.startContainer, this.offset + start); + range.setEnd(range.startContainer, this.offset + start + length); + sel.addRange(range); } - - let range = editor.selection.getRangeAt(0).cloneRange(); - range.setStart(range.startContainer, this.offset + start); - range.setEnd(range.startContainer, this.offset + start + length); - sel.addRange(range); editor.selectionController.repaintSelection(selType); } catch (e) {} @@ -169,10 +167,11 @@ CompletionContext.prototype = { // Not ideal. for (let type in this.selectionTypes) this.highlight(0, 0, type); - this.updateAsync = false; + this.contextList = []; + this.offset = 0; this.selectionTypes = {}; this.tabPressed = false; - this.offset = 0; + this.updateAsync = false; this.value = this.editor ? this.editor.rootElement.textContent : this._value; //for (let key in (k for ([k, v] in Iterator(self.contexts)) if (v.offset > this.caret))) // delete this.contexts[key]; @@ -1239,8 +1238,9 @@ function Completion() //{{{ root.containerOpen = true; context.cache.items = util.map(util.range(0, root.childCount), function (i) { let child = root.getChild(i); - let query = child.uri.substring(begin.length, child.uri.length - end.length); - if (end == "" || child.uri.substr(-end.length) == end) + let rest = child.uri.length - end.length; + let query = child.uri.substring(begin.length, rest); + if (child.uri.substr(rest) == end && query.indexOf("&") == -1) return [decodeURIComponent(query.replace("+", "%20")), child.title, child.icon]; @@ -1430,11 +1430,14 @@ function Completion() //{{{ return [0, this.filter(cmds, filter)]; }, - userMapping: function userMapping(filter, modes) + userMapping: function userMapping(context, args, modes) { - // TODO: add appropriate getters to l.mappings - let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))]; - return [0, this.filter(maps, filter)]; + liberator.dump(args); + if (args.completeArg == 0) + { + let maps = [[m.names[0], ""] for (m in mappings.getUserIterator(modes))]; + context.items = this.filter(maps, args.arguments[0]); + } } // }}} }; diff --git a/content/editor.js b/content/editor.js index 76aeaa0e..6b310bd9 100644 --- a/content/editor.js +++ b/content/editor.js @@ -173,7 +173,7 @@ function Editor() //{{{ editor.listAbbreviations(mode, lhs || ""); }, { - argCount: "2", + argCount: 2, literal: true, serial: function () [ { diff --git a/content/events.js b/content/events.js index 4809230e..1011a8a8 100644 --- a/content/events.js +++ b/content/events.js @@ -111,7 +111,7 @@ function AutoCommands() //{{{ } }, { - argCount: "3", + argCount: 3, bang: true, completer: function (context) completion.autocmdEvent(context.filter), literal: true @@ -125,8 +125,8 @@ function AutoCommands() //{{{ commands.get("doautocmd").action.call(this, args.string); }, { - argCount: "+", - completer: function (context) completion.autocmdEvent(context.filter) + completer: function (context) completion.autocmdEvent(context.filter), + literal: true } ); @@ -136,6 +136,8 @@ function AutoCommands() //{{{ function (args) { args = args.string; + if (/^\s*$/.test(args)) + return liberator.echo("No matching autocommands"); let [, event, url] = args.match(/^(\S+)(?:\s+(\S+))?$/); url = url || buffer.URL; @@ -161,9 +163,8 @@ function AutoCommands() //{{{ } }, { - // TODO: Vim actually just displays "No matching autocommands" when no arg is specified - argCount: "+", - completer: function (context) completion.autocmdEvent(context.filter) + completer: function (context) completion.autocmdEvent(context.filter), + literal: true } ); @@ -718,7 +719,8 @@ function Events() //{{{ }, { bang: true, - completer: function (context) completion.macro(context.filter) + completer: function (context) completion.macro(context.filter), + literal: true }); commands.add(["macros"], @@ -729,7 +731,10 @@ function Events() //{{{ var str = template.tabular(["Macro", "Keys"], [], events.getMacros(args.string)); liberator.echo(str, commandline.FORCE_MULTILINE); }, - { completer: function (context) completion.macro(context.filter) }); + { + completer: function (context) completion.macro(context.filter), + literal: true + }); commands.add(["pl[ay]"], "Replay a recorded macro", diff --git a/content/io.js b/content/io.js index c85e957a..c5656077 100644 --- a/content/io.js +++ b/content/io.js @@ -339,7 +339,8 @@ function IO() //{{{ }, { bang: true, - completer: function (context) completion.shellCommand(filter) + completer: function (context) completion.shellCommand(context.filter), + literal: true }); /////////////////////////////////////////////////////////////////////////////}}} diff --git a/content/liberator.js b/content/liberator.js index 8c4776d8..6af476ae 100644 --- a/content/liberator.js +++ b/content/liberator.js @@ -239,29 +239,30 @@ const liberator = (function () //{{{ "Execute the specified menu item from the command line", function (args) { - var item = args.string; - var items = getMenuItems(); + let args = args.string; + let items = getMenuItems(); - if (!items.some(function (i) i.fullMenuPath == item)) + if (!items.some(function (i) i.fullMenuPath == args)) { - liberator.echoerr("E334: Menu not found: " + item); + liberator.echoerr("E334: Menu not found: " + args); return; } - for (let i = 0; i < items.length; i++) + for (let [i, item] in Iterator(items)) { - if (items[i].fullMenuPath == item) - items[i].doCommand(); + if (item.fullMenuPath == args) + item.doCommand(); } }, { - argCount: "+", // NOTE: single arg may contain unescaped whitespace + argCount: "+", // TODO: add this as a standard menu completion function completer: function (context) { let completions = getMenuItems().map(function (item) [item.fullMenuPath, item.label]); return [0, completion.filter(completions, context.filter)]; - } + }, + literal: true }); commands.add(["exe[cute]"], @@ -307,7 +308,8 @@ const liberator = (function () //{{{ }, { bang: true, - completer: function (context) completion.help(context.filter) + completer: function (context) completion.help(context.filter), + literal: true }); commands.add(["javas[cript]", "js"], @@ -335,7 +337,8 @@ const liberator = (function () //{{{ { bang: true, completer: function (context) completion.javascript(context), - hereDoc: true + hereDoc: true, + literal: true }); commands.add(["loadplugins", "lpl"], @@ -468,7 +471,8 @@ const liberator = (function () //{{{ else return completion.javascript(context); }, - count: true + count: true, + literal: true }); commands.add(["ve[rsion]"], diff --git a/content/mappings.js b/content/mappings.js index 69994d31..9e625067 100644 --- a/content/mappings.js +++ b/content/mappings.js @@ -184,8 +184,8 @@ function Mappings() //{{{ modeDescription = modeDescription ? " in " + modeDescription + " mode" : ""; const opts = { - argCount: "2", - completer: function (context) completion.userMapping(context.filter, modes), + argCount: 2, + completer: function (context, args) completion.userMapping(context, args, modes), options: [ [["", ""], commands.OPTION_NOARG] ], @@ -224,13 +224,7 @@ function Mappings() //{{{ "Remove a mapping" + modeDescription, function (args) { - args = args.string; - - if (!args) - { - liberator.echoerr("E474: Invalid argument"); - return; - } + args = args.arguments[1]; let found = false; for (let [,mode] in Iterator(modes)) @@ -244,7 +238,10 @@ function Mappings() //{{{ if (!found) liberator.echoerr("E31: No such mapping"); }, - { completer: function (context) completion.userMapping(context.filter, modes) }); + { + argCount: "1", + completer: function (context, args) completion.userMapping(context, args, modes) + }); } /////////////////////////////////////////////////////////////////////////////}}} diff --git a/content/options.js b/content/options.js index 17a11846..3f44ea41 100644 --- a/content/options.js +++ b/content/options.js @@ -397,7 +397,8 @@ function Options() //{{{ completer: function (context, args, special, count) { return commands.get("set").completer(context.filter, special, count, { scope: options.OPTION_SCOPE_LOCAL }); - } + }, + literal: true } ); @@ -413,7 +414,8 @@ function Options() //{{{ completer: function (context, args, special, count) { return commands.get("set").completer(context.filter, special, count, { scope: options.OPTION_SCOPE_GLOBAL }); - } + }, + literal: true } ); @@ -801,6 +803,7 @@ function Options() //{{{ } return [len, completion.filter(completions, filter, true)]; }, + literal: true, serial: function () [ { command: this.name, diff --git a/content/style.js b/content/style.js index 41905375..ea91f32e 100644 --- a/content/style.js +++ b/content/style.js @@ -426,7 +426,7 @@ liberator.registerObserver("load_commands", function () } }, { - argCount: "2", + argCount: 2, bang: true, completer: function (context, args, bang) { let compl = []; @@ -463,7 +463,7 @@ liberator.registerObserver("load_commands", function () styles.removeSheet(args["-name"], args.arguments[0], args.literalArg, args["-index"], false); }, { - argCount: "2", + argCount: 2, // FIXME: Ugly. completer: function (context) [0, completion.filter( [[i, <>{s.sites.join(",")}: {s.css.replace("\n", "\\n")}] @@ -507,7 +507,7 @@ liberator.registerObserver("load_commands", function () liberator.echoerr(error); }, { - argCount: "2", + argCount: 2, bang: true, // TODO: add this as a standard highlight completion function? completer: function (context) [0, diff --git a/content/tabs.js b/content/tabs.js index 01759507..131acc25 100644 --- a/content/tabs.js +++ b/content/tabs.js @@ -354,7 +354,8 @@ function Tabs() //{{{ { bang: true, count: true, - completer: function (context) completion.buffer(context.filter) + completer: function (context) completion.buffer(context.filter), + literal: true }); // TODO: this should open in a new tab positioned directly after the current one, not at the end @@ -368,7 +369,8 @@ function Tabs() //{{{ }, { argCount: "+", - completer: function (context) completion.ex(context.filter) + completer: function (context) completion.ex(context.filter), + literal: true }); commands.add(["tabl[ast]", "bl[ast]"], @@ -478,7 +480,8 @@ function Tabs() //{{{ { bang: true, count: true, - completer: function (context) completion.buffer(context.filter) + completer: function (context) completion.buffer(context.filter), + literal: true }); commands.add(["buffers", "files", "ls", "tabs"], @@ -548,7 +551,8 @@ function Tabs() //{{{ }, { bang: true, - completer: function (context) completion.url(context) + completer: function (context) completion.url(context), + literal: true }); commands.add(["tabde[tach]"], @@ -634,7 +638,8 @@ function Tabs() //{{{ } return [0, completions]; }, - count: true + count: true, + literal: true }); commands.add(["undoa[ll]"], diff --git a/content/ui.js b/content/ui.js index 5d12b1d2..e6c47aff 100644 --- a/content/ui.js +++ b/content/ui.js @@ -500,7 +500,10 @@ function CommandLine() //{{{ if (str != null) command.action(str); }, - { completer: function (context) completion.javascript(context) }); + { + completer: function (context) completion.javascript(context), + literal: true + }); }); commands.add(["mes[sages]"], diff --git a/content/util.js b/content/util.js index 6a405572..dd68e02c 100644 --- a/content/util.js +++ b/content/util.js @@ -349,10 +349,18 @@ const util = { //{{{ return color ? string : [s for each (s in string)].join(""); }, - range: function (start, end) + range: function (start, end, reverse) { - while (start < end) - yield start++; + if (!reverse) + { + while (start < end) + yield start++; + } + else + { + while (start >= end) + yield --start; + } }, interruptableRange: function (start, end, time) diff --git a/content/vimperator.js b/content/vimperator.js index 44a19397..781c8822 100644 --- a/content/vimperator.js +++ b/content/vimperator.js @@ -283,19 +283,15 @@ const config = { //{{{ { liberator.open(args); } + else if (special) + BrowserReloadSkipCache(); else - { - if (special) - BrowserReloadSkipCache(); - else - BrowserReload(); - } + BrowserReload(); }, { bang: true, - literal: true, - argCount: 0, - completer: function (context) completion.url(context) + completer: function (context) completion.url(context), + literal: true }); commands.add(["redr[aw]"], @@ -347,7 +343,8 @@ const config = { //{{{ }, { argCount: "+", - completer: function (context) completion.sidebar(context.filter) + completer: function (context) completion.sidebar(context.filter), + literal: true }); commands.add(["winc[lose]", "wc[lose]"], @@ -366,7 +363,10 @@ const config = { //{{{ else liberator.open("about:blank", liberator.NEW_WINDOW); }, - { completer: function (context) completion.url(context) }); + { + completer: function (context) completion.url(context), + literal: true + }); /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// OPTIONS /////////////////////////////////////////////////