mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-23 08:08:00 +01:00
982 lines
36 KiB
JavaScript
982 lines
36 KiB
JavaScript
/***** BEGIN LICENSE BLOCK ***** {{{
|
||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
||
The contents of this file are subject to the Mozilla Public License Version
|
||
1.1 (the "License"); you may not use this file except in compliance with
|
||
the License. You may obtain a copy of the License at
|
||
http://www.mozilla.org/MPL/
|
||
|
||
Software distributed under the License is distributed on an "AS IS" basis,
|
||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
for the specific language governing rights and limitations under the
|
||
License.
|
||
|
||
(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>
|
||
|
||
Alternatively, the contents of this file may be used under the terms of
|
||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||
of those above. If you wish to allow use of your version of this file only
|
||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||
use your version of this file under the terms of the MPL, indicate your
|
||
decision by deleting the provisions above and replace them with the notice
|
||
and other provisions required by the GPL or the LGPL. If you do not delete
|
||
the provisions above, a recipient may use your version of this file under
|
||
the terms of any one of the MPL, the GPL or the LGPL.
|
||
}}} ***** END LICENSE BLOCK *****/
|
||
|
||
// do NOT create instances of this class yourself, use the helper method
|
||
// liberator.options.add() instead
|
||
liberator.Option = function (names, description, type, defaultValue, extraInfo) //{{{
|
||
{
|
||
if (!names || !type)
|
||
return null;
|
||
|
||
if (!extraInfo)
|
||
extraInfo = {};
|
||
|
||
let cannonName = names[0];
|
||
this.name = cannonName;
|
||
this.names = names;
|
||
this.type = type;
|
||
this.scope = (extraInfo.scope & liberator.options.OPTION_SCOPE_BOTH) ||
|
||
liberator.options.OPTION_SCOPE_GLOBAL;
|
||
// XXX set to BOTH by default someday? - kstep
|
||
this.description = description || "";
|
||
|
||
// "", 0 are valid default values
|
||
this.defaultValue = (defaultValue === undefined) ? null : defaultValue;
|
||
|
||
this.setter = extraInfo.setter || null;
|
||
this.getter = extraInfo.getter || null;
|
||
this.completer = extraInfo.completer || null;
|
||
this.validator = extraInfo.validator || null;
|
||
|
||
// this property is set to true whenever the option is first set
|
||
// useful to see whether it was changed by some rc file
|
||
this.hasChanged = false;
|
||
|
||
// add no{option} variant of boolean {option} to this.names
|
||
if (this.type == "boolean")
|
||
{
|
||
this.names = []; // reset since order is important
|
||
for (let i = 0; i < names.length; i++)
|
||
{
|
||
this.names.push(names[i]);
|
||
this.names.push("no" + names[i]);
|
||
}
|
||
}
|
||
|
||
this.__defineGetter__("globalvalue", function () liberator.options.store.get(cannonName));
|
||
this.__defineSetter__("globalvalue", function (val) { liberator.options.store.set(cannonName, val); });
|
||
if (this.globalvalue == undefined)
|
||
this.globalvalue = this.defaultValue;
|
||
|
||
this.get = function (scope)
|
||
{
|
||
if (scope)
|
||
{
|
||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
scope = this.scope;
|
||
}
|
||
|
||
var aValue;
|
||
|
||
if (liberator.has("tabs") && (scope & liberator.options.OPTION_SCOPE_LOCAL))
|
||
aValue = liberator.tabs.options[this.name];
|
||
if ((scope & liberator.options.OPTION_SCOPE_GLOBAL) && (aValue == undefined))
|
||
aValue = this.globalvalue;
|
||
|
||
if (this.getter)
|
||
this.getter.call(this, aValue);
|
||
|
||
return aValue;
|
||
};
|
||
|
||
this.set = function (newValue, scope)
|
||
{
|
||
scope = scope || this.scope;
|
||
if ((scope & this.scope) == 0) // option doesn't exist in this scope
|
||
return null;
|
||
|
||
if (this.setter)
|
||
{
|
||
let tmpValue = newValue;
|
||
newValue = this.setter.call(this, newValue);
|
||
|
||
if (typeof newValue == "undefined")
|
||
{
|
||
newValue = tmpValue;
|
||
liberator.log("DEPRECATED: '" + this.name + "' setter should return a value");
|
||
}
|
||
}
|
||
|
||
if (liberator.has("tabs") && (scope & liberator.options.OPTION_SCOPE_LOCAL))
|
||
liberator.tabs.options[this.name] = newValue;
|
||
if (scope & liberator.options.OPTION_SCOPE_GLOBAL && newValue != this.globalValue)
|
||
this.globalvalue = newValue;
|
||
|
||
this.hasChanged = true;
|
||
};
|
||
|
||
this.has = function ()
|
||
{
|
||
let value = this.value;
|
||
if (this.type == "stringlist")
|
||
value = this.value.split(",");
|
||
/* Return the number of matching arguments. */
|
||
return Array.reduce(arguments, function (n, val) value.indexOf(val) >= 0, 0);
|
||
};
|
||
|
||
this.__defineGetter__("value", this.get);
|
||
this.__defineSetter__("value", this.set);
|
||
|
||
this.hasName = function (name)
|
||
{
|
||
return this.names.indexOf(name) >= 0;
|
||
};
|
||
|
||
this.isValidValue = function (value)
|
||
{
|
||
if (this.validator)
|
||
return this.validator(value);
|
||
else
|
||
return true;
|
||
};
|
||
|
||
this.reset = function ()
|
||
{
|
||
this.value = this.defaultValue;
|
||
};
|
||
|
||
}; //}}}
|
||
|
||
liberator.Options = function () //{{{
|
||
{
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////{{{
|
||
|
||
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
|
||
.getService(Components.interfaces.nsIPrefBranch);
|
||
var options = {};
|
||
|
||
function optionObserver(key, event, option)
|
||
{
|
||
// Trigger any setters.
|
||
let opt = liberator.options.get(option);
|
||
if (event == "change" && opt)
|
||
opt.set(opt.value, liberator.options.OPTION_SCOPE_GLOBAL)
|
||
}
|
||
|
||
liberator.storage.newMap("options", false);
|
||
liberator.storage.addObserver("options", optionObserver);
|
||
liberator.registerObserver("shutdown", function () {
|
||
liberator.storage.removeObserver("options", optionObserver)
|
||
});
|
||
|
||
function storePreference(name, value)
|
||
{
|
||
var type = prefService.getPrefType(name);
|
||
switch (typeof value)
|
||
{
|
||
case "string":
|
||
if (type == prefService.PREF_INVALID || type == prefService.PREF_STRING)
|
||
prefService.setCharPref(name, value);
|
||
else if (type == prefService.PREF_INT)
|
||
liberator.echoerr("E521: Number required after =: " + name + "=" + value);
|
||
else
|
||
liberator.echoerr("E474: Invalid argument: " + name + "=" + value);
|
||
break;
|
||
case "number":
|
||
if (type == prefService.PREF_INVALID || type == prefService.PREF_INT)
|
||
prefService.setIntPref(name, value);
|
||
else
|
||
liberator.echoerr("E474: Invalid argument: " + name + "=" + value);
|
||
break;
|
||
case "boolean":
|
||
if (type == prefService.PREF_INVALID || type == prefService.PREF_BOOL)
|
||
prefService.setBoolPref(name, value);
|
||
else if (type == prefService.PREF_INT)
|
||
liberator.echoerr("E521: Number required after =: " + name + "=" + value);
|
||
else
|
||
liberator.echoerr("E474: Invalid argument: " + name + "=" + value);
|
||
break;
|
||
default:
|
||
liberator.echoerr("Unknown preference type: " + typeof value + " (" + name + "=" + value + ")");
|
||
}
|
||
}
|
||
|
||
function loadPreference(name, forcedDefault, defaultBranch)
|
||
{
|
||
var defaultValue = null; // XXX
|
||
if (forcedDefault != null) // this argument sets defaults for non-user settable options (like extensions.history.comp_history)
|
||
defaultValue = forcedDefault;
|
||
|
||
var branch = defaultBranch ? prefService.getDefaultBranch("") : prefService;
|
||
var type = prefService.getPrefType(name);
|
||
try
|
||
{
|
||
switch (type)
|
||
{
|
||
case prefService.PREF_STRING:
|
||
var value = branch.getComplexValue(name, Components.interfaces.nsISupportsString).data;
|
||
// try in case it's a localized string (will throw an exception if not)
|
||
if (!prefService.prefIsLocked(name) && !prefService.prefHasUserValue(name) &&
|
||
/^chrome:\/\/.+\/locale\/.+\.properties/.test(value))
|
||
value = branch.getComplexValue(name, Components.interfaces.nsIPrefLocalizedString).data;
|
||
return value;
|
||
case prefService.PREF_INT:
|
||
return branch.getIntPref(name);
|
||
case prefService.PREF_BOOL:
|
||
return branch.getBoolPref(name);
|
||
default:
|
||
return defaultValue;
|
||
}
|
||
}
|
||
catch (e)
|
||
{
|
||
return defaultValue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// firefox preferences which need to be changed to work well with vimperator
|
||
//
|
||
|
||
// work around firefox popup blocker
|
||
var popupAllowedEvents = loadPreference("dom.popup_allowed_events", "change click dblclick mouseup reset submit");
|
||
if (!/keypress/.test(popupAllowedEvents))
|
||
{
|
||
storePreference("dom.popup_allowed_events", popupAllowedEvents + " keypress");
|
||
liberator.registerObserver("shutdown", function ()
|
||
{
|
||
if (loadPreference("dom.popup_allowed_events", "")
|
||
== popupAllowedEvents + " keypress")
|
||
storePreference("dom.popup_allowed_events", popupAllowedEvents);
|
||
});
|
||
}
|
||
|
||
// TODO: maybe reset in .destroy()?
|
||
// TODO: move to vim.js or buffer.js
|
||
// we have our own typeahead find implementation
|
||
storePreference("accessibility.typeaheadfind.autostart", false);
|
||
storePreference("accessibility.typeaheadfind", false); // actually the above setting should do it, but has no effect in firefox
|
||
|
||
// start with saved session
|
||
storePreference("browser.startup.page", 3);
|
||
|
||
/////////////////////////////////////////////////////////////////////////////}}}
|
||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////{{{
|
||
|
||
liberator.commands.add(["let"],
|
||
"Set or list a variable",
|
||
function (args)
|
||
{
|
||
if (!args)
|
||
{
|
||
var str =
|
||
<table>
|
||
{
|
||
liberator.template.map(liberator.globalVariables, function ([i, value]) {
|
||
let prefix = typeof value == "number" ? "#" :
|
||
typeof value == "function" ? "*" :
|
||
" ";
|
||
return <tr>
|
||
<td style="width: 200px;">{i}</td>
|
||
<td>{prefix}{value}</td>
|
||
</tr>
|
||
})
|
||
}
|
||
</table>;
|
||
if (str.*.length())
|
||
liberator.echo(str, liberator.commandline.FORCE_MULTILINE);
|
||
else
|
||
liberator.echo("No variables found");
|
||
return;
|
||
}
|
||
|
||
var matches;
|
||
// 1 - type, 2 - name, 3 - +-., 4 - expr
|
||
if (matches = args.match(/([$@&])?([\w:]+)\s*([-+.])?=\s*(.+)/))
|
||
{
|
||
if (!matches[1])
|
||
{
|
||
var reference = liberator.variableReference(matches[2]);
|
||
if (!reference[0] && matches[3])
|
||
{
|
||
liberator.echoerr("E121: Undefined variable: " + matches[2]);
|
||
return;
|
||
}
|
||
|
||
var expr = liberator.evalExpression(matches[4]);
|
||
if (typeof expr == "undefined")
|
||
{
|
||
liberator.echoerr("E15: Invalid expression: " + matches[4]);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
if (!reference[0])
|
||
{
|
||
if (reference[2] == "g")
|
||
reference[0] = liberator.globalVariables;
|
||
else
|
||
return; // for now
|
||
}
|
||
|
||
if (matches[3])
|
||
{
|
||
if (matches[3] == "+")
|
||
reference[0][reference[1]] += expr;
|
||
else if (matches[3] == "-")
|
||
reference[0][reference[1]] -= expr;
|
||
else if (matches[3] == ".")
|
||
reference[0][reference[1]] += expr.toString();
|
||
}
|
||
else
|
||
reference[0][reference[1]] = expr;
|
||
}
|
||
}
|
||
}
|
||
// 1 - name
|
||
else if (matches = args.match(/^\s*([\w:]+)\s*$/))
|
||
{
|
||
var reference = liberator.variableReference(matches[1]);
|
||
if (!reference[0])
|
||
{
|
||
liberator.echoerr("E121: Undefined variable: " + matches[1]);
|
||
return;
|
||
}
|
||
|
||
var value = reference[0][reference[1]];
|
||
let prefix = typeof value == "number" ? "#" :
|
||
typeof value == "function" ? "*" :
|
||
" ";
|
||
liberator.echo(reference[1] + "\t\t" + prefix + value);
|
||
}
|
||
});
|
||
|
||
liberator.commands.add(["pref[erences]", "prefs"],
|
||
"Show " + liberator.config.hostApplication + " preferences",
|
||
function (args, special)
|
||
{
|
||
if (special) // open Firefox settings GUI dialog
|
||
{
|
||
liberator.open("about:config",
|
||
(liberator.options["newtab"] && liberator.options.get("newtab").has("all", "prefs"))
|
||
? liberator.NEW_TAB : liberator.CURRENT_TAB);
|
||
}
|
||
else
|
||
{
|
||
openPreferences();
|
||
}
|
||
},
|
||
{
|
||
argCount: "0",
|
||
bang: true,
|
||
});
|
||
|
||
liberator.commands.add(["setl[ocal]"],
|
||
"Set local option",
|
||
function (args, special, count)
|
||
{
|
||
liberator.commands.get("set").execute(args, special, count, { scope: liberator.options.OPTION_SCOPE_LOCAL });
|
||
},
|
||
{
|
||
bang: true,
|
||
count: true,
|
||
completer: function (filter, special, count)
|
||
{
|
||
return liberator.commands.get("set").completer(filter, special, count, { scope: liberator.options.OPTION_SCOPE_LOCAL });
|
||
}
|
||
}
|
||
);
|
||
|
||
liberator.commands.add(["setg[lobal]"],
|
||
"Set global option",
|
||
function (args, special, count)
|
||
{
|
||
liberator.commands.get("set").execute(args, special, count, { scope: liberator.options.OPTION_SCOPE_GLOBAL });
|
||
},
|
||
{
|
||
bang: true,
|
||
count: true,
|
||
completer: function (filter, special, count)
|
||
{
|
||
return liberator.commands.get("set").completer(filter, special, count, { scope: liberator.options.OPTION_SCOPE_GLOBAL });
|
||
}
|
||
}
|
||
);
|
||
|
||
// FIXME: Integrate with setter
|
||
function parseOpt(args, modifiers)
|
||
{
|
||
let ret = {};
|
||
|
||
ret.args = args;
|
||
ret.onlyNonDefault = false; // used for :set to print non-default options
|
||
if (!args)
|
||
{
|
||
ret.args = "all";
|
||
ret.onlyNonDefault = true;
|
||
}
|
||
|
||
[matches, prefix, ret.name, postfix, valueGiven, ret.operator, ret.value] =
|
||
args.match(/^\s*(no|inv)?([a-z_]+)([?&!])?\s*(([-+^]?)=(.*))?\s*$/) || [];
|
||
|
||
ret.prefix = prefix;
|
||
ret.postfix = postfix;
|
||
|
||
if (!matches)
|
||
return null;
|
||
|
||
ret.scope = modifiers && modifiers.scope || liberator.options.OPTION_SCOPE_BOTH;
|
||
ret.option = liberator.options.get(ret.name, ret.scope);
|
||
|
||
ret.all = (ret.name == "all")
|
||
ret.get = (ret.all || postfix == "?" || (ret.option && ret.option.type != "boolean" && !valueGiven))
|
||
ret.invert = (prefix == "inv" || postfix == "!");
|
||
ret.reset = (postfix == "&");
|
||
ret.unsetBoolean = (prefix == "no");
|
||
|
||
if (ret.value === undefined)
|
||
ret.value = "";
|
||
|
||
liberator.dump(ret);
|
||
|
||
return ret;
|
||
}
|
||
|
||
// TODO: support setting multiple options at once
|
||
liberator.commands.add(["se[t]"],
|
||
"Set an option",
|
||
function (args, special, count, modifiers)
|
||
{
|
||
if (special)
|
||
{
|
||
var onlyNonDefault = false;
|
||
if (!args)
|
||
{
|
||
args = "all";
|
||
onlyNonDefault = true;
|
||
}
|
||
|
||
let [matches, name, postfix, valueGiven, operator, value] =
|
||
args.match(/^\s*?([a-zA-Z0-9\.\-_{}]+)([?&!])?\s*(([-+^]?)=(.*))?\s*$/);
|
||
let reset = (postfix == "&");
|
||
let invertBoolean = (postfix == "!");
|
||
|
||
if (name == "all" && reset)
|
||
liberator.echoerr("You can't reset all options, it could make " + liberator.config.hostApplication + " unusable.");
|
||
else if (name == "all")
|
||
liberator.options.listPrefs(onlyNonDefault, "");
|
||
else if (reset)
|
||
liberator.options.resetPref(name);
|
||
else if (invertBoolean)
|
||
liberator.options.invertPref(name);
|
||
else if (valueGiven)
|
||
{
|
||
switch (value)
|
||
{
|
||
case undefined:
|
||
value = "";
|
||
break;
|
||
case "true":
|
||
value = true;
|
||
break;
|
||
case "false":
|
||
value = false;
|
||
break;
|
||
default:
|
||
if (/^\d+$/.test(value))
|
||
value = parseInt(value, 10);
|
||
}
|
||
liberator.options.setPref(name, value);
|
||
}
|
||
else
|
||
{
|
||
liberator.options.listPrefs(onlyNonDefault, name);
|
||
}
|
||
return;
|
||
}
|
||
|
||
let opt = parseOpt(args, modifiers);
|
||
let option = opt.option;
|
||
|
||
// reset a variable to its default value
|
||
if (opt.reset)
|
||
{
|
||
if (opt.all)
|
||
{
|
||
for (let option in liberator.options)
|
||
option.reset();
|
||
}
|
||
else
|
||
{
|
||
option.reset();
|
||
}
|
||
}
|
||
// read access
|
||
else if (opt.get)
|
||
{
|
||
if (opt.all)
|
||
{
|
||
liberator.options.list(opt.onlyNonDefault, scope);
|
||
}
|
||
else
|
||
{
|
||
if (option.type == "boolean")
|
||
liberator.echo((option.get(opt.scope) ? " " : "no") + option.name);
|
||
else
|
||
liberator.echo(" " + option.name + "=" + option.get(opt.scope));
|
||
}
|
||
}
|
||
// 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
|
||
{
|
||
let currentValue = option.get(opt.scope);
|
||
let newValue;
|
||
|
||
switch (option.type)
|
||
{
|
||
case "boolean":
|
||
if (opt.valueGiven)
|
||
{
|
||
liberator.echoerr("E474: Invalid argument: " + args);
|
||
return;
|
||
}
|
||
|
||
if (opt.invert)
|
||
newValue = !currentValue;
|
||
else
|
||
newValue = !opt.unsetBoolean;
|
||
|
||
break;
|
||
|
||
case "number":
|
||
let value = parseInt(opt.value); // deduce radix
|
||
|
||
if (isNaN(value))
|
||
{
|
||
liberator.echoerr("E521: Number required after =: " + args);
|
||
return;
|
||
}
|
||
|
||
switch (opt.operator)
|
||
{
|
||
case "+":
|
||
newValue = currentValue + value;
|
||
break;
|
||
case "-":
|
||
newValue = currentValue - value;
|
||
break;
|
||
case "^":
|
||
newValue = currentValue * value;
|
||
break;
|
||
default:
|
||
newValue = value;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
case "charlist":
|
||
switch (opt.operator)
|
||
{
|
||
case "+":
|
||
newValue = currentValue.replace(new RegExp("[" + opt.value + "]", "g"), "") + opt.value;
|
||
break;
|
||
case "-":
|
||
newValue = currentValue.replace(new RegExp("[" + opt.value + "]", "g"), "");
|
||
break;
|
||
case "^":
|
||
// NOTE: Vim doesn't prepend if there's a match in the current value
|
||
newValue = opt.value + currentValue.replace(new RegExp("[" + opt.value + "]", "g"), "");
|
||
break;
|
||
default:
|
||
newValue = opt.value;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
case "stringlist":
|
||
switch (opt.operator)
|
||
{
|
||
case "+":
|
||
if (!currentValue.match(opt.value))
|
||
newValue = (currentValue ? currentValue + "," : "") + opt.value;
|
||
else
|
||
newValue = currentValue;
|
||
break;
|
||
case "-":
|
||
newValue = currentValue.replace(new RegExp("^" + opt.value + ",?|," + opt.value), "");
|
||
break;
|
||
case "^":
|
||
if (!currentValue.match(opt.value))
|
||
newValue = opt.value + (currentValue ? "," : "") + currentValue;
|
||
else
|
||
newValue = currentValue;
|
||
break;
|
||
default:
|
||
newValue = opt.value;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
case "string":
|
||
switch (opt.operator)
|
||
{
|
||
case "+":
|
||
newValue = currentValue + opt.value;
|
||
break;
|
||
case "-":
|
||
newValue = currentValue.replace(opt.value, "");
|
||
break;
|
||
case "^":
|
||
newValue = opt.value + currentValue;
|
||
break;
|
||
default:
|
||
newValue = opt.value;
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
liberator.echoerr("E685: Internal error: option type `" + option.type + "' not supported");
|
||
}
|
||
|
||
if (option.isValidValue(newValue))
|
||
{
|
||
option.set(newValue, opt.scope);
|
||
}
|
||
else
|
||
// FIXME: need to be able to specify more specific errors
|
||
liberator.echoerr("E474: Invalid argument: " + args);
|
||
}
|
||
},
|
||
{
|
||
bang: true,
|
||
completer: function (filter, special, count, modifiers)
|
||
{
|
||
var optionCompletions = [];
|
||
var prefix = filter.match(/^no|inv/) || "";
|
||
|
||
if (prefix)
|
||
filter = filter.replace(prefix, "");
|
||
|
||
if (special) // list completions for about:config entries
|
||
{
|
||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||
.getService(Components.interfaces.nsIPrefBranch);
|
||
var prefArray = prefs.getChildList("", { value: 0 });
|
||
prefArray.sort();
|
||
|
||
if (filter.length > 0 && filter.lastIndexOf("=") == filter.length - 1)
|
||
{
|
||
for (let [,name] in Iterator(prefArray))
|
||
{
|
||
if (name.match("^" + filter.substr(0, filter.length - 1) + "$" ))
|
||
{
|
||
let value = liberator.options.getPref(name) + "";
|
||
return [filter.length + 1, [[value, ""]]];
|
||
}
|
||
}
|
||
return [0, []];
|
||
}
|
||
|
||
optionCompletions = prefArray.map(function (pref)
|
||
[pref, liberator.options.getPref(pref)]);
|
||
|
||
return [0, liberator.completion.filter(optionCompletions, filter)];
|
||
}
|
||
|
||
var scope = liberator.options.OPTION_SCOPE_BOTH;
|
||
if (modifiers && modifiers.scope)
|
||
scope = modifiers.scope;
|
||
|
||
let options = (opt for (opt in liberator.options)
|
||
if ((opt.scope & scope) && (!prefix || opt.type == "boolean")));
|
||
|
||
if (!filter)
|
||
{
|
||
let opts = [[prefix + option.name, option.description]
|
||
for (option in options)];
|
||
return [0, opts];
|
||
}
|
||
else if (filter.indexOf("=") == -1)
|
||
{
|
||
for (let option in options)
|
||
optionCompletions.push([[prefix + name, option.description]
|
||
for each (name in option.names)
|
||
if (name.indexOf(filter) == 0)]);
|
||
// Flatten array.
|
||
optionCompletions = Array.concat.apply(Array, optionCompletions);
|
||
|
||
return [0, liberator.completion.filter(optionCompletions, prefix + filter, true)];
|
||
}
|
||
else if (prefix)
|
||
return;
|
||
|
||
let [name, value] = filter.split("=", 2);
|
||
let offset = name.length + 1;
|
||
let opt = parseOpt(filter, modifiers);
|
||
let option = opt.option;
|
||
|
||
if (opt.get || opt.reset || !option || prefix)
|
||
return [0, []];
|
||
|
||
let len = opt.value.length;
|
||
let completer = option.completer;
|
||
let have = null;
|
||
let have2;
|
||
|
||
switch (option.type)
|
||
{
|
||
case "boolean":
|
||
completer = function () [["true", ""], ["false", ""]]
|
||
break;
|
||
case "stringlist":
|
||
have = option.value.split(",");
|
||
have2 = opt.value.split(",");
|
||
len = have2.pop().length;
|
||
break;
|
||
case "charlist":
|
||
have = option.value.split("");
|
||
have2 = opt.value;
|
||
len = 0;
|
||
break;
|
||
}
|
||
|
||
len = filter.length - len;
|
||
filter = filter.substr(len);
|
||
|
||
/* Not vim compatible, but is a significant enough improvement
|
||
* that it's worth breaking compatibility.
|
||
*/
|
||
let completions = [];
|
||
if (!opt.value)
|
||
completions = [[option.value, "Current value"], [option.defaultValue, "Default value"]].filter(function (f) f[0]);
|
||
if (completer)
|
||
{
|
||
completions = completions.concat(completer(filter));
|
||
if (have)
|
||
{
|
||
completions = completions.filter(function (val) have2.indexOf(val[0]) == -1);
|
||
switch (opt.operator)
|
||
{
|
||
case "+":
|
||
completions = completions.filter(function (val) have.indexOf(val[0]) == -1);
|
||
break;
|
||
case "-":
|
||
completions = completions.filter(function (val) have.indexOf(val[0]) > -1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return [len, liberator.completion.filter(completions, filter, true)];
|
||
}
|
||
});
|
||
|
||
liberator.commands.add(["unl[et]"],
|
||
"Delete a variable",
|
||
function (args, special)
|
||
{
|
||
//var names = args.split(/ /);
|
||
//if (typeof names == "string") names = [names];
|
||
|
||
//var length = names.length;
|
||
//for (let i = 0, name = names[i]; i < length; name = names[++i])
|
||
for (let i = 0; i < args.arguments.length; i++)
|
||
{
|
||
var name = args.arguments[i];
|
||
var reference = liberator.variableReference(name);
|
||
if (!reference[0])
|
||
{
|
||
if (!special)
|
||
liberator.echoerr("E108: No such variable: " + name);
|
||
return;
|
||
}
|
||
|
||
delete reference[0][reference[1]];
|
||
}
|
||
},
|
||
{
|
||
argCount: "+",
|
||
bang: true
|
||
});
|
||
|
||
/////////////////////////////////////////////////////////////////////////////}}}
|
||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
||
/////////////////////////////////////////////////////////////////////////////{{{
|
||
|
||
return {
|
||
|
||
OPTION_SCOPE_GLOBAL: 1,
|
||
OPTION_SCOPE_LOCAL: 2,
|
||
OPTION_SCOPE_BOTH: 3,
|
||
|
||
__iterator__: function ()
|
||
{
|
||
let sorted = [o for ([,o] in Iterator(options))]
|
||
.sort(function (a, b) String.localeCompare(a.name, b.name));
|
||
return (v for ([k, v] in Iterator(sorted)));
|
||
},
|
||
|
||
add: function (names, description, type, defaultValue, extraInfo)
|
||
{
|
||
if (!extraInfo)
|
||
extraInfo = {};
|
||
|
||
var option = new liberator.Option(names, description, type, defaultValue, extraInfo);
|
||
|
||
if (!option)
|
||
return false;
|
||
|
||
if (option.name in options)
|
||
{
|
||
// never replace for now
|
||
liberator.log("Warning: '" + names[0] + "' already exists, NOT replacing existing option.", 1);
|
||
return false;
|
||
}
|
||
|
||
// quickly access options with liberator.options["wildmode"]:
|
||
this.__defineGetter__(option.name, function () option.value);
|
||
this.__defineSetter__(option.name, function (value) { option.value = value; });
|
||
|
||
options[option.name] = option;
|
||
return true;
|
||
},
|
||
|
||
get: function (name, scope)
|
||
{
|
||
if (!scope)
|
||
scope = liberator.options.OPTION_SCOPE_BOTH;
|
||
|
||
if (name in options && (options[name].scope & scope))
|
||
return options[name];
|
||
|
||
for (let [,opt] in Iterator(options))
|
||
{
|
||
if (opt.hasName(name) && (opt.scope & scope))
|
||
return opt;
|
||
}
|
||
|
||
return null;
|
||
},
|
||
|
||
list: function (onlyNonDefault, scope)
|
||
{
|
||
if (!scope)
|
||
scope = liberator.options.OPTION_SCOPE_BOTH;
|
||
|
||
let opts = function (opt) {
|
||
for (let opt in Iterator(liberator.options))
|
||
{
|
||
let option = {
|
||
isDefault: opt.value == opt.defaultValue,
|
||
name: opt.name,
|
||
default: opt.defaultValue,
|
||
pre: " ", /* Unicode nonbreaking space. */
|
||
value: <></>,
|
||
};
|
||
|
||
if (onlyNonDefault && option.isDefault)
|
||
continue;
|
||
if (!(opt.scope & scope))
|
||
continue;
|
||
|
||
if (opt.type == "boolean")
|
||
{
|
||
if (!opt.value)
|
||
option.pre = "no";
|
||
option.default = (option.default ? "" : "no") + opt.name;
|
||
}
|
||
else
|
||
{
|
||
option.value = <>={liberator.template.highlight(opt.value)}</>;
|
||
}
|
||
yield option;
|
||
}
|
||
}
|
||
|
||
let list = liberator.template.options("Options", opts());
|
||
liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
|
||
},
|
||
|
||
listPrefs: function (onlyNonDefault, filter)
|
||
{
|
||
if (!filter)
|
||
filter = "";
|
||
|
||
var prefArray = prefService.getChildList("", { value: 0 });
|
||
prefArray.sort();
|
||
let prefs = function () {
|
||
for each (let pref in prefArray)
|
||
{
|
||
var userValue = prefService.prefHasUserValue(pref);
|
||
if (onlyNonDefault && !userValue || pref.indexOf(filter) == -1)
|
||
continue;
|
||
|
||
value = liberator.options.getPref(pref);
|
||
if (typeof value == "string")
|
||
value = value.substr(0, 100).replace(/\n/g, " ");
|
||
|
||
let option = {
|
||
isDefault: !userValue,
|
||
default: loadPreference(pref, null, true),
|
||
value: <>={liberator.template.highlight(value)}</>,
|
||
name: pref,
|
||
pre: " ", /* Unicode nonbreaking space. */
|
||
};
|
||
|
||
yield option;
|
||
}
|
||
}
|
||
|
||
let list = liberator.template.options(liberator.config.hostApplication + " Options", prefs());
|
||
liberator.commandline.echo(list, liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
|
||
},
|
||
|
||
get store() liberator.storage.options,
|
||
|
||
getPref: function (name, forcedDefault)
|
||
{
|
||
return loadPreference(name, forcedDefault);
|
||
},
|
||
|
||
setPref: function (name, value)
|
||
{
|
||
return storePreference(name, value);
|
||
},
|
||
|
||
resetPref: function (name)
|
||
{
|
||
return prefService.clearUserPref(name);
|
||
},
|
||
|
||
// this works only for booleans
|
||
invertPref: function (name)
|
||
{
|
||
if (prefService.getPrefType(name) == prefService.PREF_BOOL)
|
||
this.setPref(name, !this.getPref(name));
|
||
else
|
||
liberator.echoerr("E488: Trailing characters: " + name + "!");
|
||
}
|
||
};
|
||
//}}}
|
||
}; //}}}
|
||
|
||
// vim: set fdm=marker sw=4 ts=4 et:
|