X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Foptions.jsm;h=302a1d3400abd40c711470f66dfa84262083566e;hb=refs%2Fheads%2Fupstream;hp=a09fef257b3dc149ab1b8a53c1b20b054e2e0984;hpb=70740024f9c028c1fd63e1a1850ab062ff956054;p=dactyl.git diff --git a/common/modules/options.jsm b/common/modules/options.jsm index a09fef2..302a1d3 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -1,6 +1,6 @@ // Copyright (c) 2006-2008 by Martin Stubenschrott // Copyright (c) 2007-2011 by Doug Kearns -// Copyright (c) 2008-2011 by Kris Maglione +// Copyright (c) 2008-2014 by Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -8,12 +8,18 @@ try { -Components.utils.import("resource://dactyl/bootstrap.jsm"); defineModule("options", { exports: ["Option", "Options", "ValueError", "options"], - require: ["messages", "storage"], - use: ["commands", "completion", "config", "prefs", "services", "styles", "template", "util"] -}, this); + require: ["contexts", "messages", "storage"] +}); + +lazyRequire("cache", ["cache"]); +lazyRequire("config", ["config"]); +lazyRequire("commands", ["Commands"]); +lazyRequire("completion", ["CompletionContext"]); +lazyRequire("prefs", ["prefs"]); +lazyRequire("styles", ["Styles"]); +lazyRequire("template", ["template"]); /** @scope modules */ @@ -47,38 +53,19 @@ var Option = Class("Option", { init: function init(modules, names, description, defaultValue, extraInfo) { this.modules = modules; this.name = names[0]; - this.names = names; this.realNames = names; this.description = description; if (extraInfo) this.update(extraInfo); - if (Set.has(this.modules.config.defaults, this.name)) - defaultValue = this.modules.config.defaults[this.name]; - - if (defaultValue !== undefined) { - if (this.type === "string") - defaultValue = Commands.quote(defaultValue); - - if (isObject(defaultValue)) - defaultValue = iter(defaultValue).map(function (val) val.map(Option.quote).join(":")).join(","); - - if (isArray(defaultValue)) - defaultValue = defaultValue.map(Option.quote).join(","); - - this.defaultValue = this.parse(defaultValue); - } - - // add no{option} variant of boolean {option} to this.names - if (this.type == "boolean") - this.names = array([name, "no" + name] for (name in values(names))).flatten().array; + this._defaultValue = defaultValue; if (this.globalValue == undefined && !this.initialValue) this.globalValue = this.defaultValue; }, - magicalProperties: Set(["cleanupValue"]), + magicalProperties: RealSet(["cleanupValue"]), /** * @property {string} This option's description, as shown in :listoptions. @@ -88,7 +75,7 @@ var Option = Class("Option", { get helpTag() "'" + this.name + "'", initValue: function initValue() { - util.trapErrors(function () this.value = this.value, this); + util.trapErrors(() => { this.value = this.value; }); }, get isDefault() this.stringValue === this.stringDefaultValue, @@ -101,8 +88,17 @@ var Option = Class("Option", { }, /** @property {value} The option's global value. @see #scope */ - get globalValue() { try { return options.store.get(this.name, {}).value; } catch (e) { util.reportError(e); throw e; } }, - set globalValue(val) { options.store.set(this.name, { value: val, time: Date.now() }); }, + get globalValue() { + let val = options.store.get(this.name, {}).value; + if (val != null) + return val; + return this.globalValue = this.defaultValue; + }, + set globalValue(val) { + options.store.set(this.name, + { value: this.parse(this.stringify(val)), + time: Date.now() }); + }, /** * Returns *value* as an array of parsed values if the option type is @@ -113,6 +109,8 @@ var Option = Class("Option", { */ parse: function parse(value) Option.dequote(value), + parseKey: function parseKey(value) value, + /** * Returns *values* packed in the appropriate format for the option type. * @@ -146,6 +144,9 @@ var Option = Class("Option", { if ((scope & Option.SCOPE_GLOBAL) && (values == undefined)) values = this.globalValue; + if (hasOwnProperty(this, "_value")) + values = this._value; + if (this.getter) return util.trapErrors(this.getter, this, values); @@ -175,6 +176,7 @@ var Option = Class("Option", { */ if ((scope & Option.SCOPE_GLOBAL) && !skipGlobal) this.globalValue = newValues; + this._value = newValues; this.hasChanged = true; this.setFrom = null; @@ -209,7 +211,7 @@ var Option = Class("Option", { * * @returns {boolean} */ - has: function has() Array.some(arguments, function (val) this.value.indexOf(val) >= 0, this), + has: function has() Array.some(arguments, val => this.value.indexOf(val) >= 0), /** * Returns whether this option is identified by *name*. @@ -264,12 +266,13 @@ var Option = Class("Option", { return null; }, - // Properties {{{2 + // Properties {{{ /** @property {string} The option's canonical name. */ name: null, + /** @property {[string]} All names by which this option is identified. */ - names: null, + names: Class.Memoize(function () this.realNames), /** * @property {string} The option's data type. One of: @@ -323,14 +326,41 @@ var Option = Class("Option", { * references to a given domain from the given values. */ filterDomain: function filterDomain(host, values) - Array.filter(values, function (val) !this.domains([val]).some(function (val) util.isSubdomain(val, host)), this), + Array.filter(values, val => !this.domains([val]).some(val => util.isSubdomain(val, host))), /** * @property {value} The option's default value. This value will be used * unless the option is explicitly set either interactively or in an RC * file or plugin. */ - defaultValue: null, + defaultValue: Class.Memoize(function () { + let defaultValue = this._defaultValue; + delete this._defaultValue; + + if (hasOwnProperty(this.modules.config.optionDefaults, this.name)) + defaultValue = this.modules.config.optionDefaults[this.name]; + + if (defaultValue == null && this.getter) + defaultValue = this.getter(); + + if (defaultValue == undefined) + return null; + + if (this.type === "string") + defaultValue = Commands.quote(defaultValue); + + if (isArray(defaultValue)) + defaultValue = defaultValue.map(Option.quote).join(","); + else if (isObject(defaultValue)) + defaultValue = iter(defaultValue).map(val => val.map(v => Option.quote(v, /:/)) + .join(":")) + .join(","); + + if (isArray(defaultValue)) + defaultValue = defaultValue.map(Option.quote).join(","); + + return this.parse(defaultValue); + }), /** * @property {function} The function called when the option value is read. @@ -378,7 +408,8 @@ var Option = Class("Option", { hasChanged: false, /** - * Returns the timestamp when the option's value was last changed. + * @property {number} Returns the timestamp when the option's value was + * last changed. */ get lastSet() options.store.get(this.name).time, set lastSet(val) { options.store.set(this.name, { value: this.globalValue, time: Date.now() }); }, @@ -389,6 +420,7 @@ var Option = Class("Option", { */ setFrom: null + //}}} }, { /** * @property {number} Global option scope. @@ -425,21 +457,23 @@ var Option = Class("Option", { let re = util.regexp(Option.dequote(val), flags); re.bang = bang; re.result = result !== undefined ? result : !bang; + re.key = re.bang + Option.quote(util.regexp.getSource(re), /^!|:/); re.toString = function () Option.unparseRegexp(this, keepQuotes); return re; }, unparseRegexp: function unparseRegexp(re, quoted) re.bang + Option.quote(util.regexp.getSource(re), /^!|:/) + - (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result))), + (typeof re.result === "boolean" ? "" : ":" + (quoted ? re.result : Option.quote(re.result, /:/))), parseSite: function parseSite(pattern, result, rest) { if (isArray(rest)) // Called by Array.map result = undefined; let [, bang, filter] = /^(!?)(.*)/.exec(pattern); - filter = Option.dequote(filter); + filter = Option.dequote(filter).trim(); - let quote = this.keepQuotes ? util.identity : Option.quote; + let quote = this.keepQuotes ? v => v + : v => Option.quote(v, /:/); return update(Styles.matchFilter(filter), { bang: bang, @@ -454,11 +488,11 @@ var Option = Class("Option", { stringlist: function stringlist(k) this.value.indexOf(k) >= 0, get charlist() this.stringlist, - regexplist: function regexplist(k, default_) { + regexplist: function regexplist(k, default_=null) { for (let re in values(this.value)) if ((re.test || re).call(re, k)) return re.result; - return arguments.length > 1 ? default_ : null; + return default_; }, get regexpmap() this.regexplist, get sitelist() this.regexplist, @@ -466,7 +500,7 @@ var Option = Class("Option", { }, domains: { - sitelist: function (vals) array.compact(vals.map(function (site) util.getHost(site.filter))), + sitelist: function (vals) array.compact(vals.map(site => util.getHost(site.filter))), get sitemap() this.sitelist }, @@ -475,7 +509,7 @@ var Option = Class("Option", { stringlist: function (vals) vals.map(Option.quote).join(","), - stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v) for ([k, v] in Iterator(vals))].join(","), + stringmap: function (vals) [Option.quote(k, /:/) + ":" + Option.quote(v, /:/) for ([k, v] in Iterator(vals))].join(","), regexplist: function (vals) vals.join(","), get regexpmap() this.regexplist, @@ -495,7 +529,7 @@ var Option = Class("Option", { regexplist: function regexplist(value) (value === "") ? [] : Option.splitList(value, true) - .map(function (re) Option.parseRegexp(re, undefined, this.regexpFlags), this), + .map(re => Option.parseRegexp(re, undefined, this.regexpFlags)), sitelist: function sitelist(value) { if (value === "") @@ -532,8 +566,13 @@ var Option = Class("Option", { }, this)) }, + parseKey: { + number: Number, + boolean: function boolean(value) value == "true" || value == true ? true : false, + }, + testValues: { - regexpmap: function regexpmap(vals, validator) vals.every(function (re) validator(re.result)), + regexpmap: function regexpmap(vals, validator) vals.every(re => validator(re.result)), get sitemap() this.regexpmap, stringlist: function stringlist(vals, validator) vals.every(validator, this), stringmap: function stringmap(vals, validator) values(vals).every(validator, this) @@ -562,7 +601,7 @@ var Option = Class("Option", { return res; }, - quote: function quote(str, re) isArray(str) ? str.map(function (s) quote(s, re)).join(",") : + quote: function quote(str, re) isArray(str) ? str.map(s => quote(s, re)).join(",") : Commands.quoteArg[/[\s|"'\\,]|^$/.test(str) || re && re.test && re.test(str) ? (/[\b\f\n\r\t]/.test(str) ? '"' : "'") : ""](str, re), @@ -597,6 +636,23 @@ var Option = Class("Option", { return null; }, + string: function string(operator, values, scope, invert) { + if (invert) + return values[(values.indexOf(this.value) + 1) % values.length]; + + switch (operator) { + case "+": + return this.value + values; + case "-": + return this.value.replace(values, ""); + case "^": + return values + this.value; + case "=": + return values; + } + return null; + }, + stringmap: function stringmap(operator, values, scope, invert) { let res = update({}, this.value); @@ -628,8 +684,8 @@ var Option = Class("Option", { values = Array.concat(values); function uniq(ary) { - let seen = {}; - return ary.filter(function (elem) !Set.add(seen, elem)); + let seen = RealSet(); + return ary.filter(elem => !seen.add(elem)); } switch (operator) { @@ -639,11 +695,11 @@ var Option = Class("Option", { // NOTE: Vim doesn't prepend if there's a match in the current value return uniq(Array.concat(values, this.value), true); case "-": - return this.value.filter(function (item) !Set.has(this, item), Set(values)); + return this.value.filter(function (item) !this.has(item), RealSet(values)); case "=": if (invert) { - let keepValues = this.value.filter(function (item) !Set.has(this, item), Set(values)); - let addValues = values.filter(function (item) !Set.has(this, item), Set(this.value)); + let keepValues = this.value.filter(function (item) !this.has(item), RealSet(values)); + let addValues = values.filter(function (item) !this.has(item), RealSet(this.value)); return addValues.concat(keepValues); } return values; @@ -654,23 +710,7 @@ var Option = Class("Option", { get regexplist() this.stringlist, get regexpmap() this.stringlist, get sitelist() this.stringlist, - get sitemap() this.stringlist, - - string: function string(operator, values, scope, invert) { - if (invert) - return values[(values.indexOf(this.value) + 1) % values.length]; - switch (operator) { - case "+": - return this.value + values; - case "-": - return this.value.replace(values, ""); - case "^": - return values + this.value; - case "=": - return values; - } - return null; - } + get sitemap() this.stringlist }, validIf: function validIf(test, error) { @@ -686,23 +726,36 @@ var Option = Class("Option", { * @param {value|[string]} values The value or array of values to validate. * @returns {boolean} */ - validateCompleter: function validateCompleter(values) { - if (this.values) - var acceptable = this.values.array || this.values; - else { + validateCompleter: function validateCompleter(vals) { + function completions(extra) { let context = CompletionContext(""); - acceptable = context.fork("", 0, this, this.completer); - if (!acceptable) - acceptable = context.allItems.items.map(function (item) [item.text]); + return context.fork("", 0, this, this.completer, extra) || + context.allItems.items.map(item => [item.text]); + }; + + if (isObject(vals) && !isArray(vals)) { + let k = values(completions.call(this, { values: {} })).toObject(); + let v = values(completions.call(this, { value: "" })).toObject(); + + return Object.keys(vals).every(hasOwnProperty.bind(null, k)) && + values(vals).every(hasOwnProperty.bind(null, v)); } + if (this.values) + var acceptable = this.values.array || this.values; + else + acceptable = completions.call(this); + if (isArray(acceptable)) - acceptable = Set(acceptable.map(function ([k]) k)); + acceptable = RealSet(acceptable.map(([k]) => k)); + else + acceptable = RealSet(this.parseKey(k) + for (k of Object.keys(acceptable))); if (this.type === "regexpmap" || this.type === "sitemap") - return Array.concat(values).every(function (re) Set.has(acceptable, re.result)); + return Array.concat(vals).every(re => acceptable.has(re.result)); - return Array.concat(values).every(Set.has(acceptable)); + return Array.concat(vals).every(v => acceptable.has(v)); }, types: {} @@ -731,6 +784,9 @@ var Option = Class("Option", { if (type in Option.parse) class_.prototype.parse = Option.parse[type]; + if (type in Option.parseKey) + class_.prototype.parseKey = Option.parse[type]; + if (type in Option.stringify) class_.prototype.stringify = Option.stringify[type]; @@ -745,6 +801,23 @@ var Option = Class("Option", { EXPORTED_SYMBOLS.push(class_.className); }, this); +update(BooleanOption.prototype, { + names: Class.Memoize(function () + array.flatten([[name, "no" + name] for (name in values(this.realNames))])) +}); + +var OptionHive = Class("OptionHive", Contexts.Hive, { + init: function init(group) { + init.supercall(this, group); + this.values = {}; + this.has = v => hasOwnProperty(this.values, v); + }, + + add: function add(names, description, type, defaultValue, extraInfo) { + return this.modules.options.add(names, description, type, defaultValue, extraInfo); + } +}); + /** * @instance options */ @@ -752,6 +825,12 @@ var Options = Module("options", { Local: function Local(dactyl, modules, window) let ({ contexts } = modules) ({ init: function init() { const self = this; + + update(this, { + hives: contexts.Hives("options", Class("OptionHive", OptionHive, { modules: modules })), + user: contexts.hives.options.user + }); + this.needInit = []; this._options = []; this._optionMap = {}; @@ -764,17 +843,24 @@ var Options = Module("options", { opt.set(opt.globalValue, Option.SCOPE_GLOBAL, true); }, window); - services["dactyl:"].pages["options.dtd"] = function () [null, - util.makeDTD( - iter(([["option", o.name, "default"].join("."), - o.type === "string" ? o.defaultValue.replace(/'/g, "''") : - o.value === true ? "on" : - o.value === false ? "off" : o.stringDefaultValue] - for (o in self)), + modules.cache.register("options.dtd", + () => util.makeDTD( + iter(([["option", o.name, "default"].join("."), + o.type === "string" ? o.defaultValue.replace(/'/g, "''") : + o.defaultValue === true ? "on" : + o.defaultValue === false ? "off" : o.stringDefaultValue] + for (o in self)), - ([["option", o.name, "type"].join("."), o.type] for (o in self)), + ([["option", o.name, "type"].join("."), o.type] for (o in self)), - config.dtd))]; + config.dtd)), + true); + }, + + signals: { + "io.source": function ioSource(context, file, modTime) { + cache.flushEntry("options.dtd", modTime); + } }, dactyl: dactyl, @@ -793,33 +879,38 @@ var Options = Module("options", { function opts(opt) { for (let opt in Iterator(this)) { + if (filter && !filter(opt)) + continue; + if (!(opt.scope & scope)) + continue; + let option = { __proto__: opt, isDefault: opt.isDefault, default: opt.stringDefaultValue, pre: "\u00a0\u00a0", // Unicode nonbreaking space. - value: <> + value: [] }; - if (filter && !filter(opt)) - continue; - if (!(opt.scope & scope)) - continue; - if (opt.type == "boolean") { if (!opt.value) option.pre = "no"; option.default = (opt.defaultValue ? "" : "no") + opt.name; } - else if (isArray(opt.value)) - option.value = <>={template.map(opt.value, function (v) template.highlight(String(v)), <>, )}; + else if (isArray(opt.value) && opt.type != "charlist") + option.value = ["", "=", + template.map(opt.value, + v => template.highlight(String(v)), + ["", ",", + ["span", { style: "width: 0; display: inline-block" }, " "]])]; else - option.value = <>={template.highlight(opt.stringValue)}; + option.value = ["", "=", template.highlight(opt.stringValue)]; yield option; } }; - modules.commandline.commandOutput(template.options("Options", opts.call(this), this["verbose"] > 0)); + modules.commandline.commandOutput( + template.options("Options", opts.call(this), this["verbose"] > 0)); }, cleanup: function cleanup() { @@ -840,7 +931,11 @@ var Options = Module("options", { * @optional */ add: function add(names, description, type, defaultValue, extraInfo) { - const self = this; + if (!util.isDactyl(Components.stack.caller)) + deprecated.warn(add, "options.add", "group.options.add"); + + util.assert(type in Option.types, _("option.noSuchType", type), + false); if (!extraInfo) extraInfo = {}; @@ -853,9 +948,11 @@ var Options = Module("options", { this.remove(name); } - let closure = function () self._optionMap[name]; + let closure = () => this._optionMap[name]; + + memoize(this._optionMap, name, + function () Option.types[type](modules, names, description, defaultValue, extraInfo)); - memoize(this._optionMap, name, function () Option.types[type](modules, names, description, defaultValue, extraInfo)); for (let alias in values(names.slice(1))) memoize(this._optionMap, alias, closure); @@ -876,7 +973,7 @@ var Options = Module("options", { /** @property {Iterator(Option)} @private */ __iterator__: function __iterator__() - values(this._options.sort(function (a, b) String.localeCompare(a.name, b.name))), + values(this._options.sort((a, b) => String.localeCompare(a.name, b.name))), allPrefs: deprecated("prefs.getNames", function allPrefs() prefs.getNames.apply(prefs, arguments)), getPref: deprecated("prefs.get", function getPref() prefs.get.apply(prefs, arguments)), @@ -891,7 +988,7 @@ var Options = Module("options", { setPref: deprecated("prefs.set", function setPref() prefs.set.apply(prefs, arguments)), withContext: deprecated("prefs.withContext", function withContext() prefs.withContext.apply(prefs, arguments)), - cleanupPrefs: Class.memoize(function () localPrefs.Branch("cleanup.option.")), + cleanupPrefs: Class.Memoize(() => config.prefs.Branch("cleanup.option.")), cleanup: function cleanup(reason) { if (~["disable", "uninstall"].indexOf(reason)) @@ -969,7 +1066,8 @@ var Options = Module("options", { res.optionValue = res.option.get(res.scope); try { - res.values = res.option.parse(res.value); + if (!res.invert || res.option.type != "number") // Hack. + res.values = res.option.parse(res.value); } catch (e) { res.error = e; @@ -986,7 +1084,7 @@ var Options = Module("options", { */ remove: function remove(name) { let opt = this.get(name); - this._options = this._options.filter(function (o) o != opt); + this._options = this._options.filter(o => o != opt); for (let name in values(opt.names)) delete this._optionMap[name]; }, @@ -998,36 +1096,19 @@ var Options = Module("options", { commands: function initCommands(dactyl, modules, window) { const { commands, contexts, options } = modules; - let args = { - getMode: function (args) findMode(args["-mode"]), - iterate: function (args) { - for (let map in mappings.iterate(this.getMode(args))) - for (let name in values(map.names)) - yield { name: name, __proto__: map }; - }, - format: { - description: function (map) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <> - {options.get("passkeys").has(map.name) - ? ({ - tempate.linkifyHelp(_("option.passkeys.passedBy")) - }) - : <>} - {template.linkifyHelp(map.description)} - ) - } - }; - dactyl.addUsageCommand({ name: ["listo[ptions]", "lo"], description: "List all options along with their short descriptions", index: "option", iterate: function (args) options, format: { - description: function (opt) (XML.ignoreWhitespace = false, XML.prettyPrinting = false, <> - {opt.scope == Option.SCOPE_LOCAL - ? ({_("option.bufferLocal")}) : ""} - {template.linkifyHelp(opt.description)} - ), + description: function (opt) [ + opt.scope == Option.SCOPE_LOCAL + ? ["span", { highlight: "URLExtra" }, + "(" + _("option.bufferLocal") + ")"] + : "", + template.linkifyHelp(opt.description) + ], help: function (opt) "'" + opt.name + "'" } }); @@ -1039,12 +1120,14 @@ var Options = Module("options", { let list = []; function flushList() { - let names = Set(list.map(function (opt) opt.option ? opt.option.name : "")); + let names = RealSet(list.map(opt => opt.option ? opt.option.name : "")); if (list.length) - if (list.some(function (opt) opt.all)) - options.list(function (opt) !(list[0].onlyNonDefault && opt.isDefault), list[0].scope); + if (list.some(opt => opt.all)) + options.list(opt => !(list[0].onlyNonDefault && opt.isDefault), + list[0].scope); else - options.list(function (opt) Set.has(names, opt.name), list[0].scope); + options.list(opt => names.has(opt.name), + list[0].scope); list = []; } @@ -1060,11 +1143,12 @@ var Options = Module("options", { } else { var [matches, name, postfix, valueGiven, operator, value] = - arg.match(/^\s*?([^=]+?)([?&!])?\s*(([-+^]?)=(.*))?\s*$/); + arg.match(/^\s*?((?:[^=\\']|\\.|'[^']*')+?)([?&!])?\s*(([-+^]?)=(.*))?\s*$/); reset = (postfix == "&"); invertBoolean = (postfix == "!"); } + name = Option.dequote(name); if (name == "all" && reset) modules.commandline.input(_("pref.prompt.resetAll", config.host) + " ", function (resp) { @@ -1155,11 +1239,11 @@ var Options = Module("options", { context.advance(filter.length); filter = filter.substr(0, filter.length - 1); - context.pushProcessor(0, function (item, text, next) next(item, text.substr(0, 100))); + context.pushProcessor(0, (item, text, next) => next(item, text.substr(0, 100))); context.completions = [ [prefs.get(filter), _("option.currentValue")], [prefs.defaults.get(filter), _("option.defaultValue")] - ].filter(function (k) k[0] != null); + ].filter(k => k[0] != null); return null; } @@ -1172,8 +1256,12 @@ var Options = Module("options", { context.highlight(); if (context.filter.indexOf("=") == -1) { if (false && prefix) - context.filters.push(function ({ item }) item.type == "boolean" || prefix == "inv" && isArray(item.values)); - return completion.option(context, opt.scope, opt.name == "inv" ? opt.name : prefix); + context.filters.push(({ item }) => (item.type == "boolean" || + prefix == "inv" && isArray(item.values))); + + return completion.option(context, opt.scope, + opt.name == "inv" ? opt.name + : prefix); } function error(length, message) { @@ -1199,11 +1287,11 @@ var Options = Module("options", { if (!opt.value && !opt.operator && !opt.invert) { context.fork("default", 0, this, function (context) { context.title = ["Extra Completions"]; - context.pushProcessor(0, function (item, text, next) next(item, text.substr(0, 100))); + context.pushProcessor(0, (item, text, next) => next(item, text.substr(0, 100))); context.completions = [ [option.stringValue, _("option.currentValue")], [option.stringDefaultValue, _("option.defaultValue")] - ].filter(function (f) f[0] !== ""); + ].filter(f => f[0] !== ""); context.quote = ["", util.identity, ""]; }); } @@ -1213,15 +1301,15 @@ var Options = Module("options", { // Fill in the current values if we're removing if (opt.operator == "-" && isArray(opt.values)) { - let have = Set([i.text for (i in values(context.allItems.items))]); + let have = RealSet((i.text for (i in values(context.allItems.items)))); context = context.fork("current-values", 0); context.anchored = optcontext.anchored; context.maxItems = optcontext.maxItems; - context.filters.push(function (i) !Set.has(have, i.text)); + context.filters.push(i => !have.has(i.text)); modules.completion.optionValue(context, opt.name, opt.operator, null, function (context) { - context.generate = function () option.value.map(function (o) [o, ""]); + context.generate = () => option.value.map(o => [o, ""]); }); context.title = ["Current values"]; } @@ -1238,24 +1326,7 @@ var Options = Module("options", { function fmt(value) (typeof value == "number" ? "#" : typeof value == "function" ? "*" : " ") + value; - if (!args || args == "g:") { - let str = - - { - template.map(globalVariables, function ([i, value]) { - return - - - ; - }) - } -
{i}{fmt(value)}
; - if (str.text().length() == str.*.length()) - dactyl.echomsg(_("variable.none")); - else - dactyl.echo(str, modules.commandline.FORCE_MULTILINE); - return; - } + util.assert(!(!args || args == "g:")); let matches = args.match(/^([a-z]:)?([\w]+)(?:\s*([-+.])?=\s*(.*)?)?$/); if (matches) { @@ -1264,7 +1335,7 @@ var Options = Module("options", { util.assert(scope == "g:" || scope == null, _("command.let.illegalVar", scope + name)); - util.assert(Set.has(globalVariables, name) || (expr && !op), + util.assert(hasOwnProperty(globalVariables, name) || (expr && !op), _("command.let.undefinedVar", fullName)); if (!expr) @@ -1362,7 +1433,7 @@ var Options = Module("options", { function (args) { for (let [, name] in args) { name = name.replace(/^g:/, ""); // throw away the scope prefix - if (!Set.has(dactyl._globalVariables, name)) { + if (!hasOwnProperty(dactyl._globalVariables, name)) { if (!args.bang) dactyl.echoerr(_("command.let.noSuch", name)); return; @@ -1386,11 +1457,11 @@ var Options = Module("options", { context.anchored = false; context.completions = modules.options; if (prefix == "inv") - context.keys.text = function (opt) - opt.type == "boolean" || isArray(opt.value) ? opt.names.map(function (n) "inv" + n) + context.keys.text = opt => + opt.type == "boolean" || isArray(opt.value) ? opt.names.map(n => "inv" + n) : opt.names; if (scope) - context.filters.push(function ({ item }) item.scope & scope); + context.filters.push(({ item }) => item.scope & scope); }; completion.optionValue = function (context, name, op, curValue, completer) { @@ -1442,22 +1513,41 @@ var Options = Module("options", { context.advance(Option._splitAt); context.filter = Option.dequote(context.filter); + function val(obj) { + if (isArray(opt.defaultValue)) { + let val = [].find.call(obj, re => (re.key == extra.key)); + return val && val.result; + } + if (hasOwnProperty(opt.defaultValue, extra.key)) + return obj[extra.key]; + } + + if (extra.key && extra.value != null) { + context.fork("default", 0, this, function (context) { + context.completions = [ + [val(opt.value), _("option.currentValue")], + [val(opt.defaultValue), _("option.defaultValue")] + ].filter(f => (f[0] !== "" && f[0] != null)); + }); + context = context.fork("stuff", 0); + } + context.title = ["Option Value"]; context.quote = Commands.complQuote[Option._quote] || Commands.complQuote[""]; // Not Vim compatible, but is a significant enough improvement // that it's worth breaking compatibility. if (isArray(newValues)) { - context.filters.push(function (i) newValues.indexOf(i.text) == -1); + context.filters.push(i => newValues.indexOf(i.text) == -1); if (op == "+") - context.filters.push(function (i) curValues.indexOf(i.text) == -1); + context.filters.push(i => curValues.indexOf(i.text) == -1); if (op == "-") - context.filters.push(function (i) curValues.indexOf(i.text) > -1); + context.filters.push(i => curValues.indexOf(i.text) > -1); memoize(extra, "values", function () { if (op == "+") return curValues.concat(newValues); if (op == "-") - return curValues.filter(function (v) newValues.indexOf(val) == -1); + return curValues.filter(v => newValues.indexOf(val) == -1); return newValues; }); } @@ -1469,7 +1559,7 @@ var Options = Module("options", { }, javascript: function initJavascript(dactyl, modules, window) { const { options, JavaScript } = modules; - JavaScript.setCompleter(options.get, [function () ([o.name, o.description] for (o in options))]); + JavaScript.setCompleter(Options.prototype.get, [() => ([o.name, o.description] for (o in options))]); }, sanitizer: function initSanitizer(dactyl, modules, window) { const { sanitizer } = modules; @@ -1507,4 +1597,4 @@ endModule(); } catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } -// vim: set fdm=marker sw=4 ts=4 et ft=javascript: +// vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: