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-2012 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) === String(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 *filter*.
48 * @param {string} event The event name filter.
49 * @param {string} filter The URL pattern filter.
50 * @returns {[AutoCommand]}
52 get: function (event, filter) {
53 filter = filter && String(Group.compileFilter(filter));
54 return this._store.filter(function (autoCmd) autoCmd.match(event, filter));
58 * Deletes all autocommands with a matching *event* and *filter*.
60 * @param {string} event The event name filter.
61 * @param {string} filter The URL pattern filter.
63 remove: function (event, filter) {
64 filter = filter && String(Group.compileFilter(filter));
65 this._store = this._store.filter(function (autoCmd) !autoCmd.match(event, filter));
70 * @instance autocommands
72 var AutoCommands = Module("autocommands", {
76 get activeHives() contexts.allGroups.autocmd.filter(function (h) h._store.length),
78 add: deprecated("group.autocmd.add", { get: function add() autocommands.user.closure.add }),
79 get: deprecated("group.autocmd.get", { get: function get() autocommands.user.closure.get }),
80 remove: deprecated("group.autocmd.remove", { get: function remove() autocommands.user.closure.remove }),
83 * Lists all autocommands with a matching *event*, *regexp* and optionally
86 * @param {string} event The event name filter.
87 * @param {string} regexp The URL pattern filter.
88 * @param {[Hive]} hives List of hives.
91 list: function (event, regexp, hives) {
93 let hives = hives || this.activeHives;
97 hive._store.forEach(function (autoCmd) {
98 if (autoCmd.match(event, regexp)) {
99 cmds[autoCmd.event] = cmds[autoCmd.event] || [];
100 cmds[autoCmd.event].push(autoCmd);
108 ["tr", { highlight: "Title" },
109 ["td", { colspan: "3" }, "----- Auto Commands -----"]],
110 hives.map(function (hive) [
112 ["td", { colspan: "3" },
113 ["span", { highlight: "Title" }, hive.name],
114 " ", hive.filter.toJSONXML(modules)]],
115 ["tr", { style: "height: .5ex;" }],
116 iter(cmds(hive)).map(function ([event, items]) [
117 ["tr", { style: "height: .5ex;" }],
118 items.map(function (item, i)
120 ["td", { highlight: "Title", style: "padding-left: 1em; padding-right: 1em;" },
121 i == 0 ? event : ""],
122 ["td", {}, item.filter.toJSONXML ? item.filter.toJSONXML(modules) : String(item.filter)],
123 ["td", {}, String(item.command)]]),
124 ["tr", { style: "height: .5ex;" }]]).toArray(),
125 ["tr", { style: "height: .5ex;" }]
127 commandline.commandOutput(table);
131 * Triggers the execution of all autocommands registered for *event*. A map
132 * of *args* is passed to each autocommand when it is being executed.
134 * @param {string} event The event to fire.
135 * @param {Object} args The args to pass to each autocommand.
137 trigger: function (event, args) {
138 if (options.get("eventignore").has(event))
141 dactyl.echomsg(_("autocmd.executing", event, "*".quote()), 8);
143 let lastPattern = null;
144 var { url, doc } = args;
146 uri = util.createURI(url);
148 var { uri, doc } = buffer;
150 event = event.toLowerCase();
151 for (let hive in values(this.matchingHives(uri, doc))) {
152 let args = hive.makeArgs(doc, null, arguments[1]);
154 for (let autoCmd in values(hive._store))
155 if (autoCmd.eventName === event && autoCmd.filter(uri, doc)) {
156 if (!lastPattern || lastPattern !== String(autoCmd.filter))
157 dactyl.echomsg(_("autocmd.executing", event, autoCmd.filter), 8);
159 lastPattern = String(autoCmd.filter);
160 dactyl.echomsg(_("autocmd.autocommand", autoCmd.command), 9);
162 dactyl.trapErrors(autoCmd.command, autoCmd, args);
168 contexts: function initContexts() {
169 update(AutoCommands.prototype, {
170 hives: contexts.Hives("autocmd", AutoCmdHive),
171 user: contexts.hives.autocmd.user,
172 allHives: contexts.allGroups.autocmd,
173 matchingHives: function matchingHives(uri, doc) contexts.matchingGroups(uri, doc).autocmd
176 commands: function initCommands() {
177 commands.add(["au[tocmd]"],
178 "Execute commands automatically on events",
180 let [event, filter, cmd] = args;
184 // NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
185 let validEvents = Object.keys(config.autocommands).map(String.toLowerCase);
186 validEvents.push("*");
188 events = Option.parse.stringlist(event);
189 dactyl.assert(events.every(function (event) validEvents.indexOf(event.toLowerCase()) >= 0),
190 _("autocmd.noGroup", event));
193 if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern
195 args["-group"].remove(event, filter);
196 cmd = contexts.bindMacro(args, "-ex", function (params) params);
197 args["-group"].add(events, filter, cmd);
204 // TODO: "*" only appears to work in Vim when there is a {group} specified
205 if (args[0] != "*" || args.length > 1)
206 args["-group"].remove(event, filter); // remove all
209 autocommands.list(event, filter, args.explicitOpts["-group"] ? [args["-group"]] : null); // list all
213 completer: function (context, args) {
214 if (args.length == 1)
215 return completion.autocmdEvent(context);
216 if (args.length == 3)
217 return args["-javascript"] ? completion.javascript(context) : completion.ex(context);
223 contexts.GroupFlag("autocmd"),
225 names: ["-javascript", "-js"],
226 description: "Interpret the action as JavaScript code rather than an Ex command"
234 description: "Apply the autocommands matching the specified URL pattern to the current buffer"
237 description: "Apply the autocommands matching the specified URL pattern to all buffers"
239 ].forEach(function (command) {
240 commands.add([command.name],
242 // TODO: Perhaps this should take -args to pass to the command?
245 if (args.length == 0)
246 return void dactyl.echomsg(_("autocmd.noMatching"));
248 let [event, url] = args;
249 let uri = util.createURI(url) || buffer.uri;
250 let validEvents = Object.keys(config.autocommands);
252 // TODO: add command validators
253 dactyl.assert(event != "*",
254 _("autocmd.cantExecuteAll"));
255 dactyl.assert(validEvents.indexOf(event) >= 0,
256 _("autocmd.noGroup", args));
257 dactyl.assert(autocommands.get(event).some(function (c) c.filter(uri)),
258 _("autocmd.noMatching"));
260 if (this.name == "doautoall" && dactyl.has("tabs")) {
261 let current = tabs.index();
263 for (let i = 0; i < tabs.count; i++) {
265 // if no url arg is specified use the current buffer's URL
266 autocommands.trigger(event, { url: uri.spec });
269 tabs.select(current);
272 autocommands.trigger(event, { url: uri.spec });
274 argCount: "*", // FIXME: kludged for proper error message should be "1".
275 completer: function (context) completion.autocmdEvent(context),
280 completion: function initCompletion() {
281 completion.autocmdEvent = function autocmdEvent(context) {
282 context.completions = Iterator(config.autocommands);
285 javascript: function initJavascript() {
286 JavaScript.setCompleter(AutoCmdHive.prototype.get, [function () Iterator(config.autocommands)]);
288 options: function initOptions() {
289 options.add(["eventignore", "ei"],
290 "List of autocommand event names which should be ignored",
293 values: iter(update({ all: "All Events" }, config.autocommands)).toArray(),
294 has: Option.has.toggleAll
299 // vim: set fdm=marker sw=4 sts=4 ts=8 et: