mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-22 07:37:59 +01:00
* Standard module format. All modules are explicitly declared as modules, they're created via a constructor and instantiated automatically. They're dependency aware. They stringify properly. * Classes are declared the same way (rather like Structs already were). They also stringify properly. Plus, each instance has a rather nifty closure member that closes all of its methods around 'this', so you can pass them to map, forEach, setTimeout, etc. Modules are themselves classes, with a special metaclass, as it were. * Doug Crockford is dead, metaphorically speaking. Closure-based classes just don't fit into any of the common JavaScript frameworks, and they're inefficient and confusing. Now, all class and module members are accessed explicitly via 'this', which makes it very clear that they're class members and not (e.g.) local variables, without anything nasty like Hungarian notation. * Strictly one module per file. Classes that belong to a module live in the same file. * For the moment, there are quite a few utility functions sitting in base.c, because my class implementation used them, and I haven't had the time or inclination to sort them out. I plan to reconcile them with the current mess that is the util namespace. * Changed bracing style.
276 lines
10 KiB
JavaScript
276 lines
10 KiB
JavaScript
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
|
//
|
|
// This work is licensed for reuse under an MIT license. Details are
|
|
// given in the LICENSE.txt file included with this file.
|
|
|
|
|
|
/** @scope modules */
|
|
|
|
const AutoCommand = new Struct("event", "pattern", "command");
|
|
|
|
/**
|
|
* @instance autocommands
|
|
*/
|
|
const AutoCommands = Module("autocommands", {
|
|
init: function () {
|
|
this._store = [];
|
|
},
|
|
|
|
__iterator__: function () util.Array.itervalues(this._store),
|
|
|
|
/**
|
|
* Adds a new autocommand. <b>cmd</b> will be executed when one of the
|
|
* specified <b>events</b> occurs and the URL of the applicable buffer
|
|
* matches <b>regex</b>.
|
|
*
|
|
* @param {Array} events The array of event names for which this
|
|
* autocommand should be executed.
|
|
* @param {string} regex The URL pattern to match against the buffer URL.
|
|
* @param {string} cmd The Ex command to run.
|
|
*/
|
|
add: function (events, regex, cmd) {
|
|
if (typeof events == "string") {
|
|
events = events.split(",");
|
|
liberator.log("DEPRECATED: the events list arg to autocommands.add() should be an array of event names");
|
|
}
|
|
events.forEach(function (event) {
|
|
this._store.push(new AutoCommand(event, RegExp(regex), cmd));
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Returns all autocommands with a matching <b>event</b> and
|
|
* <b>regex</b>.
|
|
*
|
|
* @param {string} event The event name filter.
|
|
* @param {string} regex The URL pattern filter.
|
|
* @returns {AutoCommand[]}
|
|
*/
|
|
get: function (event, regex) {
|
|
return this._store.filter(function (autoCmd) matchAutoCmd(autoCmd, event, regex));
|
|
},
|
|
|
|
/**
|
|
* Deletes all autocommands with a matching <b>event</b> and
|
|
* <b>regex</b>.
|
|
*
|
|
* @param {string} event The event name filter.
|
|
* @param {string} regex The URL pattern filter.
|
|
*/
|
|
remove: function (event, regex) {
|
|
this._store = this._store.filter(function (autoCmd) !matchAutoCmd(autoCmd, event, regex));
|
|
},
|
|
|
|
/**
|
|
* Lists all autocommands with a matching <b>event</b> and
|
|
* <b>regex</b>.
|
|
*
|
|
* @param {string} event The event name filter.
|
|
* @param {string} regex The URL pattern filter.
|
|
*/
|
|
list: function (event, regex) {
|
|
let cmds = {};
|
|
|
|
// XXX
|
|
this._store.forEach(function (autoCmd) {
|
|
if (matchAutoCmd(autoCmd, event, regex)) {
|
|
cmds[autoCmd.event] = cmds[autoCmd.event] || [];
|
|
cmds[autoCmd.event].push(autoCmd);
|
|
}
|
|
});
|
|
|
|
let list = template.commandOutput(
|
|
<table>
|
|
<tr highlight="Title">
|
|
<td colspan="2">----- Auto Commands -----</td>
|
|
</tr>
|
|
{
|
|
template.map(cmds, function ([event, items])
|
|
<tr highlight="Title">
|
|
<td colspan="2">{event}</td>
|
|
</tr>
|
|
+
|
|
template.map(items, function (item)
|
|
<tr>
|
|
<td> {item.pattern.source}</td>
|
|
<td>{item.command}</td>
|
|
</tr>))
|
|
}
|
|
</table>);
|
|
|
|
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
|
},
|
|
|
|
/**
|
|
* Triggers the execution of all autocommands registered for
|
|
* <b>event</b>. A map of <b>args</b> is passed to each autocommand
|
|
* when it is being executed.
|
|
*
|
|
* @param {string} event The event to fire.
|
|
* @param {Object} args The args to pass to each autocommand.
|
|
*/
|
|
trigger: function (event, args) {
|
|
if (options.get("eventignore").has("all", event))
|
|
return;
|
|
|
|
let autoCmds = this._store.filter(function (autoCmd) autoCmd.event == event);
|
|
|
|
liberator.echomsg("Executing " + event + " Auto commands for \"*\"", 8);
|
|
|
|
let lastPattern = null;
|
|
let url = args.url || "";
|
|
|
|
for (let [, autoCmd] in Iterator(autoCmds)) {
|
|
if (autoCmd.pattern.test(url)) {
|
|
if (!lastPattern || lastPattern.source != autoCmd.pattern.source)
|
|
liberator.echomsg("Executing " + event + " Auto commands for \"" + autoCmd.pattern.source + "\"", 8);
|
|
|
|
lastPattern = autoCmd.pattern;
|
|
liberator.echomsg("autocommand " + autoCmd.command, 9);
|
|
|
|
if (typeof autoCmd.command == "function") {
|
|
try {
|
|
autoCmd.command.call(autoCmd, args);
|
|
}
|
|
catch (e) {
|
|
liberator.reportError(e);
|
|
liberator.echoerr(e);
|
|
}
|
|
}
|
|
else
|
|
liberator.execute(commands.replaceTokens(autoCmd.command, args), null, true);
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
matchAutoCmd: function (autoCmd, event, regex) {
|
|
return (!event || autoCmd.event == event) && (!regex || autoCmd.pattern.source == regex);
|
|
},
|
|
}, {
|
|
commands: function () {
|
|
commands.add(["au[tocmd]"],
|
|
"Execute commands automatically on events",
|
|
function (args) {
|
|
let [event, regex, cmd] = args;
|
|
let events = [];
|
|
|
|
try {
|
|
RegExp(regex);
|
|
}
|
|
catch (e) {
|
|
liberator.assert(false, "E475: Invalid argument: " + regex);
|
|
}
|
|
|
|
if (event) {
|
|
// NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
|
|
let validEvents = config.autocommands.map(function (event) event[0]);
|
|
validEvents.push("*");
|
|
|
|
events = event.split(",");
|
|
liberator.assert(events.every(function (event) validEvents.indexOf(event) >= 0),
|
|
"E216: No such group or event: " + event);
|
|
}
|
|
|
|
if (cmd) { // add new command, possibly removing all others with the same event/pattern
|
|
if (args.bang)
|
|
autocommands.remove(event, regex);
|
|
if (args["-javascript"])
|
|
cmd = eval("(function (args) { with(args) {" + cmd + "} })");
|
|
autocommands.add(events, regex, cmd);
|
|
}
|
|
else {
|
|
if (event == "*")
|
|
event = null;
|
|
|
|
if (args.bang) {
|
|
// TODO: "*" only appears to work in Vim when there is a {group} specified
|
|
if (args[0] != "*" || regex)
|
|
autocommands.remove(event, regex); // remove all
|
|
}
|
|
else
|
|
autocommands.list(event, regex); // list all
|
|
}
|
|
}, {
|
|
bang: true,
|
|
completer: function (context) completion.autocmdEvent(context),
|
|
literal: 2,
|
|
options: [[["-javascript", "-js"], commands.OPTION_NOARG]]
|
|
});
|
|
|
|
[
|
|
{
|
|
name: "do[autocmd]",
|
|
description: "Apply the autocommands matching the specified URL pattern to the current buffer"
|
|
}, {
|
|
name: "doautoa[ll]",
|
|
description: "Apply the autocommands matching the specified URL pattern to all buffers"
|
|
}
|
|
].forEach(function (command) {
|
|
commands.add([command.name],
|
|
command.description,
|
|
// TODO: Perhaps this should take -args to pass to the command?
|
|
function (args) {
|
|
// Vim compatible
|
|
if (args.length == 0)
|
|
return void liberator.echomsg("No matching autocommands");
|
|
|
|
let [event, url] = args;
|
|
let defaultURL = url || buffer.URL;
|
|
let validEvents = config.autocommands.map(function (e) e[0]);
|
|
|
|
// TODO: add command validators
|
|
liberator.assert(event != "*",
|
|
"E217: Can't execute autocommands for ALL events");
|
|
liberator.assert(validEvents.indexOf(event) >= 0,
|
|
"E216: No such group or event: " + args);
|
|
liberator.assert(autocommands.get(event).some(function (c) c.pattern.test(defaultURL)),
|
|
"No matching autocommands");
|
|
|
|
if (this.name == "doautoall" && liberator.has("tabs")) {
|
|
let current = tabs.index();
|
|
|
|
for (let i = 0; i < tabs.count; i++) {
|
|
tabs.select(i);
|
|
// if no url arg is specified use the current buffer's URL
|
|
autocommands.trigger(event, { url: url || buffer.URL });
|
|
}
|
|
|
|
tabs.select(current);
|
|
}
|
|
else
|
|
autocommands.trigger(event, { url: defaultURL });
|
|
}, {
|
|
argCount: "*", // FIXME: kludged for proper error message should be "1".
|
|
completer: function (context) completion.autocmdEvent(context)
|
|
});
|
|
});
|
|
},
|
|
completion: function () {
|
|
completion.setFunctionCompleter(autocommands.get, [function () config.autocommands]);
|
|
|
|
completion.autocmdEvent = function autocmdEvent(context) {
|
|
context.completions = config.autocommands;
|
|
};
|
|
|
|
completion.macro = function macro(context) {
|
|
context.title = ["Macro", "Keys"];
|
|
context.completions = [item for (item in events.getMacros())];
|
|
};
|
|
},
|
|
options: function () {
|
|
options.add(["eventignore", "ei"],
|
|
"List of autocommand event names which should be ignored",
|
|
"stringlist", "",
|
|
{
|
|
completer: function () config.autocommands.concat([["all", "All events"]]),
|
|
validator: Option.validateCompleter
|
|
});
|
|
|
|
options.add(["focuscontent", "fc"],
|
|
"Try to stay in normal mode after loading a web page",
|
|
"boolean", false);
|
|
},
|
|
});
|
|
|
|
// vim: set fdm=marker sw=4 ts=4 et:
|