mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 17:47:59 +01:00
Context-specific completer options. Option refactoring.
New review Owner: dougkearns Hopefully the changeset hash will actually be linked this time. If not, it's the tip of the testing branch, presumably r4161. I've been meaning to do this for a while. It allows case matching, sorting, and auto completion options to be assigned on a per context basis, with fine grained control. It also adds builtin regex support to options since it's used fairly extensively. There are definitely other options that would benefit from the regexlist type, if I can dig them up. The interface (and perhaps the doc wording) is the only place I'm really ambivalent. --HG-- branch : testing
This commit is contained in:
@@ -598,6 +598,18 @@ const Bookmarks = Module("bookmarks", {
|
|||||||
context.completions = [["", "Don't perform searches by default"]].concat(context.completions);
|
context.completions = [["", "Don't perform searches by default"]].concat(context.completions);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
options.add(["suggestengines"],
|
||||||
|
"Engine Alias which has a feature of suggest",
|
||||||
|
"stringlist", "google",
|
||||||
|
{
|
||||||
|
completer: function completer(value) {
|
||||||
|
let engines = services.get("browserSearch").getEngines({})
|
||||||
|
.filter(function (engine) engine.supportsResponseType("application/x-suggestions+json"));
|
||||||
|
|
||||||
|
return engines.map(function (engine) [engine.alias, engine.description]);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
completion: function () {
|
completion: function () {
|
||||||
completion.bookmark = function bookmark(context, tags, extra) {
|
completion.bookmark = function bookmark(context, tags, extra) {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ const CommandLine = Module("commandline", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._autocompleteTimer = new Timer(200, 500, function autocompleteTell(tabPressed) {
|
this._autocompleteTimer = new Timer(200, 500, function autocompleteTell(tabPressed) {
|
||||||
if (!events.feedingKeys && self._completions && options.get("wildoptions").has("auto")) {
|
if (!events.feedingKeys && self._completions && options.get("autocomplete").values.length) {
|
||||||
self._completions.complete(true, false);
|
self._completions.complete(true, false);
|
||||||
self._completions.itemList.show();
|
self._completions.itemList.show();
|
||||||
}
|
}
|
||||||
@@ -1516,84 +1516,6 @@ const CommandLine = Module("commandline", {
|
|||||||
options.add(["showmode", "smd"],
|
options.add(["showmode", "smd"],
|
||||||
"Show the current mode in the command line",
|
"Show the current mode in the command line",
|
||||||
"boolean", true);
|
"boolean", true);
|
||||||
|
|
||||||
options.add(["suggestengines"],
|
|
||||||
"Engine Alias which has a feature of suggest",
|
|
||||||
"stringlist", "google",
|
|
||||||
{
|
|
||||||
completer: function completer(value) {
|
|
||||||
let engines = services.get("browserSearch").getEngines({})
|
|
||||||
.filter(function (engine) engine.supportsResponseType("application/x-suggestions+json"));
|
|
||||||
|
|
||||||
return engines.map(function (engine) [engine.alias, engine.description]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["complete", "cpt"],
|
|
||||||
"Items which are completed at the :open prompts",
|
|
||||||
"charlist", typeof(config.defaults["complete"]) == "string" ? config.defaults["complete"] : "slf",
|
|
||||||
{
|
|
||||||
completer: function (context) array(values(completion.urlCompleters))
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["wildcase", "wic"],
|
|
||||||
"Completion case matching mode",
|
|
||||||
"string", "smart",
|
|
||||||
{
|
|
||||||
completer: function () [
|
|
||||||
["smart", "Case is significant when capital letters are typed"],
|
|
||||||
["match", "Case is always significant"],
|
|
||||||
["ignore", "Case is never significant"]
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["wildignore", "wig"],
|
|
||||||
"List of file patterns to ignore when completing files",
|
|
||||||
"stringlist", "",
|
|
||||||
{
|
|
||||||
validator: function validator(values) {
|
|
||||||
// TODO: allow for escaping the ","
|
|
||||||
try {
|
|
||||||
RegExp("^(" + values.join("|") + ")$");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["wildmode", "wim"],
|
|
||||||
"Define how command line completion works",
|
|
||||||
"stringlist", "list:full",
|
|
||||||
{
|
|
||||||
completer: function (context) [
|
|
||||||
// Why do we need ""?
|
|
||||||
["", "Complete only the first match"],
|
|
||||||
["full", "Complete the next full match"],
|
|
||||||
["longest", "Complete to longest common string"],
|
|
||||||
["list", "If more than one match, list all matches"],
|
|
||||||
["list:full", "List all and complete first match"],
|
|
||||||
["list:longest", "List all and complete common string"]
|
|
||||||
],
|
|
||||||
checkHas: function (value, val) {
|
|
||||||
let [first, second] = value.split(":", 2);
|
|
||||||
return first == val || second == val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.add(["wildoptions", "wop"],
|
|
||||||
"Change how command line completion is done",
|
|
||||||
"stringlist", "",
|
|
||||||
{
|
|
||||||
completer: function completer(value) {
|
|
||||||
return [
|
|
||||||
["", "Default completion that won't show or sort the results"],
|
|
||||||
["auto", "Automatically show this._completions while you are typing"],
|
|
||||||
["sort", "Always sort the completion list"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
styles: function () {
|
styles: function () {
|
||||||
let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize;
|
let fontSize = util.computedStyle(document.getElementById(config.mainWindowId)).fontSize;
|
||||||
|
|||||||
@@ -894,10 +894,10 @@ const Commands = Module("commands", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[prefix] = context.filter.match(/^(?:\w*[\s!]|!)\s*/);
|
[prefix] = context.filter.match(/^(?:\w*[\s!]|!)\s*/);
|
||||||
let cmdContext = context.fork(cmd, prefix.length);
|
let cmdContext = context.fork(command.name, prefix.length);
|
||||||
let argContext = context.fork("args", prefix.length);
|
let argContext = context.fork("args", prefix.length);
|
||||||
args = command.parseArgs(cmdContext.filter, argContext, { count: count, bang: bang });
|
args = command.parseArgs(cmdContext.filter, argContext, { count: count, bang: bang });
|
||||||
if (args) {
|
if (args && !cmdContext.waitingForTab) {
|
||||||
// FIXME: Move to parseCommand
|
// FIXME: Move to parseCommand
|
||||||
args.count = count;
|
args.count = count;
|
||||||
args.bang = bang;
|
args.bang = bang;
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ const CompletionContext = Class("CompletionContext", {
|
|||||||
if (editor instanceof this.constructor) {
|
if (editor instanceof this.constructor) {
|
||||||
let parent = editor;
|
let parent = editor;
|
||||||
name = parent.name + "/" + name;
|
name = parent.name + "/" + name;
|
||||||
|
|
||||||
|
this.autoComplete = options.get("autocomplete").getKey(name);
|
||||||
|
this.sortResults = options.get("wildsort").getKey(name);
|
||||||
|
this.wildcase = options.get("wildcase").getKey(name);
|
||||||
|
|
||||||
this.contexts = parent.contexts;
|
this.contexts = parent.contexts;
|
||||||
if (name in this.contexts)
|
if (name in this.contexts)
|
||||||
self = this.contexts[name];
|
self = this.contexts[name];
|
||||||
@@ -146,6 +151,8 @@ const CompletionContext = Class("CompletionContext", {
|
|||||||
this.top = this;
|
this.top = this;
|
||||||
this.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
|
this.__defineGetter__("incomplete", function () this.contextList.some(function (c) c.parent && c.incomplete));
|
||||||
this.__defineGetter__("waitingForTab", function () this.contextList.some(function (c) c.parent && c.waitingForTab));
|
this.__defineGetter__("waitingForTab", function () this.contextList.some(function (c) c.parent && c.waitingForTab));
|
||||||
|
this.__defineSetter__("incomplete", function (val) {});
|
||||||
|
this.__defineSetter__("waitingForTab", function (val) {});
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -333,7 +340,7 @@ const CompletionContext = Class("CompletionContext", {
|
|||||||
get ignoreCase() {
|
get ignoreCase() {
|
||||||
if ("_ignoreCase" in this)
|
if ("_ignoreCase" in this)
|
||||||
return this._ignoreCase;
|
return this._ignoreCase;
|
||||||
let mode = options["wildcase"];
|
let mode = this.wildcase;
|
||||||
if (mode == "match")
|
if (mode == "match")
|
||||||
return this._ignoreCase = false;
|
return this._ignoreCase = false;
|
||||||
if (mode == "ignore")
|
if (mode == "ignore")
|
||||||
@@ -367,7 +374,7 @@ const CompletionContext = Class("CompletionContext", {
|
|||||||
if (this.maxItems)
|
if (this.maxItems)
|
||||||
filtered = filtered.slice(0, this.maxItems);
|
filtered = filtered.slice(0, this.maxItems);
|
||||||
|
|
||||||
if (options.get("wildoptions").has("sort") && this.compare)
|
if (this.sortResults && this.compare)
|
||||||
filtered.sort(this.compare);
|
filtered.sort(this.compare);
|
||||||
let quote = this.quote;
|
let quote = this.quote;
|
||||||
if (quote)
|
if (quote)
|
||||||
@@ -498,8 +505,14 @@ const CompletionContext = Class("CompletionContext", {
|
|||||||
completer = self[completer];
|
completer = self[completer];
|
||||||
let context = CompletionContext(this, name, offset);
|
let context = CompletionContext(this, name, offset);
|
||||||
this.contextList.push(context);
|
this.contextList.push(context);
|
||||||
if (completer)
|
|
||||||
|
if (!context.autoComplete && !context.tabPressed && context.editor)
|
||||||
|
context.waitingForTab = true;
|
||||||
|
else if (completer)
|
||||||
return completer.apply(self || this, [context].concat(Array.slice(arguments, arguments.callee.length)));
|
return completer.apply(self || this, [context].concat(Array.slice(arguments, arguments.callee.length)));
|
||||||
|
|
||||||
|
if (completer)
|
||||||
|
return null;
|
||||||
return context;
|
return context;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -741,6 +754,80 @@ const Completion = Module("completion", {
|
|||||||
//}}}
|
//}}}
|
||||||
}, {
|
}, {
|
||||||
UrlCompleter: Struct("name", "description", "completer")
|
UrlCompleter: Struct("name", "description", "completer")
|
||||||
|
}, {
|
||||||
|
commands: function () {
|
||||||
|
commands.add(["contexts"],
|
||||||
|
"List the completion contexts used during completion of an ex command",
|
||||||
|
function (args) {
|
||||||
|
commandline.echo(template.commandOutput(
|
||||||
|
<div highlight="Completions">
|
||||||
|
{ template.completionRow(["Context", "Title"], "CompTitle") }
|
||||||
|
{ template.map(completion.contextList || [], function (item) template.completionRow(item, "CompItem")) }
|
||||||
|
</div>),
|
||||||
|
null, commandline.FORCE_MULTILINE);
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
argCount: "1",
|
||||||
|
completer: function (context, args) {
|
||||||
|
let PREFIX = "/ex/contexts";
|
||||||
|
context.fork("ex", 0, completion, "ex");
|
||||||
|
completion.contextList = [[k.substr(PREFIX.length), v.title[0]] for ([k, v] in iter(context.contexts)) if (k.substr(0, PREFIX.length) == PREFIX)];
|
||||||
|
},
|
||||||
|
literal: 0
|
||||||
|
});
|
||||||
|
},
|
||||||
|
options: function () {
|
||||||
|
options.add(["autocomplete", "au"],
|
||||||
|
"Automatically update the completion list on any key press",
|
||||||
|
"regexlist", ".*");
|
||||||
|
|
||||||
|
options.add(["complete", "cpt"],
|
||||||
|
"Items which are completed at the :open prompts",
|
||||||
|
"charlist", typeof(config.defaults["complete"]) == "string" ? config.defaults["complete"] : "slf",
|
||||||
|
{
|
||||||
|
completer: function (context) array(values(completion.urlCompleters))
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["wildcase", "wic"],
|
||||||
|
"Completion case matching mode",
|
||||||
|
"regexmap", "smart",
|
||||||
|
{
|
||||||
|
completer: function () [
|
||||||
|
["smart", "Case is significant when capital letters are typed"],
|
||||||
|
["match", "Case is always significant"],
|
||||||
|
["ignore", "Case is never significant"]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["wildmode", "wim"],
|
||||||
|
"Define how command line completion works",
|
||||||
|
"stringlist", "list:full",
|
||||||
|
{
|
||||||
|
completer: function (context) [
|
||||||
|
// Why do we need ""?
|
||||||
|
// Because its description is useful during completion. --Kris
|
||||||
|
["", "Complete only the first match"],
|
||||||
|
["full", "Complete the next full match"],
|
||||||
|
["longest", "Complete to longest common string"],
|
||||||
|
["list", "If more than one match, list all matches"],
|
||||||
|
["list:full", "List all and complete first match"],
|
||||||
|
["list:longest", "List all and complete common string"]
|
||||||
|
],
|
||||||
|
checkHas: function (value, val) {
|
||||||
|
let [first, second] = value.split(":", 2);
|
||||||
|
return first == val || second == val;
|
||||||
|
},
|
||||||
|
has: function () {
|
||||||
|
test = function (val) this.values.some(function (value) this.checkHas(value, val), this);
|
||||||
|
return Array.some(arguments, test, this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
options.add(["wildsort", "wis"],
|
||||||
|
"Regexp list of which contexts to sort",
|
||||||
|
"regexlist", ".*");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// vim: set fdm=marker sw=4 ts=4 et:
|
// vim: set fdm=marker sw=4 ts=4 et:
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
<xsl:variable name="type" select="preceding-sibling::liberator:type[1] | following-sibling::liberator:type[1]"/>
|
<xsl:variable name="type" select="preceding-sibling::liberator:type[1] | following-sibling::liberator:type[1]"/>
|
||||||
<span liberator:highlight="HelpDefault">(default:<xsl:text> </xsl:text>
|
<span liberator:highlight="HelpDefault">(default:<xsl:text> </xsl:text>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test="starts-with($type, 'string')">
|
<xsl:when test="starts-with($type, 'string') or starts-with($type, 'regex')">
|
||||||
<span liberator:highlight="HelpString"><xsl:apply-templates/></span>
|
<span liberator:highlight="HelpString"><xsl:apply-templates/></span>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
<xsl:otherwise>
|
<xsl:otherwise>
|
||||||
@@ -351,9 +351,13 @@
|
|||||||
|
|
||||||
<xsl:template match="liberator:ex" mode="pass-2">
|
<xsl:template match="liberator:ex" mode="pass-2">
|
||||||
<span liberator:highlight="HelpEx">
|
<span liberator:highlight="HelpEx">
|
||||||
<xsl:call-template name="linkify-tag">
|
<xsl:variable name="tag" select="str:tokenize(text(), ' [!')[1]"/>
|
||||||
<xsl:with-param name="contents" select="."/>
|
<a href="liberator://help-tag/{$tag}" style="color: inherit;">
|
||||||
</xsl:call-template>
|
<xsl:if test="contains($tags, concat(' ', $tag, ' '))">
|
||||||
|
<xsl:attribute name="href">#<xsl:value-of select="$tag"/></xsl:attribute>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:apply-templates/>
|
||||||
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ const File = Class("File", {
|
|||||||
*/
|
*/
|
||||||
MODE_EXCL: 0x80,
|
MODE_EXCL: 0x80,
|
||||||
|
|
||||||
expandPathList: function (list) list.split(",").map(this.expandPath).join(","),
|
expandPathList: function (list) list.map(this.expandPath),
|
||||||
|
|
||||||
expandPath: function (path, relative) {
|
expandPath: function (path, relative) {
|
||||||
|
|
||||||
@@ -1028,8 +1028,8 @@ lookup:
|
|||||||
b.isdir - a.isdir || String.localeCompare(a.text, b.text);
|
b.isdir - a.isdir || String.localeCompare(a.text, b.text);
|
||||||
|
|
||||||
if (options["wildignore"]) {
|
if (options["wildignore"]) {
|
||||||
let wigRegexp = RegExp("(^" + options.get("wildignore").values.join("|") + ")$");
|
let wig = options.get("wildignore");
|
||||||
context.filters.push(function ({item: f}) f.isDirectory() || !wigRegexp.test(f.leafName));
|
context.filters.push(function ({item: f}) f.isDirectory() || !wig.getKey(this.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// context.background = true;
|
// context.background = true;
|
||||||
@@ -1099,6 +1099,10 @@ lookup:
|
|||||||
options.add(["shellcmdflag", "shcf"],
|
options.add(["shellcmdflag", "shcf"],
|
||||||
"Flag passed to shell when executing :! and :run commands",
|
"Flag passed to shell when executing :! and :run commands",
|
||||||
"string", shellcmdflag);
|
"string", shellcmdflag);
|
||||||
|
|
||||||
|
options.add(["wildignore", "wig"],
|
||||||
|
"List of file patterns to ignore when completing files",
|
||||||
|
"regexlist", "");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
* getter - see {@link Option#getter}
|
* getter - see {@link Option#getter}
|
||||||
* completer - see {@link Option#completer}
|
* completer - see {@link Option#completer}
|
||||||
* valdator - see {@link Option#validator}
|
* valdator - see {@link Option#validator}
|
||||||
* checkHas - see {@link Option#checkHas}
|
|
||||||
* @optional
|
* @optional
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@@ -33,6 +32,17 @@ const Option = Class("Option", {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
||||||
|
if (this.type in Option.getKey)
|
||||||
|
this.getKey = Option.getKey[this.type];
|
||||||
|
|
||||||
|
if (this.type in Option.parseValues)
|
||||||
|
this.parseValues = Option.parseValues[this.type];
|
||||||
|
|
||||||
|
if (this.type in Option.joinValues)
|
||||||
|
this.joinValues = Option.joinValues[this.type];
|
||||||
|
|
||||||
|
this._op = Option.ops[this.type];
|
||||||
|
|
||||||
if (arguments.length > 3)
|
if (arguments.length > 3)
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
|
|
||||||
@@ -44,7 +54,7 @@ const Option = Class("Option", {
|
|||||||
this.names = array([name, "no" + name] for (name in values(names))).flatten().__proto__;
|
this.names = array([name, "no" + name] for (name in values(names))).flatten().__proto__;
|
||||||
|
|
||||||
if (this.globalValue == undefined)
|
if (this.globalValue == undefined)
|
||||||
this.globalValue = this.defaultValue;
|
this.globalValue = this.parseValues(this.defaultValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @property {value} The option's global value. @see #scope */
|
/** @property {value} The option's global value. @see #scope */
|
||||||
@@ -58,13 +68,7 @@ const Option = Class("Option", {
|
|||||||
* @param {value} value The option value.
|
* @param {value} value The option value.
|
||||||
* @returns {value|string[]}
|
* @returns {value|string[]}
|
||||||
*/
|
*/
|
||||||
parseValues: function (value) {
|
parseValues: function (value) value,
|
||||||
if (this.type == "stringlist")
|
|
||||||
return (value === "") ? [] : value.split(",");
|
|
||||||
if (this.type == "charlist")
|
|
||||||
return Array.slice(value);
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <b>values</b> packed in the appropriate format for the option
|
* Returns <b>values</b> packed in the appropriate format for the option
|
||||||
@@ -73,16 +77,10 @@ const Option = Class("Option", {
|
|||||||
* @param {value|string[]} values The option value.
|
* @param {value|string[]} values The option value.
|
||||||
* @returns {value}
|
* @returns {value}
|
||||||
*/
|
*/
|
||||||
joinValues: function (values) {
|
joinValues: function (vals) vals,
|
||||||
if (this.type == "stringlist")
|
|
||||||
return values.join(",");
|
|
||||||
if (this.type == "charlist")
|
|
||||||
return values.join("");
|
|
||||||
return values;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** @property {value|string[]} The option value or array of values. */
|
/** @property {value|string[]} The option value or array of values. */
|
||||||
get values() this.parseValues(this.value),
|
get values() this.getValues(this.scope),
|
||||||
set values(values) this.setValues(values, this.scope),
|
set values(values) this.setValues(values, this.scope),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,7 +91,26 @@ const Option = Class("Option", {
|
|||||||
* {@link Option#scope}).
|
* {@link Option#scope}).
|
||||||
* @returns {value|string[]}
|
* @returns {value|string[]}
|
||||||
*/
|
*/
|
||||||
getValues: function (scope) this.parseValues(this.get(scope)),
|
getValues: function (scope) {
|
||||||
|
if (scope) {
|
||||||
|
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
scope = this.scope;
|
||||||
|
|
||||||
|
let values;
|
||||||
|
|
||||||
|
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||||
|
values = tabs.options[this.name];
|
||||||
|
if ((scope & Option.SCOPE_GLOBAL) && (values == undefined))
|
||||||
|
values = this.globalValue;
|
||||||
|
|
||||||
|
if (this.getter)
|
||||||
|
return liberator.trapErrors(this.getter, this, values);
|
||||||
|
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the option's value from an array of values if the option type is
|
* Sets the option's value from an array of values if the option type is
|
||||||
@@ -102,8 +119,22 @@ const Option = Class("Option", {
|
|||||||
* @param {number} scope The scope to apply these values to (see
|
* @param {number} scope The scope to apply these values to (see
|
||||||
* {@link Option#scope}).
|
* {@link Option#scope}).
|
||||||
*/
|
*/
|
||||||
setValues: function (values, scope) {
|
setValues: function (newValues, scope, skipGlobal) {
|
||||||
this.set(this.joinValues(values), scope || this.scope);
|
scope = scope || this.scope;
|
||||||
|
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.setter)
|
||||||
|
newValues = liberator.trapErrors(this.setter, this, newValues);
|
||||||
|
if (newValues === undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
||||||
|
tabs.options[this.name] = newValues;
|
||||||
|
if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal)
|
||||||
|
this.globalValue = newValues;
|
||||||
|
|
||||||
|
this.hasChanged = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,26 +146,7 @@ const Option = Class("Option", {
|
|||||||
* {@link Option#scope}).
|
* {@link Option#scope}).
|
||||||
* @returns {value}
|
* @returns {value}
|
||||||
*/
|
*/
|
||||||
get: function (scope) {
|
get: function (scope) this.joinValues(this.getValues(scope)),
|
||||||
if (scope) {
|
|
||||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scope = this.scope;
|
|
||||||
|
|
||||||
let value;
|
|
||||||
|
|
||||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
|
||||||
value = tabs.options[this.name];
|
|
||||||
if ((scope & Option.SCOPE_GLOBAL) && (value == undefined))
|
|
||||||
value = this.globalValue;
|
|
||||||
|
|
||||||
if (this.getter)
|
|
||||||
return liberator.trapErrors(this.getter, this, value);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the option value to <b>newValue</b> for the specified <b>scope</b>.
|
* Sets the option value to <b>newValue</b> for the specified <b>scope</b>.
|
||||||
@@ -145,21 +157,7 @@ const Option = Class("Option", {
|
|||||||
* @param {number} scope The scope to apply this value to (see
|
* @param {number} scope The scope to apply this value to (see
|
||||||
* {@link Option#scope}).
|
* {@link Option#scope}).
|
||||||
*/
|
*/
|
||||||
set: function (newValue, scope) {
|
set: function (newValue, scope) this.setValues(this.parseValues(newValue), scope),
|
||||||
scope = scope || this.scope;
|
|
||||||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.setter)
|
|
||||||
newValue = liberator.trapErrors(this.setter, this, newValue);
|
|
||||||
|
|
||||||
if (liberator.has("tabs") && (scope & Option.SCOPE_LOCAL))
|
|
||||||
tabs.options[this.name] = newValue;
|
|
||||||
if ((scope & Option.SCOPE_GLOBAL) && newValue != this.globalValue)
|
|
||||||
this.globalValue = newValue;
|
|
||||||
|
|
||||||
this.hasChanged = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {value} The option's current value. The option's local value,
|
* @property {value} The option's current value. The option's local value,
|
||||||
@@ -169,21 +167,15 @@ const Option = Class("Option", {
|
|||||||
get value() this.get(),
|
get value() this.get(),
|
||||||
set value(val) this.set(val),
|
set value(val) this.set(val),
|
||||||
|
|
||||||
|
getKey: function (key) undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the option value contains one or more of the specified
|
* Returns whether the option value contains one or more of the specified
|
||||||
* arguments.
|
* arguments.
|
||||||
*
|
*
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
has: function () {
|
has: function () Array.some(arguments, function (val) this.values.indexOf(val) >= 0, this),
|
||||||
let self = this;
|
|
||||||
let test = function (val) values.indexOf(val) >= 0;
|
|
||||||
if (this.checkHas)
|
|
||||||
test = function (val) values.some(function (value) self.checkHas(value, val));
|
|
||||||
let values = this.values;
|
|
||||||
// return whether some argument matches
|
|
||||||
return Array.some(arguments, function (val) test(val));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this option is identified by <b>name</b>.
|
* Returns whether this option is identified by <b>name</b>.
|
||||||
@@ -216,97 +208,16 @@ const Option = Class("Option", {
|
|||||||
* @param {boolean} invert Whether this is an invert boolean operation.
|
* @param {boolean} invert Whether this is an invert boolean operation.
|
||||||
*/
|
*/
|
||||||
op: function (operator, values, scope, invert) {
|
op: function (operator, values, scope, invert) {
|
||||||
let newValue = null;
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
switch (this.type) {
|
let newValues = this._op(operator, values, scope, invert);
|
||||||
case "boolean":
|
|
||||||
if (operator != "=")
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (invert)
|
if (newValues == null)
|
||||||
newValue = !this.value;
|
|
||||||
else
|
|
||||||
newValue = values;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "number":
|
|
||||||
// TODO: support floats? Validators need updating.
|
|
||||||
if (!/^[+-]?(?:0x[0-9a-f]+|0[0-7]+|0|[1-9]\d*)$/i.test(values))
|
|
||||||
return "E521: Number required after := " + this.name + "=" + values;
|
|
||||||
|
|
||||||
let value = parseInt(values/* deduce radix */);
|
|
||||||
|
|
||||||
switch (operator) {
|
|
||||||
case "+":
|
|
||||||
newValue = this.value + value;
|
|
||||||
break;
|
|
||||||
case "-":
|
|
||||||
newValue = this.value - value;
|
|
||||||
break;
|
|
||||||
case "^":
|
|
||||||
newValue = this.value * value;
|
|
||||||
break;
|
|
||||||
case "=":
|
|
||||||
newValue = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "charlist":
|
|
||||||
case "stringlist":
|
|
||||||
values = Array.concat(values);
|
|
||||||
switch (operator) {
|
|
||||||
case "+":
|
|
||||||
newValue = util.Array.uniq(Array.concat(this.values, values), true);
|
|
||||||
break;
|
|
||||||
case "^":
|
|
||||||
// NOTE: Vim doesn't prepend if there's a match in the current value
|
|
||||||
newValue = util.Array.uniq(Array.concat(values, this.values), true);
|
|
||||||
break;
|
|
||||||
case "-":
|
|
||||||
newValue = this.values.filter(function (item) values.indexOf(item) == -1);
|
|
||||||
break;
|
|
||||||
case "=":
|
|
||||||
newValue = values;
|
|
||||||
if (invert) {
|
|
||||||
let keepValues = this.values.filter(function (item) values.indexOf(item) == -1);
|
|
||||||
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
|
||||||
newValue = addValues.concat(keepValues);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "string":
|
|
||||||
switch (operator) {
|
|
||||||
case "+":
|
|
||||||
newValue = this.value + values;
|
|
||||||
break;
|
|
||||||
case "-":
|
|
||||||
newValue = this.value.replace(values, "");
|
|
||||||
break;
|
|
||||||
case "^":
|
|
||||||
newValue = values + this.value;
|
|
||||||
break;
|
|
||||||
case "=":
|
|
||||||
newValue = values;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "E685: Internal error: option type `" + this.type + "' not supported";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue == null)
|
|
||||||
return "Operator " + operator + " not supported for option type " + this.type;
|
return "Operator " + operator + " not supported for option type " + this.type;
|
||||||
if (!this.isValidValue(newValue))
|
|
||||||
|
if (!this.isValidValue(newValues))
|
||||||
return "E474: Invalid argument: " + values;
|
return "E474: Invalid argument: " + values;
|
||||||
this.setValues(newValue, scope);
|
|
||||||
|
this.setValues(newValues, scope);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -319,11 +230,13 @@ const Option = Class("Option", {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {string} The option's data type. One of:
|
* @property {string} The option's data type. One of:
|
||||||
* "boolean" - Boolean E.g. true
|
* "boolean" - Boolean, e.g., true
|
||||||
* "number" - Integer E.g. 1
|
* "number" - Integer, e.g., 1
|
||||||
* "string" - String E.g. "Vimperator"
|
* "string" - String, e.g., "Vimperator"
|
||||||
* "charlist" - Character list E.g. "rb"
|
* "charlist" - Character list, e.g., "rb"
|
||||||
* "stringlist" - String list E.g. "homepage,quickmark,tabopen,paste"
|
* "regexlist" - Regex list, e.g., "^foo,bar$"
|
||||||
|
* "stringmap" - String map, e.g., "key=v,foo=bar"
|
||||||
|
* "regexmap" - Regex map, e.g., "^key=v,foo$=bar"
|
||||||
*/
|
*/
|
||||||
type: null,
|
type: null,
|
||||||
|
|
||||||
@@ -370,12 +283,6 @@ const Option = Class("Option", {
|
|||||||
return Option.validateCompleter.apply(this, arguments);
|
return Option.validateCompleter.apply(this, arguments);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* @property The function called to determine whether the option already
|
|
||||||
* contains a specified value.
|
|
||||||
* @see #has
|
|
||||||
*/
|
|
||||||
checkHas: null,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {boolean} Set to true whenever the option is first set. This
|
* @property {boolean} Set to true whenever the option is first set. This
|
||||||
@@ -410,6 +317,131 @@ const Option = Class("Option", {
|
|||||||
*/
|
*/
|
||||||
SCOPE_BOTH: 3,
|
SCOPE_BOTH: 3,
|
||||||
|
|
||||||
|
parseRegex: function (val, result) {
|
||||||
|
let [, bang, val] = /^(!?)(.*)/.exec(val);
|
||||||
|
let re = RegExp(val);
|
||||||
|
re.bang = bang;
|
||||||
|
re.result = arguments.length == 2 ? result : !bang;
|
||||||
|
return re;
|
||||||
|
},
|
||||||
|
unparseRegex: function (re) re.bang + re.source + (typeof re.result == "string" ? "=" + re.result : ""),
|
||||||
|
|
||||||
|
getKey: {
|
||||||
|
stringlist: function (k) this.values.indexOf(k) >= 0,
|
||||||
|
regexlist: function (k) {
|
||||||
|
for (let re in values(this.values))
|
||||||
|
if (re.test(k))
|
||||||
|
return re.result;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
joinValues: {
|
||||||
|
charlist: function (vals) vals.join(""),
|
||||||
|
stringlist: function (vals) vals.join(","),
|
||||||
|
stringmap: function (vals) [k + "=" + v for ([k, v] in Iterator(vals))].join(","),
|
||||||
|
regexlist: function (vals) vals.map(Option.unparseRegex).join(","),
|
||||||
|
},
|
||||||
|
|
||||||
|
parseValues: {
|
||||||
|
number: function (value) Number(value),
|
||||||
|
boolean: function (value) value == "true" || value == true ? true : false,
|
||||||
|
charlist: function (value) Array.slice(value),
|
||||||
|
stringlist: function (value) (value === "") ? [] : value.split(","),
|
||||||
|
stringmap: function (value) array(v.split("=") for (v in values(value.split(",")))).toObject(),
|
||||||
|
regexlist: function (value) (value === "") ? [] : value.split(",").map(Option.parseRegex),
|
||||||
|
regexmap: function (value) value.split(",").map(function (v) v.split("="))
|
||||||
|
.map(function ([k, v]) v != null ? Option.parseRegex(k, v) : Option.parseRegex('.?', k))
|
||||||
|
},
|
||||||
|
|
||||||
|
ops: {
|
||||||
|
boolean: function (operator, values, scope, invert) {
|
||||||
|
if (operator != "=")
|
||||||
|
return null;
|
||||||
|
if (invert)
|
||||||
|
return !this.value;
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
|
number: function (operator, values, scope, invert) {
|
||||||
|
// TODO: support floats? Validators need updating.
|
||||||
|
if (!/^[+-]?(?:0x[0-9a-f]+|0[0-7]*|[1-9]+)$/i.test(values))
|
||||||
|
return "E521: Number required after := " + this.name + "=" + values;
|
||||||
|
|
||||||
|
let value = parseInt(values);
|
||||||
|
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
return this.value + value;
|
||||||
|
case "-":
|
||||||
|
return this.value - value;
|
||||||
|
case "^":
|
||||||
|
return this.value * value;
|
||||||
|
case "=":
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
stringmap: function (operator, values, scope, invert) {
|
||||||
|
values = Array.concat(values);
|
||||||
|
orig = [k + "=" + v for ([k, v] in Iterator(this.values))];
|
||||||
|
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
return util.Array.uniq(Array.concat(orig, values), true);
|
||||||
|
case "^":
|
||||||
|
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||||||
|
return util.Array.uniq(Array.concat(values, orig), true);
|
||||||
|
case "-":
|
||||||
|
return orig.filter(function (item) values.indexOf(item) == -1);
|
||||||
|
case "=":
|
||||||
|
if (invert) {
|
||||||
|
let keepValues = orig.filter(function (item) values.indexOf(item) == -1);
|
||||||
|
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
||||||
|
return addValues.concat(keepValues);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
stringlist: function (operator, values, scope, invert) {
|
||||||
|
values = Array.concat(values);
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
return util.Array.uniq(Array.concat(this.values, values), true);
|
||||||
|
case "^":
|
||||||
|
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||||||
|
return util.Array.uniq(Array.concat(values, this.values), true);
|
||||||
|
case "-":
|
||||||
|
return this.values.filter(function (item) values.indexOf(item) == -1);
|
||||||
|
case "=":
|
||||||
|
if (invert) {
|
||||||
|
let keepValues = this.values.filter(function (item) values.indexOf(item) == -1);
|
||||||
|
let addValues = values.filter(function (item) self.values.indexOf(item) == -1);
|
||||||
|
return addValues.concat(keepValues);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
string: function (operator, values, scope, invert) {
|
||||||
|
switch (operator) {
|
||||||
|
case "+":
|
||||||
|
return this.value + values;
|
||||||
|
case "-":
|
||||||
|
return this.value.replace(values, "");
|
||||||
|
case "^":
|
||||||
|
return values + this.value;
|
||||||
|
case "=":
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: Run this by default?
|
// TODO: Run this by default?
|
||||||
/**
|
/**
|
||||||
* Validates the specified <b>values</b> against values generated by the
|
* Validates the specified <b>values</b> against values generated by the
|
||||||
@@ -423,10 +455,21 @@ const Option = Class("Option", {
|
|||||||
let res = context.fork("", 0, this, this.completer);
|
let res = context.fork("", 0, this, this.completer);
|
||||||
if (!res)
|
if (!res)
|
||||||
res = context.allItems.items.map(function (item) [item.text]);
|
res = context.allItems.items.map(function (item) [item.text]);
|
||||||
|
if (this.type == "regexmap")
|
||||||
|
return Array.concat(values).every(function (re) res.some(function (item) item[0] == re.result));
|
||||||
return Array.concat(values).every(function (value) res.some(function (item) item[0] == value));
|
return Array.concat(values).every(function (value) res.some(function (item) item[0] == value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Option.joinValues["regexmap"] = Option.joinValues["regexlist"];
|
||||||
|
|
||||||
|
Option.getKey["charlist"] = Option.getKey["stringlist"];
|
||||||
|
Option.getKey["regexmap"] = Option.getKey["regexlist"];
|
||||||
|
|
||||||
|
Option.ops["charlist"] = Option.ops["stringlist"];
|
||||||
|
Option.ops["regexlist"] = Option.ops["stringlist"];
|
||||||
|
Option.ops["regexmap"] = Option.ops["stringlist"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @instance options
|
* @instance options
|
||||||
*/
|
*/
|
||||||
@@ -462,7 +505,7 @@ const Options = Module("options", {
|
|||||||
// Trigger any setters.
|
// Trigger any setters.
|
||||||
let opt = options.get(option);
|
let opt = options.get(option);
|
||||||
if (event == "change" && opt)
|
if (event == "change" && opt)
|
||||||
opt.set(opt.value, Option.SCOPE_GLOBAL);
|
opt.setValues(opt.globalValue, Option.SCOPE_GLOBAL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.newMap("options", { store: false });
|
storage.newMap("options", { store: false });
|
||||||
@@ -1008,8 +1051,6 @@ const Options = Module("options", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// write access
|
// write access
|
||||||
// NOTE: the behavior is generally Vim compatible but could be
|
|
||||||
// improved. i.e. Vim's behavior is pretty sloppy to no real benefit
|
|
||||||
else {
|
else {
|
||||||
option.setFrom = modifiers.setFrom || null;
|
option.setFrom = modifiers.setFrom || null;
|
||||||
|
|
||||||
@@ -1017,7 +1058,12 @@ const Options = Module("options", {
|
|||||||
liberator.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
|
liberator.assert(!opt.valueGiven, "E474: Invalid argument: " + arg);
|
||||||
opt.values = !opt.unsetBoolean;
|
opt.values = !opt.unsetBoolean;
|
||||||
}
|
}
|
||||||
let res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
|
try {
|
||||||
|
var res = opt.option.op(opt.operator || "=", opt.values, opt.scope, opt.invert);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
res = e;
|
||||||
|
}
|
||||||
if (res)
|
if (res)
|
||||||
liberator.echoerr(res);
|
liberator.echoerr(res);
|
||||||
}
|
}
|
||||||
@@ -1244,8 +1290,15 @@ const Options = Module("options", {
|
|||||||
if (!completer)
|
if (!completer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let curValues = curValue != null ? opt.parseValues(curValue) : opt.values;
|
try {
|
||||||
let newValues = opt.parseValues(context.filter);
|
var curValues = curValue != null ? opt.parseValues(curValue) : opt.values;
|
||||||
|
var newValues = opt.parseValues(context.filter);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
context.message = "Error: " + e;
|
||||||
|
context.completions = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let len = context.filter.length;
|
let len = context.filter.length;
|
||||||
switch (opt.type) {
|
switch (opt.type) {
|
||||||
@@ -1253,9 +1306,18 @@ const Options = Module("options", {
|
|||||||
if (!completer)
|
if (!completer)
|
||||||
completer = function () [["true", ""], ["false", ""]];
|
completer = function () [["true", ""], ["false", ""]];
|
||||||
break;
|
break;
|
||||||
|
case "regexlist":
|
||||||
|
newValues = context.filter.split(",");
|
||||||
|
// Fallthrough
|
||||||
case "stringlist":
|
case "stringlist":
|
||||||
let target = newValues.pop();
|
let target = newValues.pop() || "";
|
||||||
len = target ? target.length : 0;
|
len = target.length;
|
||||||
|
break;
|
||||||
|
case "stringmap":
|
||||||
|
case "regexmap":
|
||||||
|
let vals = context.filter.split(",");
|
||||||
|
target = vals.pop() || "";
|
||||||
|
len = target.length - (target.indexOf("=") + 1);
|
||||||
break;
|
break;
|
||||||
case "charlist":
|
case "charlist":
|
||||||
len = 0;
|
len = 0;
|
||||||
@@ -1268,9 +1330,10 @@ const Options = Module("options", {
|
|||||||
let completions = completer(context);
|
let completions = completer(context);
|
||||||
if (!completions)
|
if (!completions)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Not Vim compatible, but is a significant enough improvement
|
// Not Vim compatible, but is a significant enough improvement
|
||||||
// that it's worth breaking compatibility.
|
// that it's worth breaking compatibility.
|
||||||
if (newValues instanceof Array) {
|
if (isarray(newValues)) {
|
||||||
completions = completions.filter(function (val) newValues.indexOf(val[0]) == -1);
|
completions = completions.filter(function (val) newValues.indexOf(val[0]) == -1);
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case "+":
|
case "+":
|
||||||
|
|||||||
@@ -213,10 +213,9 @@ const Sanitizer = Module("sanitizer", {
|
|||||||
{
|
{
|
||||||
setter: function (values) {
|
setter: function (values) {
|
||||||
for (let [, pref] in Iterator(sanitizer.prefNames)) {
|
for (let [, pref] in Iterator(sanitizer.prefNames)) {
|
||||||
continue;
|
|
||||||
options.setPref(pref, false);
|
options.setPref(pref, false);
|
||||||
|
|
||||||
for (let [, value] in Iterator(this.parseValues(values))) {
|
for (let [, value] in Iterator(values)) {
|
||||||
if (Sanitizer.prefToArg(pref) == value) {
|
if (Sanitizer.prefToArg(pref) == value) {
|
||||||
options.setPref(pref, true);
|
options.setPref(pref, true);
|
||||||
break;
|
break;
|
||||||
@@ -226,7 +225,7 @@ const Sanitizer = Module("sanitizer", {
|
|||||||
|
|
||||||
return values;
|
return values;
|
||||||
},
|
},
|
||||||
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(Sanitizer.prefToArg).join(","),
|
getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(Sanitizer.prefToArg),
|
||||||
completer: function (value) [
|
completer: function (value) [
|
||||||
["cache", "Cache"],
|
["cache", "Cache"],
|
||||||
["commandline", "Command-line history"],
|
["commandline", "Command-line history"],
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ Highlights.prototype.CSS = <![CDATA[
|
|||||||
HelpString display: inline-block; color: green; font-weight: normal; vertical-align: text-top;
|
HelpString display: inline-block; color: green; font-weight: normal; vertical-align: text-top;
|
||||||
HelpString::before content: '"';
|
HelpString::before content: '"';
|
||||||
HelpString::after content: '"';
|
HelpString::after content: '"';
|
||||||
|
HelpString[delim]::before content: attr(delim);
|
||||||
|
HelpString[delim]::after content: attr(delim);
|
||||||
|
|
||||||
HelpHead,html|h1,liberator://help/* {
|
HelpHead,html|h1,liberator://help/* {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -1094,9 +1094,9 @@ const Tabs = Module("tabs", {
|
|||||||
"Where to show requested popup windows",
|
"Where to show requested popup windows",
|
||||||
"stringlist", "tab",
|
"stringlist", "tab",
|
||||||
{
|
{
|
||||||
setter: function (value) {
|
setter: function (values) {
|
||||||
let [open, restriction] = [1, 0];
|
let [open, restriction] = [1, 0];
|
||||||
for (let [, opt] in Iterator(this.parseValues(value))) {
|
for (let [, opt] in Iterator(values)) {
|
||||||
if (opt == "tab")
|
if (opt == "tab")
|
||||||
open = 3;
|
open = 3;
|
||||||
else if (opt == "window")
|
else if (opt == "window")
|
||||||
@@ -1107,7 +1107,7 @@ const Tabs = Module("tabs", {
|
|||||||
|
|
||||||
options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option.");
|
options.safeSetPref("browser.link.open_newwindow", open, "See 'popups' option.");
|
||||||
options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option.");
|
options.safeSetPref("browser.link.open_newwindow.restriction", restriction, "See 'popups' option.");
|
||||||
return value;
|
return values;
|
||||||
},
|
},
|
||||||
completer: function (context) [
|
completer: function (context) [
|
||||||
["tab", "Open popups in a new tab"],
|
["tab", "Open popups in a new tab"],
|
||||||
|
|||||||
@@ -194,8 +194,6 @@ const Template = Module("template", {
|
|||||||
return <>:{commandline.command}<br/>{xml}</>;
|
return <>:{commandline.command}<br/>{xml}</>;
|
||||||
},
|
},
|
||||||
|
|
||||||
// every item must have a .xml property which defines how to draw itself
|
|
||||||
// @param headers is an array of strings, the text for the header columns
|
|
||||||
genericTable: function genericTable(items, format) {
|
genericTable: function genericTable(items, format) {
|
||||||
completion.listCompleter(function (context) {
|
completion.listCompleter(function (context) {
|
||||||
context.filterFunc = null;
|
context.filterFunc = null;
|
||||||
|
|||||||
@@ -17,11 +17,27 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt>boolean</dt> <dd>can only be on or off</dd>
|
<dt>boolean</dt> <dd>Can only be on or off</dd>
|
||||||
<dt>number</dt> <dd>has a numeric value</dd>
|
<dt>number</dt> <dd>A numeric value</dd>
|
||||||
<dt>string</dt> <dd>has a string value</dd>
|
<dt>string</dt> <dd>A string value</dd>
|
||||||
<dt>charlist</dt> <dd>like a string but with unique characters</dd>
|
<dt>charlist</dt> <dd>A string containing a discrete set of distinct characters</dd>
|
||||||
<dt>stringlist</dt> <dd>a comma-separated list of strings</dd>
|
<dt>stringlist</dt> <dd>A comma-separated list of strings</dd>
|
||||||
|
<dt>stringmap</dt> <dd>A comma-separated list of key-value pairs, e.g., <str>key=val,foo=bar</str></dd>
|
||||||
|
<dt>regexlist</dt>
|
||||||
|
<dd>
|
||||||
|
A comma-separated list of regular expressions. Expressions may be
|
||||||
|
prefixed with a <em>!</em>, in which case the match will be negated. A
|
||||||
|
literal <em>!</em> at the begining of the expression may be matched with
|
||||||
|
<em>[!]</em>. Generally, the first matching regular expression is used.
|
||||||
|
</dd>
|
||||||
|
<dt>regexmap</dt>
|
||||||
|
<dd>
|
||||||
|
A combination of a <em>stringmap</em> and a <em>regexlist</em>. Each key
|
||||||
|
in the <a>key</a>=<a>value</a> pair is a regexp. If the regexp begins with a
|
||||||
|
<em>!</em>, the sense match is negated, such that a non-matching
|
||||||
|
expression will be considered a match and <html:i>vice versa</html:i>.
|
||||||
|
The first <a>key</a> to match yields value.
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<h2 tag="set-option E764">Setting options</h2>
|
<h2 tag="set-option E764">Setting options</h2>
|
||||||
@@ -293,6 +309,40 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags>'au' 'autocomplete'</tags>
|
||||||
|
<spec>'autocomplete' 'au'</spec>
|
||||||
|
<type>regexlist</type>
|
||||||
|
<default>.*</default>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
A list of regexps defining which completion contexts should be
|
||||||
|
autocompleted. When the value is non-empty, the completion list is
|
||||||
|
automatically opened along with the commandline. Thereafter, any key
|
||||||
|
press triggers a completion update for the matching contexts.
|
||||||
|
Non-matching contexts will only be updated when the tab key is
|
||||||
|
pressed. This option is useful for disabling autocompletion for
|
||||||
|
computationally intense contexts that don't perform well on your
|
||||||
|
system under load.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
To enable autocompletion for everything but <ex>:history</ex> or
|
||||||
|
<ex>:bmarks</ex>, you would choose a value such as,
|
||||||
|
<str delim="'">!/ex/bmarks,.?</str>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
Completion contexts have names very much like Unix path names. This
|
||||||
|
denotes the tree in which they're called. A completer will never be
|
||||||
|
called unless every completer preceding it in the tree was also
|
||||||
|
called. For example, if your completer excludes <str>/ex/</str>, it
|
||||||
|
will also exclude <str>/ex/bmarks</str>, and so on.
|
||||||
|
</note>
|
||||||
|
|
||||||
|
<p>See also <ex>:contexts</ex></p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>$CDPATH</tags>
|
<tags>$CDPATH</tags>
|
||||||
@@ -1249,41 +1299,50 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'wildcase' 'wic'</tags>
|
<tags>'wic' 'wildcase'</tags>
|
||||||
<spec>'wildcase' 'wic'</spec>
|
<spec>'wildcase' 'wic'</spec>
|
||||||
<type>string</type>
|
<type>regexmap</type>
|
||||||
<default>smart</default>
|
<default>smart</default>
|
||||||
<description>
|
<description>
|
||||||
<p>Defines how completions are matched with regard to character case. Possible values:</p>
|
<p>
|
||||||
|
Defines how completions are matched for a given completion context
|
||||||
|
with regard to character case.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Possible values:</p>
|
||||||
|
|
||||||
<dl>
|
<dl>
|
||||||
<dt><str>smart</str></dt> <dd>Case is significant when capital letters are typed</dd>
|
<dt><str>smart</str></dt> <dd>Case is significant when capital letters are typed</dd>
|
||||||
<dt><str>match</str></dt> <dd>Case is always significant</dd>
|
<dt><str>match</str></dt> <dd>Case is always significant</dd>
|
||||||
<dt><str>ignore</str></dt> <dd>Case is never significant</dd>
|
<dt><str>ignore</str></dt> <dd>Case is never significant</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
<p>See also <ex>:contexts</ex></p>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'wildignore' 'wig'</tags>
|
<tags>'wildignore' 'wig'</tags>
|
||||||
<spec>'wildignore' 'wig'</spec>
|
<spec>'wildignore' 'wig'</spec>
|
||||||
<type>stringlist</type>
|
<type>regexlist</type>
|
||||||
<default></default>
|
<default></default>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
List of file patterns to ignore when completing files. E.g., to ignore object
|
List of file patterns to ignore when completing files. E.g., to ignore object
|
||||||
files and Vim swap files
|
files and Vim swap files
|
||||||
<ex>:set wildignore=<str>.<em>\\.o,\\..</em>\\.s[a-z]\\<a>2</a></str></ex>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<code><ex>:set wildignore=<str delim="'">\.o$</str>,<str delim="'">^\..*\.s[a-z]<a>2</a>$</str></ex></code>
|
||||||
|
|
||||||
<note>Unlike Vim each pattern is a regex rather than a glob.</note>
|
<note>Unlike Vim each pattern is a regex rather than a glob.</note>
|
||||||
|
<note>
|
||||||
|
The only way to include a literal comma in a pattern is with the
|
||||||
|
escape <str>\u0044</str>.
|
||||||
|
</note>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'wim' 'wildmode'</tags>
|
<tags>'wim' 'wildmode'</tags>
|
||||||
<spec>'wildmode' 'wim'</spec>
|
<spec>'wildmode' 'wim'</spec>
|
||||||
@@ -1318,25 +1377,23 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'wop' 'wildoptions'</tags>
|
<tags>'wis' 'wildsort'</tags>
|
||||||
<spec>'wildoptions' 'wop'</spec>
|
<spec>'wildsort' 'wis'</spec>
|
||||||
<type>stringlist</type>
|
<type>regexlist</type>
|
||||||
<default></default>
|
<default>.*</default>
|
||||||
<description>
|
<description>
|
||||||
<p>A list of words that change how command-line completion is done.</p>
|
<p>
|
||||||
|
A list of regexps defining which completion contexts
|
||||||
|
should be sorted. The main purpose of this option is to
|
||||||
|
prevent sorting of certain completion lists that don't
|
||||||
|
perform well under load.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>Possible words:</p>
|
<p>See also <ex>:contexts</ex></p>
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>auto</dt> <dd>Automatically show completions while you are typing.</dd>
|
|
||||||
<dt>sort</dt> <dd>Always sort the completion list, overriding the <o>complete</o> option.</dd>
|
|
||||||
</dl>
|
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'wsp' 'wordseparators'</tags>
|
<tags>'wsp' 'wordseparators'</tags>
|
||||||
<spec>'wordseparators' 'wsp'</spec>
|
<spec>'wordseparators' 'wsp'</spec>
|
||||||
|
|||||||
@@ -17,16 +17,27 @@
|
|||||||
<tags>:beep</tags>
|
<tags>:beep</tags>
|
||||||
<spec>:beep</spec>
|
<spec>:beep</spec>
|
||||||
<description>
|
<description>
|
||||||
<p>Play a system beep.</p>
|
<p>
|
||||||
|
Play a system beep. This should not be used for any purpose other
|
||||||
|
than testing the visual bell.
|
||||||
|
</p>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-l> CTRL-L :redr :redraw]]></tags>
|
<tags>:contexts</tags>
|
||||||
<spec>:redr<oa>aw</oa></spec>
|
<spec>:contexts <a>ex-command</a></spec>
|
||||||
<description>
|
<description>
|
||||||
<p>Redraws the screen. Useful to update the screen halfway executing a script or function.</p>
|
<p>
|
||||||
|
Lists the completion contexts used during the completion of its
|
||||||
|
arguments. These context names are used in options such as
|
||||||
|
<o>autocomplete</o> and <o>wildcase</o>. Note that completion must
|
||||||
|
be triggered in order for this command to be effective, so if
|
||||||
|
autocompletion is not active, you'll need to press the
|
||||||
|
<k name="Tab"/> key at least once. You should also be aware that
|
||||||
|
this command is only useful from the commandline.
|
||||||
|
</p>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
@@ -46,6 +57,15 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags><![CDATA[<C-l> CTRL-L :redr :redraw]]></tags>
|
||||||
|
<spec>:redr<oa>aw</oa></spec>
|
||||||
|
<description>
|
||||||
|
<p>Redraws the screen. Useful to update the screen halfway executing a script or function.</p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>:run :! :!cmd</tags>
|
<tags>:run :! :!cmd</tags>
|
||||||
<spec>:!<a>cmd</a></spec>
|
<spec>:!<a>cmd</a></spec>
|
||||||
|
|||||||
Reference in New Issue
Block a user