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) === 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);
106 XML.prettyPrinting = XML.ignoreWhitespace = false;
107 commandline.commandOutput(
109 <tr highlight="Title">
110 <td colspan="3">----- Auto Commands -----</td>
113 template.map(hives, function (hive)
115 <td colspan="3"><span highlight="Title">{hive.name}</span>
118 <tr style="height: .5ex;"/> +
119 template.map(cmds(hive), function ([event, items])
120 <tr style="height: .5ex;"/> +
121 template.map(items, function (item, i)
123 <td highlight="Title" style="padding-left: 1em; padding-right: 1em;">{i == 0 ? event : ""}</td>
124 <td>{item.filter.toXML ? item.filter.toXML() : item.filter}</td>
125 <td>{item.command}</td>
127 <tr style="height: .5ex;"/>) +
128 <tr style="height: .5ex;"/>)
134 * Triggers the execution of all autocommands registered for *event*. A map
135 * of *args* is passed to each autocommand when it is being executed.
137 * @param {string} event The event to fire.
138 * @param {Object} args The args to pass to each autocommand.
140 trigger: function (event, args) {
141 if (options.get("eventignore").has(event))
144 dactyl.echomsg(_("autocmd.executing", event, "*".quote()), 8);
146 let lastPattern = null;
147 var { url, doc } = args;
149 uri = util.createURI(url);
151 var { uri, doc } = buffer;
153 event = event.toLowerCase();
154 for (let hive in values(this.matchingHives(uri, doc))) {
155 let args = hive.makeArgs(doc, null, arguments[1]);
157 for (let autoCmd in values(hive._store))
158 if (autoCmd.eventName === event && autoCmd.filter(uri, doc)) {
159 if (!lastPattern || lastPattern !== String(autoCmd.filter))
160 dactyl.echomsg(_("autocmd.executing", event, autoCmd.filter), 8);
162 lastPattern = String(autoCmd.filter);
163 dactyl.echomsg(_("autocmd.autocommand", autoCmd.command), 9);
165 dactyl.trapErrors(autoCmd.command, autoCmd, args);
171 contexts: function () {
172 update(AutoCommands.prototype, {
173 hives: contexts.Hives("autocmd", AutoCmdHive),
174 user: contexts.hives.autocmd.user,
175 allHives: contexts.allGroups.autocmd,
176 matchingHives: function matchingHives(uri, doc) contexts.matchingGroups(uri, doc).autocmd
179 commands: function () {
180 commands.add(["au[tocmd]"],
181 "Execute commands automatically on events",
183 let [event, filter, cmd] = args;
187 // NOTE: event can only be a comma separated list for |:au {event} {pat} {cmd}|
188 let validEvents = Object.keys(config.autocommands).map(String.toLowerCase);
189 validEvents.push("*");
191 events = Option.parse.stringlist(event);
192 dactyl.assert(events.every(function (event) validEvents.indexOf(event.toLowerCase()) >= 0),
193 _("autocmd.noGroup", event));
196 if (args.length > 2) { // add new command, possibly removing all others with the same event/pattern
198 args["-group"].remove(event, filter);
199 cmd = contexts.bindMacro(args, "-ex", function (params) params);
200 args["-group"].add(events, filter, cmd);
207 // TODO: "*" only appears to work in Vim when there is a {group} specified
208 if (args[0] != "*" || args.length > 1)
209 args["-group"].remove(event, filter); // remove all
212 autocommands.list(event, filter, args.explicitOpts["-group"] ? [args["-group"]] : null); // list all
216 completer: function (context, args) {
217 if (args.length == 1)
218 return completion.autocmdEvent(context);
219 if (args.length == 3)
220 return args["-javascript"] ? completion.javascript(context) : completion.ex(context);
226 contexts.GroupFlag("autocmd"),
228 names: ["-javascript", "-js"],
229 description: "Interpret the action as JavaScript code rather than an Ex command"
237 description: "Apply the autocommands matching the specified URL pattern to the current buffer"
240 description: "Apply the autocommands matching the specified URL pattern to all buffers"
242 ].forEach(function (command) {
243 commands.add([command.name],
245 // TODO: Perhaps this should take -args to pass to the command?
248 if (args.length == 0)
249 return void dactyl.echomsg(_("autocmd.noMatching"));
251 let [event, url] = args;
252 let defaultURL = url || buffer.uri.spec;
253 let validEvents = Object.keys(config.autocommands);
255 // TODO: add command validators
256 dactyl.assert(event != "*",
257 _("autocmd.cantExecuteAll"));
258 dactyl.assert(validEvents.indexOf(event) >= 0,
259 _("autocmd.noGroup", args));
260 dactyl.assert(autocommands.get(event).some(function (c) c.patterns.some(function (re) re.test(defaultURL) ^ !re.result)),
261 _("autocmd.noMatching"));
263 if (this.name == "doautoall" && dactyl.has("tabs")) {
264 let current = tabs.index();
266 for (let i = 0; i < tabs.count; i++) {
268 // if no url arg is specified use the current buffer's URL
269 autocommands.trigger(event, { url: url || buffer.uri.spec });
272 tabs.select(current);
275 autocommands.trigger(event, { url: defaultURL });
277 argCount: "*", // FIXME: kludged for proper error message should be "1".
278 completer: function (context) completion.autocmdEvent(context),
283 completion: function () {
284 completion.autocmdEvent = function autocmdEvent(context) {
285 context.completions = Iterator(config.autocommands);
288 javascript: function () {
289 JavaScript.setCompleter(AutoCmdHive.prototype.get, [function () Iterator(config.autocommands)]);
291 options: function () {
292 options.add(["eventignore", "ei"],
293 "List of autocommand event names which should be ignored",
296 values: iter(update({ all: "All Events" }, config.autocommands)).toArray(),
297 has: Option.has.toggleAll
302 // vim: set fdm=marker sw=4 ts=4 et: