1 // Copyright (c) 2006-2008 by Martin Stubenschrott <stubenschrott@vimperator.org>
2 // Copyright (c) 2007-2011 by Doug Kearns <dougkearns@gmail.com>
3 // Copyright (c) 2008-2011 by Kris Maglione <maglione.k@gmail.com>
5 // This work is licensed for reuse under an MIT license. Details are
6 // given in the LICENSE.txt file included with this file.
11 var AutoCommand = Struct("event", "filter", "command");
12 update(AutoCommand.prototype, {
13 eventName: Class.memoize(function () this.event.toLowerCase()),
15 match: function (event, pattern) {
16 return (!event || this.eventName == event.toLowerCase()) && (!pattern || String(this.filter) === pattern);
20 var AutoCmdHive = Class("AutoCmdHive", Contexts.Hive, {
21 init: function init(group) {
22 init.supercall(this, group);
26 __iterator__: function () array.iterValues(this._store),
29 * Adds a new autocommand. *cmd* will be executed when one of the specified
30 * *events* occurs and the URL of the applicable buffer matches *regexp*.
32 * @param {Array} events The array of event names for which this
33 * autocommand should be executed.
34 * @param {string} pattern The URL pattern to match against the buffer URL.
35 * @param {string} cmd The Ex command to run.
37 add: function (events, pattern, cmd) {
38 if (!callable(pattern))
39 pattern = Group.compileFilter(pattern);
41 for (let event in values(events))
42 this._store.push(AutoCommand(event, pattern, cmd));
46 * Returns all autocommands with a matching *event* and *regexp*.
48 * @param {string} event The event name filter.
49 * @param {string} pattern The URL pattern filter.
50 * @returns {[AutoCommand]}
52 get: function (event, pattern) {
53 return this._store.filter(function (autoCmd) autoCmd.match(event, regexp));
57 * Deletes all autocommands with a matching *event* and *regexp*.
59 * @param {string} event The event name filter.
60 * @param {string} regexp The URL pattern filter.
62 remove: function (event, regexp) {
63 this._store = this._store.filter(function (autoCmd) !autoCmd.match(event, regexp));
68 * @instance autocommands
70 var AutoCommands = Module("autocommands", {
73 hives: contexts.Hives("autocmd", AutoCmdHive),
74 user: contexts.hives.autocmd.user,
75 allHives: contexts.allGroups.autocmd,
76 matchingHives: function matchingHives(uri, doc) contexts.matchingGroups(uri, doc).autocmd
80 get activeHives() contexts.allGroups.autocmd.filter(function (h) h._store.length),
82 add: deprecated("group.autocmd.add", { get: function add() autocommands.user.closure.add }),
83 get: deprecated("group.autocmd.get", { get: function get() autocommands.user.closure.get }),
84 remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.closure.remove }),
87 * Lists all autocommands with a matching *event*, *regexp* and optionally
90 * @param {string} event The event name filter.
91 * @param {string} regexp The URL pattern filter.
92 * @param {[Hive]} hives List of hives.
95 list: function (event, regexp, hives) {
97 let hives = hives || this.activeHives;
101 hive._store.forEach(function (autoCmd) {
102 if (autoCmd.match(event, regexp)) {
103 cmds[autoCmd.event] = cmds[autoCmd.event] || [];
104 cmds[autoCmd.event].push(autoCmd);
110 commandline.commandOutput(
112 <tr highlight="Title">
113 <td colspan="3">----- Auto Commands -----</td>
116 template.map(hives, function (hive)
117 <tr highlight="Title">
118 <td colspan="3">{hive.name}</td>
120 <tr style="height: .5ex;"/> +
121 template.map(cmds(hive), function ([event, items])
122 <tr style="height: .5ex;"/> +
123 template.map(items, function (item, i)
125 <td highlight="Title" style="padding-right: 1em;">{i == 0 ? event : ""}</td>
126 <td>{item.filter.toXML ? item.filter.toXML() : item.filter}</td>
127 <td>{item.command}</td>
129 <tr style="height: .5ex;"/>) +
130 <tr style="height: .5ex;"/>)
136 * Triggers the execution of all autocommands registered for *event*. A map
137 * of *args* is passed to each autocommand when it is being executed.
139 * @param {string} event The event to fire.
140 * @param {Object} args The args to pass to each autocommand.
142 trigger: function (event, args) {
143 if (options.get("eventignore").has(event))
146 dactyl.echomsg(_("autocmd.executing", event, "*".quote()), 8);
148 let lastPattern = null;
149 var { url, doc } = args;
151 uri = util.createURI(url);
153 var { uri, doc } = buffer;
155 event = event.toLowerCase();
156 for (let hive in values(this.matchingHives(uri, doc))) {
157 let args = update({},
158 hive.argsExtra(arguments[1]),
161 for (let autoCmd in values(hive._store))
162 if (autoCmd.eventName === event && autoCmd.filter(uri, doc)) {
163 if (!lastPattern || lastPattern !== String(autoCmd.filter))
164 dactyl.echomsg(_("autocmd.executing", event, autoCmd.filter), 8);
166 lastPattern = String(autoCmd.filter);
167 dactyl.echomsg(_("autocmd.autocommand", autoCmd.command), 9);
169 dactyl.trapErrors(autoCmd.command, autoCmd, args);
175 commands: function () {
176 commands.add(["au[tocmd]"],
177 "Execute commands automatically on events",
179 let [event, regexp, cmd] = args;
183 // NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
184 let validEvents = Object.keys(config.autocommands).map(String.toLowerCase);
185 validEvents.push("*");
187 events = Option.parse.stringlist(event);
188 dactyl.assert(events.every(function (event) validEvents.indexOf(event.toLowerCase()) >= 0),
189 _("autocmd.noGroup", event));
192 if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern
194 args["-group"].remove(event, regexp);
195 cmd = contexts.bindMacro(args, "-ex", function (params) params);
196 args["-group"].add(events, regexp, cmd);
203 // TODO: "*" only appears to work in Vim when there is a {group} specified
204 if (args[0] != "*" || args.length > 1)
205 args["-group"].remove(event, regexp); // remove all
208 autocommands.list(event, regexp, args.explicitOpts["-group"] ? [args["-group"]] : null); // list all
212 completer: function (context, args) {
213 if (args.length == 1)
214 return completion.autocmdEvent(context);
215 if (args.length == 3)
216 return args["-javascript"] ? completion.javascript(context) : completion.ex(context);
222 contexts.GroupFlag("autocmd"),
224 names: ["-javascript", "-js"],
225 description: "Interpret the action as JavaScript code rather than an Ex command"
233 description: "Apply the autocommands matching the specified URL pattern to the current buffer"
236 description: "Apply the autocommands matching the specified URL pattern to all buffers"
238 ].forEach(function (command) {
239 commands.add([command.name],
241 // TODO: Perhaps this should take -args to pass to the command?
244 if (args.length == 0)
245 return void dactyl.echomsg(_("autocmd.noMatching"));
247 let [event, url] = args;
248 let defaultURL = url || buffer.uri.spec;
249 let validEvents = Object.keys(config.autocommands);
251 // TODO: add command validators
252 dactyl.assert(event != "*",
253 _("autocmd.cantExecuteAll"));
254 dactyl.assert(validEvents.indexOf(event) >= 0,
255 _("autocmd.noGroup", args));
256 dactyl.assert(autocommands.get(event).some(function (c) c.patterns.some(function (re) re.test(defaultURL) ^ !re.result)),
257 _("autocmd.noMatching"));
259 if (this.name == "doautoall" && dactyl.has("tabs")) {
260 let current = tabs.index();
262 for (let i = 0; i < tabs.count; i++) {
264 // if no url arg is specified use the current buffer's URL
265 autocommands.trigger(event, { url: url || buffer.uri.spec });
268 tabs.select(current);
271 autocommands.trigger(event, { url: defaultURL });
273 argCount: "*", // FIXME: kludged for proper error message should be "1".
274 completer: function (context) completion.autocmdEvent(context),
279 completion: function () {
280 completion.autocmdEvent = function autocmdEvent(context) {
281 context.completions = Iterator(config.autocommands);
284 javascript: function () {
285 JavaScript.setCompleter(autocommands.user.get, [function () Iterator(config.autocommands)]);
287 options: function () {
288 options.add(["eventignore", "ei"],
289 "List of autocommand event names which should be ignored",
292 values: iter(update({ all: "All Events" }, config.autocommands)).toArray(),
293 has: Option.has.toggleAll
298 // vim: set fdm=marker sw=4 ts=4 et: