1
0
mirror of https://github.com/gryf/pentadactyl-pm.git synced 2026-01-10 12:34:12 +01:00

Allow localization of command/mapping/option descriptions.

This commit is contained in:
Kris Maglione
2011-03-04 20:37:29 -05:00
parent f3c14e861c
commit 058094f87d
8 changed files with 139 additions and 78 deletions

View File

@@ -852,7 +852,7 @@ var Events = Module("events", {
evt_obj.charCode = keyname.charCodeAt(0);
}
else if (set.has(this._pseudoKeys)) {
else if (set.has(this._pseudoKeys, keyname)) {
evt_obj.dactylString = "<" + this._key_key[keyname] + ">";
}
else if (/mouse$/.test(keyname)) { // mouse events

View File

@@ -54,6 +54,8 @@ var Map = Class("Map", {
get toStringParams() [this.modes.map(function (m) m.name), this.names.map(String.quote)],
get identifier() [this.modes[0].name, this.hive.prefix + this.names[0]].join("."),
/** @property {number} A unique ID for this mapping. */
id: null,
/** @property {number[]} All of the modes for which this mapping applies. */
@@ -61,7 +63,7 @@ var Map = Class("Map", {
/** @property {function (number)} The function called to execute this mapping. */
action: null,
/** @property {string} This mapping's description, as shown in :listkeys. */
description: "",
description: Messages.Localized(""),
/** @property {boolean} Whether this mapping accepts an argument. */
arg: false,

View File

@@ -363,11 +363,11 @@ function set(ary) {
* @param {string} key The key to add.
* @returns boolean
*/
set.add = function (set, key) {
set.add = curry(function set_add(set, key) {
let res = this.has(set, key);
set[key] = true;
return res;
}
});
/**
* Returns true if the given set contains the given key.
*
@@ -375,8 +375,8 @@ set.add = function (set, key) {
* @param {string} key The key to check.
* @returns {boolean}
*/
set.has = function (set, key) hasOwnProperty.call(set, key) &&
propertyIsEnumerable.call(set, key);
set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) &&
propertyIsEnumerable.call(set, key));
/**
* Returns a new set containing the members of the first argument which
* do not exist in any of the other given arguments.
@@ -384,13 +384,13 @@ set.has = function (set, key) hasOwnProperty.call(set, key) &&
* @param {object} set The set.
* @returns {object}
*/
set.subtract = function (set) {
set.subtract = function set_subtract(set) {
set = update({}, set);
for (let i = 1; i < arguments.length; i++)
for (let k in keys(arguments[i]))
delete set[k];
return set;
}
};
/**
* Removes an element from a set and returns true if the element was
* previously contained.
@@ -399,12 +399,67 @@ set.subtract = function (set) {
* @param {string} key The key to remove.
* @returns boolean
*/
set.remove = function (set, key) {
set.remove = curry(function set_remove(set, key) {
let res = set.has(set, key);
delete set[key];
return res;
});
/**
* Curries a function to the given number of arguments. Each
* call of the resulting function returns a new function. When
* a call does not contain enough arguments to satisfy the
* required number, the resulting function is another curried
* function with previous arguments accumulated.
*
* function foo(a, b, c) [a, b, c].join(" ");
* curry(foo)(1, 2, 3) -> "1 2 3";
* curry(foo)(4)(5, 6) -> "4 5 6";
* curry(foo)(7)(8)(9) -> "7 8 9";
*
* @param {function} fn The function to curry.
* @param {integer} length The number of arguments expected.
* @default fn.length
* @optional
* @param {object} self The 'this' value for the returned function. When
* omitted, the value of 'this' from the first call to the function is
* preserved.
* @optional
*/
function curry(fn, length, self, acc) {
if (length == null)
length = fn.length;
if (length == 0)
return fn;
// Close over function with 'this'
function close(self, fn) function () fn.apply(self, Array.slice(arguments));
if (acc == null)
acc = [];
return function curried() {
let args = acc.concat(Array.slice(arguments));
// The curried result should preserve 'this'
if (arguments.length == 0)
return close(self || this, curried);
if (args.length >= length)
return fn.apply(self || this, args);
return curry(fn, length, self || this, args);
};
}
if (curry.bind)
var bind = function bind(func) func.bind.apply(func, Array.slice(arguments, bind.length));
else
var bind = function bind(func, self) {
let args = Array.slice(arguments, bind.length);
return function bound() func.apply(self, args.concat(Array.slice(arguments)));
};
/**
* Returns true if both arguments are functions and
* (targ() instanceof src) would also return true.
@@ -538,61 +593,6 @@ function memoize(obj, key, getter) {
});
}
/**
* Curries a function to the given number of arguments. Each
* call of the resulting function returns a new function. When
* a call does not contain enough arguments to satisfy the
* required number, the resulting function is another curried
* function with previous arguments accumulated.
*
* function foo(a, b, c) [a, b, c].join(" ");
* curry(foo)(1, 2, 3) -> "1 2 3";
* curry(foo)(4)(5, 6) -> "4 5 6";
* curry(foo)(7)(8)(9) -> "7 8 9";
*
* @param {function} fn The function to curry.
* @param {integer} length The number of arguments expected.
* @default fn.length
* @optional
* @param {object} self The 'this' value for the returned function. When
* omitted, the value of 'this' from the first call to the function is
* preserved.
* @optional
*/
function curry(fn, length, self, acc) {
if (length == null)
length = fn.length;
if (length == 0)
return fn;
// Close over function with 'this'
function close(self, fn) function () fn.apply(self, Array.slice(arguments));
if (acc == null)
acc = [];
return function curried() {
let args = acc.concat(Array.slice(arguments));
// The curried result should preserve 'this'
if (arguments.length == 0)
return close(self || this, curried);
if (args.length >= length)
return fn.apply(self || this, args);
return curry(fn, length, self || this, args);
};
}
if (curry.bind)
var bind = function bind(func) func.bind.apply(func, Array.slice(arguments, bind.length));
else
var bind = function bind(func, self) {
let args = Array.slice(arguments, bind.length);
return function bound() func.apply(self, args.concat(Array.slice(arguments)));
};
let sandbox = Cu.Sandbox(this);
sandbox.__proto__ = this;
/**

View File

@@ -11,8 +11,8 @@ try {
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("commands", {
exports: ["ArgType", "Command", "Commands", "CommandOption", "Ex", "commands"],
require: ["contexts", "util"],
use: ["config", "messages", "options", "services", "template"]
require: ["contexts", "messages", "util"],
use: ["config", "options", "services", "template"]
}, this);
/**
@@ -132,10 +132,14 @@ var Command = Class("Command", {
update(this, extraInfo);
if (this.options)
this.options = this.options.map(CommandOption.fromArray, CommandOption);
for each (let option in this.options)
option.localeName = ["command", this.name, option.names[0]];
},
get toStringParams() [this.name, this.hive.name],
get identifier() this.hive.prefix + this.name,
get helpTag() ":" + this.name,
get lastCommand() this._lastCommand || commandline.command,
@@ -224,7 +228,7 @@ var Command = Class("Command", {
names: null,
/** @property {string} This command's description, as shown in :listcommands */
description: "",
description: Messages.Localized(""),
/**
* @property {function (Args)} The function called to execute this command.
*/
@@ -1078,7 +1082,10 @@ var Commands = Module("commands", {
context.completions = compl;
}
complete.advance(args.completeStart);
complete.keys = { text: "names", description: "description" };
complete.keys = {
text: "names",
description: function (opt) messages.get(["command", params.name, opt.names[0], "description"].join("."), opt.description)
};
complete.title = ["Options"];
if (completeOpts)
complete.completions = completeOpts;

View File

@@ -434,6 +434,8 @@ var Contexts = Module("contexts", {
get persist() this.group.persist,
set persist(val) this.group.persist = val,
prefix: Class.memoize(function () this.name === "builtin" ? "" : this.name + ":"),
get toStringParams() [this.name]
})
}, {

View File

@@ -78,6 +78,48 @@ var Messages = Module("messages", {
}
}, {
Localized: Class("Localized", Class.Property, {
init: function init(prop, obj) {
let _prop = "localized_" + prop;
if (this.initialized) {
/*
if (config.locale === "en-US")
return { configurable: true, enumerable: true, value: null, writable: true };
*/
obj[_prop] = this.default;
return {
get: function get() {
let self = this;
let value = this[_prop];
function getter(key, default_) function getter() messages.get([name, key].join("."), default_);
let name = [this.constructor.className.toLowerCase(), this.identifier || this.name, prop].join(".");
if (!isObject(value))
value = messages.get(name, value)
else if (isArray(value))
// Deprecated
iter(value).forEach(function ([k, v]) {
if (isArray(v))
memoize(v, 1, getter(v[0], v[1]));
else
memoize(value, k, getter(k, v));
});
else
iter(value).forEach(function ([k, v]) {
memoize(value, k, function () messages.get([name, k].join("."), v));
});
return Class.replaceProperty(this, prop, value);
},
set: function set(val) this[_prop] = val
}
}
this.default = prop;
this.initialized = true;
}
})
}, {
javascript: function initJavascript(dactyl, modules, window) {
modules.JavaScript.setCompleter([this._, this.get, this.format], [

View File

@@ -11,7 +11,7 @@ try {
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("options", {
exports: ["Option", "Options", "ValueError", "options"],
require: ["storage"],
require: ["messages", "storage"],
use: ["commands", "completion", "prefs", "services", "styles", "template", "util"]
}, this);
@@ -65,6 +65,11 @@ var Option = Class("Option", {
this._op = Option.ops[this.type];
// Need to trigger setter
if (extraInfo && "values" in extraInfo)
this.values = extraInfo.values;
delete extraInfo.values;
if (extraInfo)
update(this, extraInfo);
@@ -92,6 +97,11 @@ var Option = Class("Option", {
this.globalValue = this.defaultValue;
},
/**
* @property {string} This option's description, as shown in :listoptions.
*/
description: Messages.Localized(""),
get helpTag() "'" + this.name + "'",
initValue: function initValue() {
@@ -291,11 +301,6 @@ var Option = Class("Option", {
*/
scope: 1, // Option.SCOPE_GLOBAL // XXX set to BOTH by default someday? - kstep
/**
* @property {string} This option's description, as shown in :listoptions.
*/
description: "",
cleanupValue: null,
/**
@@ -311,7 +316,7 @@ var Option = Class("Option", {
* @property {[[string, string]]} This option's possible values.
* @see CompletionContext
*/
values: null,
values: Messages.Localized(null),
/**
* @property {function(host, values)} A function which should return a list
@@ -679,7 +684,7 @@ var Option = Class("Option", {
*/
validateCompleter: function validateCompleter(values) {
if (this.values)
var acceptable = this.values;
var acceptable = this.values.array || this.values;
else {
let context = CompletionContext("");
acceptable = context.fork("", 0, this, this.completer);
@@ -688,7 +693,10 @@ var Option = Class("Option", {
}
if (this.type === "regexpmap" || this.type === "sitemap")
return Array.concat(values).every(function (re) acceptable.some(function (item) item[0] == re.result));
return Array.concat(values).every(function (value) acceptable.some(function (item) item[0] == value));
if (isArray(acceptable))
acceptable = set(acceptable.map(function ([k]) k));
return Array.concat(values).every(set.has(acceptable));
}
});

View File

@@ -161,6 +161,7 @@ var Overlay = Module("Overlay", {
defineModule.time("load", null, function _load() {
["addons",
"base",
"io",
"commands",
"completion",
"config",
@@ -168,7 +169,6 @@ var Overlay = Module("Overlay", {
"downloads",
"finder",
"highlight",
"io",
"javascript",
"messages",
"options",