// Copyright (c) 2006-2009 by Martin Stubenschrott // // 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", { requires: ["config"], init: function () { this._store = []; }, __iterator__: function () util.Array.itervalues(this._store), /** * Adds a new autocommand. cmd will be executed when one of the * specified events occurs and the URL of the applicable buffer * matches regex. * * @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 event and * regex. * * @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 event and * regex. * * @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 event and * regex. * * @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( { template.map(cmds, function ([event, items]) + template.map(items, function (item) )) }
----- Auto Commands -----
{event}
 {item.pattern.source} {item.command}
); commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE); }, /** * Triggers the execution of all autocommands registered for * event. A map of args 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: