Merge hooks into options and make each one an array option. This allows

multiple commands to be easily bound to one hook. set-hook and
show-hooks remain but they are now variants of set-option and
show-options. show-options now has a -H flag to show hooks (by default
they are not shown).
pull/1702/head
nicm 2019-04-26 11:38:51 +00:00
parent f1e14f86c4
commit dfb7bb6830
20 changed files with 356 additions and 414 deletions

View File

@ -56,7 +56,6 @@ SRCS= alerts.c \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
cmd-set-hook.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
@ -78,7 +77,6 @@ SRCS= alerts.c \
format-draw.c \
grid-view.c \
grid.c \
hooks.c \
input-keys.c \
input.c \
job.c \

View File

@ -329,7 +329,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(&fs, s, 0);
hooks_insert(s->hooks, item, &fs, "after-new-session");
cmdq_insert_hook(s, item, &fs, "after-new-session");
free(cwd);
free(newname);

View File

@ -105,7 +105,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_winlink(&fs, new_wl, 0);
hooks_insert(s->hooks, item, &fs, "after-new-window");
cmdq_insert_hook(s, item, &fs, "after-new-window");
return (CMD_RETURN_NORMAL);
}

View File

@ -98,6 +98,60 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
} while (item != NULL);
}
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct options *oo;
va_list ap;
char *name;
struct cmdq_item *new_item;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
if (item->flags & CMDQ_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
o = options_get(oo, name);
if (o == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL) {
cmdq_insert_after(item, new_item);
item = new_item;
} else
cmdq_append(NULL, new_item);
a = options_array_next(a);
}
free(name);
}
/* Remove an item. */
static void
cmdq_remove(struct cmdq_item *item)
@ -245,7 +299,7 @@ cmdq_fire_command(struct cmdq_item *item)
fsp = &fs;
else
goto out;
hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
}
out:

View File

@ -196,7 +196,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp, 1)) {
cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
}

View File

@ -119,7 +119,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
} else {
/*
* If -T and select-window is invoked on same window as
@ -137,7 +137,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
}
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
}
recalculate_sizes();

View File

@ -1,133 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Set or show global or session hooks.
*/
static enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "gRt:u", 1, 2 },
.usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
static enum cmd_retval
cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_list *cmdlist;
struct hooks *hooks;
struct hook *hook;
char *cause, *tmp;
const char *name, *cmd, *target;
if (args_has(args, 'g'))
hooks = global_hooks;
else {
if (item->target.s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
}
hooks = item->target.s->hooks;
}
if (self->entry == &cmd_show_hooks_entry) {
hook = hooks_first(hooks);
while (hook != NULL) {
tmp = cmd_list_print(hook->cmdlist);
cmdq_print(item, "%s -> %s", hook->name, tmp);
free(tmp);
hook = hooks_next(hook);
}
return (CMD_RETURN_NORMAL);
}
name = args->argv[0];
if (*name == '\0') {
cmdq_error(item, "invalid hook name");
return (CMD_RETURN_ERROR);
}
if (args->argc < 2)
cmd = NULL;
else
cmd = args->argv[1];
if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) {
cmdq_error(item, "no command allowed");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'R')) {
notify_hook(item, name);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'u')) {
hooks_remove(hooks, name);
return (CMD_RETURN_NORMAL);
}
if (cmd == NULL) {
cmdq_error(item, "no command given");
return (CMD_RETURN_ERROR);
}
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
}
return (CMD_RETURN_ERROR);
}
hooks_add(hooks, name, cmdlist);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@ -65,6 +65,19 @@ const struct cmd_entry cmd_set_window_option_entry = {
.exec = cmd_set_option_exec
};
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agRt:u", 1, 2 },
.usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
};
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@ -87,6 +100,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
return (CMD_RETURN_NORMAL);
}
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
@ -200,8 +218,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
options_default(oo, options_table_entry(o));
else
options_remove(o);
} else
options_array_set(o, idx, NULL, 0);
} else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (*name == '@') {
if (value == NULL) {
cmdq_error(item, "empty value");
@ -222,9 +243,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1) {
if (!append)
options_array_clear(o);
options_array_assign(o, value);
} else if (options_array_set(o, idx, value, append) != 0) {
cmdq_error(item, "invalid index: %s", argument);
if (options_array_assign(o, value, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (options_array_set(o, idx, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
}
@ -366,6 +393,8 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
return (-1);
}
return (0);
case OPTIONS_TABLE_COMMAND:
break;
}
return (-1);
}

View File

@ -39,8 +39,8 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "gqst:vw", 0, 1 },
.usage = "[-gqsvw] [-t target-session|target-window] [option]",
.args = { "gHqst:vw", 0, 1 },
.usage = "[-gHqsvw] [-t target-session|target-window] [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@ -61,6 +61,19 @@ const struct cmd_entry cmd_show_window_options_entry = {
.exec = cmd_show_options_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
};
static enum cmd_retval
cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
{
@ -162,15 +175,20 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
{
struct options_array_item *a;
const char *name;
char *value, *tmp, *escaped;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", options_name(o), idx);
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_isarray(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", name);
return;
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
@ -178,8 +196,6 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
return;
}
tmp = NULL;
name = options_name(o);
}
value = options_tostring(o, idx, 0);
@ -200,16 +216,28 @@ static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct options_entry *o;
struct options_array_item *a;
u_int idx;
struct options_entry *o;
struct options_array_item *a;
u_int idx;
int flags;
o = options_first(oo);
while (o != NULL) {
flags = options_table_entry(o)->flags;
if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') &&
(flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry &&
(~flags & OPTIONS_TABLE_IS_HOOK))) {
o = options_next(o);
continue;
}
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
else {
a = options_array_first(o);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", options_name(o));
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);

View File

@ -144,7 +144,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
hooks_insert(s->hooks, item, &fs, "after-split-window");
cmdq_insert_hook(s, item, &fs, "after-split-window");
return (CMD_RETURN_NORMAL);
}

View File

@ -174,7 +174,10 @@ cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
int argc;
char **argv;
*cause = NULL;
if (cause != NULL)
*cause = NULL;
log_debug ("%s: %s", __func__, s);
if (cmd_string_split(s, &argc, &argv) != 0) {
xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);

173
hooks.c
View File

@ -1,173 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct hooks {
RB_HEAD(hooks_tree, hook) tree;
struct hooks *parent;
};
static int hooks_cmp(struct hook *, struct hook *);
RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp);
static struct hook *hooks_find1(struct hooks *, const char *);
static void hooks_free1(struct hooks *, struct hook *);
static int
hooks_cmp(struct hook *hook1, struct hook *hook2)
{
return (strcmp(hook1->name, hook2->name));
}
struct hooks *
hooks_get(struct session *s)
{
if (s != NULL)
return (s->hooks);
return (global_hooks);
}
struct hooks *
hooks_create(struct hooks *parent)
{
struct hooks *hooks;
hooks = xcalloc(1, sizeof *hooks);
RB_INIT(&hooks->tree);
hooks->parent = parent;
return (hooks);
}
static void
hooks_free1(struct hooks *hooks, struct hook *hook)
{
RB_REMOVE(hooks_tree, &hooks->tree, hook);
cmd_list_free(hook->cmdlist);
free((char *)hook->name);
free(hook);
}
void
hooks_free(struct hooks *hooks)
{
struct hook *hook, *hook1;
RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1)
hooks_free1(hooks, hook);
free(hooks);
}
struct hook *
hooks_first(struct hooks *hooks)
{
return (RB_MIN(hooks_tree, &hooks->tree));
}
struct hook *
hooks_next(struct hook *hook)
{
return (RB_NEXT(hooks_tree, &hooks->tree, hook));
}
void
hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
hook = xcalloc(1, sizeof *hook);
hook->name = xstrdup(name);
hook->cmdlist = cmdlist;
hook->cmdlist->references++;
RB_INSERT(hooks_tree, &hooks->tree, hook);
}
void
hooks_remove(struct hooks *hooks, const char *name)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
}
static struct hook *
hooks_find1(struct hooks *hooks, const char *name)
{
struct hook hook;
hook.name = name;
return (RB_FIND(hooks_tree, &hooks->tree, &hook));
}
struct hook *
hooks_find(struct hooks *hooks, const char *name)
{
struct hook hook0, *hook;
hook0.name = name;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
while (hook == NULL) {
hooks = hooks->parent;
if (hooks == NULL)
break;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
}
return (hook);
}
void
hooks_insert(struct hooks *hooks, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct hook *hook;
va_list ap;
char *name;
struct cmdq_item *new_item;
if (item->flags & CMDQ_NOHOOKS)
return;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
hook = hooks_find(hooks, name);
if (hook == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(NULL, new_item);
free(name);
}

View File

@ -36,13 +36,34 @@ struct notify_entry {
};
static void
notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w,
int pane)
{
struct cmd_find_state fs;
struct hook *hook;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
if (s != NULL) {
cmdq_format(item, "hook_session", "$%u", s->id);
cmdq_format(item, "hook_session_name", "%s", s->name);
}
if (w != NULL) {
cmdq_format(item, "hook_window", "@%u", w->id);
cmdq_format(item, "hook_window_name", "%s", w->name);
}
if (pane != -1)
cmdq_format(item, "hook_pane", "%%%d", pane);
}
static void
notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
{
struct cmd_find_state fs;
struct options *oo;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
log_debug("%s: %s", __func__, ne->name);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
@ -50,26 +71,31 @@ notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
else
cmd_find_copy_state(&fs, &ne->fs);
hook = hooks_find(hooks_get(fs.s), ne->name);
if (hook == NULL)
if (fs.s == NULL)
oo = global_s_options;
else
oo = fs.s->options;
o = options_get(oo, ne->name);
if (o == NULL)
return;
log_debug("notify hook %s", ne->name);
new_item = cmdq_get_command(hook->cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
if (s != NULL) {
cmdq_format(new_item, "hook_session", "$%u", s->id);
cmdq_format(new_item, "hook_session_name", "%s", s->name);
new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
notify_hook_formats(new_item, s, w, ne->pane);
cmdq_insert_after(item, new_item);
item = new_item;
a = options_array_next(a);
}
if (w != NULL) {
cmdq_format(new_item, "hook_window", "@%u", w->id);
cmdq_format(new_item, "hook_window_name", "%s", w->name);
}
if (ne->pane != -1)
cmdq_format(new_item, "hook_pane", "%%%d", ne->pane);
cmdq_insert_after(item, new_item);
}
static enum cmd_retval
@ -102,7 +128,7 @@ notify_callback(struct cmdq_item *item, void *data)
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
notify_hook1(item, ne);
notify_insert_hook(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@ -169,7 +195,7 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.window = item->target.w;
ne.pane = item->target.wp->id;
notify_hook1(item, &ne);
notify_insert_hook(item, &ne);
}
void

View File

@ -130,8 +130,19 @@ static const char *options_table_status_format_default[] = {
OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
};
/* Helper for hook options. */
#define OPTIONS_TABLE_HOOK(hook_name, default_value) \
{ .name = hook_name, \
.type = OPTIONS_TABLE_COMMAND, \
.scope = OPTIONS_TABLE_SESSION, \
.flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \
.default_str = default_value, \
.separator = "" \
}
/* Top-level options. */
const struct options_table_entry options_table[] = {
/* Server options. */
{ .name = "buffer-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@ -224,6 +235,7 @@ const struct options_table_entry options_table[] = {
.separator = ","
},
/* Session options. */
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@ -542,6 +554,7 @@ const struct options_table_entry options_table[] = {
.default_str = " -_@"
},
/* Window options. */
{ .name = "aggressive-resize",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
@ -776,5 +789,66 @@ const struct options_table_entry options_table[] = {
.default_num = 1
},
/* Hook options. */
OPTIONS_TABLE_HOOK("after-bind-key", ""),
OPTIONS_TABLE_HOOK("after-capture-pane", ""),
OPTIONS_TABLE_HOOK("after-copy-mode", ""),
OPTIONS_TABLE_HOOK("after-display-message", ""),
OPTIONS_TABLE_HOOK("after-display-panes", ""),
OPTIONS_TABLE_HOOK("after-list-buffers", ""),
OPTIONS_TABLE_HOOK("after-list-clients", ""),
OPTIONS_TABLE_HOOK("after-list-keys", ""),
OPTIONS_TABLE_HOOK("after-list-panes", ""),
OPTIONS_TABLE_HOOK("after-list-sessions", ""),
OPTIONS_TABLE_HOOK("after-list-windows", ""),
OPTIONS_TABLE_HOOK("after-load-buffer", ""),
OPTIONS_TABLE_HOOK("after-lock-server", ""),
OPTIONS_TABLE_HOOK("after-new-session", ""),
OPTIONS_TABLE_HOOK("after-new-window", ""),
OPTIONS_TABLE_HOOK("after-paste-buffer", ""),
OPTIONS_TABLE_HOOK("after-pipe-pane", ""),
OPTIONS_TABLE_HOOK("after-queue", ""),
OPTIONS_TABLE_HOOK("after-refresh-client", ""),
OPTIONS_TABLE_HOOK("after-rename-session", ""),
OPTIONS_TABLE_HOOK("after-rename-window", ""),
OPTIONS_TABLE_HOOK("after-resize-pane", ""),
OPTIONS_TABLE_HOOK("after-resize-window", ""),
OPTIONS_TABLE_HOOK("after-save-buffer", ""),
OPTIONS_TABLE_HOOK("after-select-layout", ""),
OPTIONS_TABLE_HOOK("after-select-pane", ""),
OPTIONS_TABLE_HOOK("after-select-window", ""),
OPTIONS_TABLE_HOOK("after-send-keys", ""),
OPTIONS_TABLE_HOOK("after-set-buffer", ""),
OPTIONS_TABLE_HOOK("after-set-environment", ""),
OPTIONS_TABLE_HOOK("after-set-hook", ""),
OPTIONS_TABLE_HOOK("after-set-option", ""),
OPTIONS_TABLE_HOOK("after-show-environment", ""),
OPTIONS_TABLE_HOOK("after-show-messages", ""),
OPTIONS_TABLE_HOOK("after-show-options", ""),
OPTIONS_TABLE_HOOK("after-split-window", ""),
OPTIONS_TABLE_HOOK("after-unbind-key", ""),
OPTIONS_TABLE_HOOK("alert-activity", ""),
OPTIONS_TABLE_HOOK("alert-bell", ""),
OPTIONS_TABLE_HOOK("alert-silence", ""),
OPTIONS_TABLE_HOOK("client-attached", ""),
OPTIONS_TABLE_HOOK("client-detached", ""),
OPTIONS_TABLE_HOOK("client-resized", ""),
OPTIONS_TABLE_HOOK("client-session-changed", ""),
OPTIONS_TABLE_HOOK("pane-died", ""),
OPTIONS_TABLE_HOOK("pane-exited", ""),
OPTIONS_TABLE_HOOK("pane-focus-in", ""),
OPTIONS_TABLE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_HOOK("pane-set-clipboard", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),
OPTIONS_TABLE_HOOK("session-window-changed", ""),
OPTIONS_TABLE_HOOK("window-layout-changed", ""),
OPTIONS_TABLE_HOOK("window-linked", ""),
OPTIONS_TABLE_HOOK("window-pane-changed", ""),
OPTIONS_TABLE_HOOK("window-renamed", ""),
OPTIONS_TABLE_HOOK("window-unlinked", ""),
{ .name = NULL }
};

View File

@ -76,6 +76,9 @@ static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_IS_STYLE(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_STYLE)
#define OPTIONS_IS_COMMAND(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_COMMAND)
#define OPTIONS_IS_ARRAY(o) \
((o)->tableentry != NULL && \
@ -108,6 +111,8 @@ options_value_free(struct options_entry *o, union options_value *ov)
{
if (OPTIONS_IS_STRING(o))
free(ov->string);
if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
cmd_list_free(ov->cmdlist);
}
static char *
@ -116,6 +121,8 @@ options_value_tostring(struct options_entry *o, union options_value *ov,
{
char *s;
if (OPTIONS_IS_COMMAND(o))
return (cmd_list_print(ov->cmdlist));
if (OPTIONS_IS_STYLE(o))
return (xstrdup(style_tostring(&ov->style)));
if (OPTIONS_IS_NUMBER(o)) {
@ -140,6 +147,7 @@ options_value_tostring(struct options_entry *o, union options_value *ov,
break;
case OPTIONS_TABLE_STRING:
case OPTIONS_TABLE_STYLE:
case OPTIONS_TABLE_COMMAND:
fatalx("not a number option type");
}
return (s);
@ -231,11 +239,12 @@ options_default(struct options *oo, const struct options_table_entry *oe)
ov = &o->value;
if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
if (oe->default_arr != NULL) {
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0);
} else
options_array_assign(o, oe->default_str);
if (oe->default_arr == NULL) {
options_array_assign(o, oe->default_str, NULL);
return (o);
}
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0, NULL);
return (o);
}
@ -340,13 +349,22 @@ options_array_get(struct options_entry *o, u_int idx)
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
int append)
int append, char **cause)
{
struct options_array_item *a;
char *new;
struct cmd_list *cmdlist;
if (!OPTIONS_IS_ARRAY(o))
if (!OPTIONS_IS_ARRAY(o)) {
*cause = xstrdup("not an array");
return (-1);
}
if (OPTIONS_IS_COMMAND(o)) {
cmdlist = cmd_string_parse(value, NULL, 0, cause);
if (cmdlist == NULL && *cause != NULL)
return (-1);
}
a = options_array_item(o, idx);
if (value == NULL) {
@ -355,25 +373,29 @@ options_array_set(struct options_entry *o, u_int idx, const char *value,
return (0);
}
if (a == NULL) {
a = xcalloc(1, sizeof *a);
a->index = idx;
a->value.string = xstrdup(value);
RB_INSERT(options_array, &o->value.array, a);
} else {
options_value_free(o, &a->value);
if (OPTIONS_IS_STRING(o)) {
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value.string, value);
else
new = xstrdup(value);
a->value.string = new;
}
if (a == NULL) {
a = xcalloc(1, sizeof *a);
a->index = idx;
RB_INSERT(options_array, &o->value.array, a);
} else
options_value_free(o, &a->value);
if (OPTIONS_IS_STRING(o))
a->value.string = new;
else if (OPTIONS_IS_COMMAND(o))
a->value.cmdlist = cmdlist;
return (0);
}
void
options_array_assign(struct options_entry *o, const char *s)
int
options_array_assign(struct options_entry *o, const char *s, char **cause)
{
const char *separator;
char *copy, *next, *string;
@ -382,7 +404,18 @@ options_array_assign(struct options_entry *o, const char *s)
separator = o->tableentry->separator;
if (separator == NULL)
separator = " ,";
if (*separator == '\0') {
if (*s == '\0')
return (0);
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
break;
}
return (options_array_set(o, i, s, 0, cause));
}
if (*s == '\0')
return (0);
copy = string = xstrdup(s);
while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0')
@ -393,9 +426,13 @@ options_array_assign(struct options_entry *o, const char *s)
}
if (i == UINT_MAX)
break;
options_array_set(o, i, next, 0);
if (options_array_set(o, i, next, 0, cause) != 0) {
free(copy);
return (-1);
}
}
free(copy);
return (0);
}
struct options_array_item *

View File

@ -129,7 +129,6 @@ session_create(const char *prefix, const char *name, const char *cwd,
s->environ = env;
s->options = oo;
s->hooks = hooks_create(global_hooks);
status_update_cache(s);
@ -193,9 +192,7 @@ session_free(__unused int fd, __unused short events, void *arg)
if (s->references == 0) {
environ_free(s->environ);
options_free(s->options);
hooks_free(s->hooks);
free(s->name);
free(s);

View File

@ -1323,7 +1323,7 @@ status_prompt_complete_list(u_int *size, const char *s)
while (a != NULL) {
value = options_array_item_value(a)->string;
if ((cp = strchr(value, '=')) == NULL)
goto next;
goto next;
valuelen = cp - value;
if (slen > valuelen || strncmp(value, s, slen) != 0)
goto next;

30
tmux.1
View File

@ -3383,7 +3383,7 @@ function key sequences; these have a number included to indicate modifiers such
as Shift, Alt or Ctrl.
.El
.It Xo Ic show-options
.Op Fl gqsvw
.Op Fl gHqsvw
.Op Fl t Ar target-session | Ar target-window
.Op Ar option
.Xc
@ -3415,6 +3415,8 @@ If
is set, no error will be returned if
.Ar option
is unset.
.Fl H
includes hooks (omitted by default).
.It Xo Ic show-window-options
.Op Fl gv
.Op Fl t Ar target-window
@ -3439,6 +3441,26 @@ commands have an
.Em after
hook and there are a number of hooks not associated with commands.
.Pp
Hooks are stored as array options, members of the array are executed in
order when the hook is triggered.
Hooks may be configured with the
.Ic set-hook
or
.Ic set-option
commands and displayed with
.Ic show-hooks
or
.Ic show-options
.Fl H .
The following two commands are equivalent:
.Bd -literal -offset indent.
set-hook -g pane-mode-changed[42] 'set -g status-left-style bg=red'
set-option -g pane-mode-changed[42] 'set -g status-left-style bg=red'
.Ed
.Pp
Setting a hook without specifying an array index clears the hook and sets the
first member of the array.
.Pp
A command's after
hook is run after it completes, except when the command is run as part of a hook
itself.
@ -3449,7 +3471,7 @@ For example, the following command adds a hook to select the even-vertical
layout after every
.Ic split-window :
.Bd -literal -offset indent
set-hook after-split-window "selectl even-vertical"
set-hook -g after-split-window "selectl even-vertical"
.Ed
.Pp
All the notifications listed in the
@ -3513,7 +3535,7 @@ Run when a window is unlinked from a session.
Hooks are managed with these commands:
.Bl -tag -width Ds
.It Xo Ic set-hook
.Op Fl gRu
.Op Fl agRu
.Op Fl t Ar target-session
.Ar hook-name
.Ar command
@ -3535,6 +3557,8 @@ hooks (for
.Ar target-session
with
.Fl t ) .
.Fl a
appends to a hook.
Like options, session hooks inherit from the global ones.
.Pp
With

3
tmux.c
View File

@ -39,7 +39,6 @@ struct options *global_options; /* server options */
struct options *global_s_options; /* session options */
struct options *global_w_options; /* window options */
struct environ *global_environ;
struct hooks *global_hooks;
struct timeval start_time;
const char *socket_path;
@ -312,8 +311,6 @@ main(int argc, char **argv)
flags |= CLIENT_UTF8;
}
global_hooks = hooks_create(NULL);
global_environ = environ_create();
for (var = environ; *var != NULL; var++)
environ_put(global_environ, *var);

39
tmux.h
View File

@ -683,15 +683,6 @@ struct style {
u_int range_argument;
};
/* Hook data structures. */
struct hook {
const char *name;
struct cmd_list *cmdlist;
RB_ENTRY(hook) entry;
};
/* Virtual screen. */
struct screen_sel;
struct screen_titles;
@ -995,7 +986,6 @@ struct session {
int statusat;
u_int statuslines;
struct hooks *hooks;
struct options *options;
#define SESSION_PASTING 0x1
@ -1521,6 +1511,7 @@ union options_value {
long long number;
struct style style;
struct options_array array;
struct cmd_list *cmdlist;
};
/* Option table entries. */
@ -1531,17 +1522,19 @@ enum options_table_type {
OPTIONS_TABLE_COLOUR,
OPTIONS_TABLE_FLAG,
OPTIONS_TABLE_CHOICE,
OPTIONS_TABLE_STYLE
OPTIONS_TABLE_STYLE,
OPTIONS_TABLE_COMMAND
};
enum options_table_scope {
OPTIONS_TABLE_NONE,
OPTIONS_TABLE_SERVER,
OPTIONS_TABLE_SESSION,
OPTIONS_TABLE_WINDOW,
OPTIONS_TABLE_WINDOW
};
#define OPTIONS_TABLE_IS_ARRAY 0x1
#define OPTIONS_TABLE_IS_HOOK 0x2
struct options_table_entry {
const char *name;
@ -1599,7 +1592,6 @@ struct spawn_context {
};
/* tmux.c */
extern struct hooks *global_hooks;
extern struct options *global_options;
extern struct options *global_s_options;
extern struct options *global_w_options;
@ -1693,20 +1685,6 @@ u_int format_width(const char *);
char *format_trim_left(const char *, u_int);
char *format_trim_right(const char *, u_int);
/* hooks.c */
struct hook;
struct hooks *hooks_get(struct session *);
struct hooks *hooks_create(struct hooks *);
void hooks_free(struct hooks *);
struct hook *hooks_first(struct hooks *);
struct hook *hooks_next(struct hook *);
void hooks_add(struct hooks *, const char *, struct cmd_list *);
void hooks_copy(struct hooks *, struct hooks *);
void hooks_remove(struct hooks *, const char *);
struct hook *hooks_find(struct hooks *, const char *);
void printflike(4, 5) hooks_insert(struct hooks *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...);
/* notify.c */
void notify_hook(struct cmdq_item *, const char *);
void notify_input(struct window_pane *, struct evbuffer *);
@ -1734,8 +1712,9 @@ void options_remove(struct options_entry *);
void options_array_clear(struct options_entry *);
union options_value *options_array_get(struct options_entry *, u_int);
int options_array_set(struct options_entry *, u_int, const char *,
int);
void options_array_assign(struct options_entry *, const char *);
int, char **);
int options_array_assign(struct options_entry *, const char *,
char **);
struct options_array_item *options_array_first(struct options_entry *);
struct options_array_item *options_array_next(struct options_array_item *);
u_int options_array_item_index(struct options_array_item *);
@ -1956,6 +1935,8 @@ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *,
struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *);
void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *);
void cmdq_append(struct client *, struct cmdq_item *);
void cmdq_insert_hook(struct session *, struct cmdq_item *,
struct cmd_find_state *, const char *, ...);
void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *,
const char *, ...);
u_int cmdq_next(struct client *);