Add initial framework for more powerful formatting of command output and

use it for list-{panes,windows,sessions}. This allows more descriptive
replacements (such as #{session_name}) and conditionals.

Later this will be used for status_replace and list-keys and other
places.
This commit is contained in:
Nicholas Marriott 2011-08-26 10:53:16 +00:00
parent 4697b35d4f
commit 4a5dff3f11
7 changed files with 578 additions and 96 deletions

View File

@ -30,7 +30,7 @@ SRCS= arguments.c attributes.c cfg.c client.c clock.c \
cmd-display-message.c cmd-display-panes.c cmd-if-shell.c \ cmd-display-message.c cmd-display-panes.c cmd-if-shell.c \
cmd-pipe-pane.c cmd-capture-pane.c cmd.c \ cmd-pipe-pane.c cmd-capture-pane.c cmd.c \
colour.c environ.c grid-view.c grid-utf8.c grid.c input-keys.c \ colour.c environ.c grid-view.c grid-utf8.c grid.c input-keys.c \
input.c key-bindings.c key-string.c \ input.c key-bindings.c key-string.c format.c \
layout-custom.c layout-set.c layout.c log.c job.c \ layout-custom.c layout-set.c layout.c log.c job.c \
mode-key.c names.c options.c options-table.c paste.c procname.c \ mode-key.c names.c options.c options-table.c paste.c procname.c \
resize.c screen-redraw.c screen-write.c screen.c session.c status.c \ resize.c screen-redraw.c screen-write.c screen.c session.c status.c \

View File

@ -28,15 +28,16 @@
int cmd_list_panes_exec(struct cmd *, struct cmd_ctx *); int cmd_list_panes_exec(struct cmd *, struct cmd_ctx *);
void cmd_list_panes_server(struct cmd_ctx *); void cmd_list_panes_server(struct cmd *, struct cmd_ctx *);
void cmd_list_panes_session(struct session *, struct cmd_ctx *, int); void cmd_list_panes_session(
void cmd_list_panes_window( struct cmd *, struct session *, struct cmd_ctx *, int);
void cmd_list_panes_window(struct cmd *,
struct session *, struct winlink *, struct cmd_ctx *, int); struct session *, struct winlink *, struct cmd_ctx *, int);
const struct cmd_entry cmd_list_panes_entry = { const struct cmd_entry cmd_list_panes_entry = {
"list-panes", "lsp", "list-panes", "lsp",
"ast:", 0, 0, "asF:t:", 0, 0,
"[-as] [-t target]", "[-as] [-F format] [-t target]",
0, 0,
NULL, NULL,
NULL, NULL,
@ -51,87 +52,92 @@ cmd_list_panes_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl; struct winlink *wl;
if (args_has(args, 'a')) if (args_has(args, 'a'))
cmd_list_panes_server(ctx); cmd_list_panes_server(self, ctx);
else if (args_has(args, 's')) { else if (args_has(args, 's')) {
s = cmd_find_session(ctx, args_get(args, 't'), 0); s = cmd_find_session(ctx, args_get(args, 't'), 0);
if (s == NULL) if (s == NULL)
return (-1); return (-1);
cmd_list_panes_session(s, ctx, 1); cmd_list_panes_session(self, s, ctx, 1);
} else { } else {
wl = cmd_find_window(ctx, args_get(args, 't'), &s); wl = cmd_find_window(ctx, args_get(args, 't'), &s);
if (wl == NULL) if (wl == NULL)
return (-1); return (-1);
cmd_list_panes_window(s, wl, ctx, 0); cmd_list_panes_window(self, s, wl, ctx, 0);
} }
return (0); return (0);
} }
void void
cmd_list_panes_server(struct cmd_ctx *ctx) cmd_list_panes_server(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct session *s; struct session *s;
RB_FOREACH(s, sessions, &sessions) RB_FOREACH(s, sessions, &sessions)
cmd_list_panes_session(s, ctx, 2); cmd_list_panes_session(self, s, ctx, 2);
} }
void void
cmd_list_panes_session(struct session *s, struct cmd_ctx *ctx, int type) cmd_list_panes_session(
struct cmd *self, struct session *s, struct cmd_ctx *ctx, int type)
{ {
struct winlink *wl; struct winlink *wl;
RB_FOREACH(wl, winlinks, &s->windows) RB_FOREACH(wl, winlinks, &s->windows)
cmd_list_panes_window(s, wl, ctx, type); cmd_list_panes_window(self, s, wl, ctx, type);
} }
void void
cmd_list_panes_window( cmd_list_panes_window(struct cmd *self,
struct session *s, struct winlink *wl, struct cmd_ctx *ctx, int type) struct session *s, struct winlink *wl, struct cmd_ctx *ctx, int type)
{ {
struct args *args = self->args;
struct window_pane *wp; struct window_pane *wp;
struct grid *gd; u_int n;
struct grid_line *gl; struct format_tree *ft;
u_int i, n; const char *template;
unsigned long long size; char *line;
template = args_get(args, 'F');
if (template == NULL) {
switch (type) {
case 0:
template = "#{line}: "
"[#{pane_width}x#{pane_height}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
case 1:
template = "#{window_index}.#{line}: "
"[#{pane_width}x#{pane_height}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
case 2:
template = "#{session_name}:#{window_index}.#{line}: "
"[#{pane_width}x#{pane_height}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
}
}
n = 0; n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) { TAILQ_FOREACH(wp, &wl->window->panes, entry) {
gd = wp->base.grid; ft = format_create();
format_add(ft, "line", "%u", n);
format_session(ft, s);
format_winlink(ft, s, wl);
format_window_pane(ft, wp);
size = 0; line = format_expand(ft, template);
for (i = 0; i < gd->hsize; i++) { ctx->print(ctx, "%s", line);
gl = &gd->linedata[i]; xfree(line);
size += gl->cellsize * sizeof *gl->celldata;
size += gl->utf8size * sizeof *gl->utf8data;
}
size += gd->hsize * sizeof *gd->linedata;
switch (type) { format_free(ft);
case 0:
ctx->print(ctx,
"%u: [%ux%u] [history %u/%u, %llu bytes] %%%u%s%s",
n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
wp->id, wp == wp->window->active ? " (active)" : "",
wp->fd == -1 ? " (dead)" : "");
break;
case 1:
ctx->print(ctx,
"%d.%u: [%ux%u] [history %u/%u, %llu bytes] "
"%%%u%s%s", wl->idx,
n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
wp->id, wp == wp->window->active ? " (active)" : "",
wp->fd == -1 ? " (dead)" : "");
break;
case 2:
ctx->print(ctx,
"%s:%d.%u: [%ux%u] [history %u/%u, %llu bytes] "
"%%%u%s%s", s->name, wl->idx,
n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
wp->id, wp == wp->window->active ? " (active)" : "",
wp->fd == -1 ? " (dead)" : "");
break;
}
n++; n++;
} }
} }

View File

@ -31,40 +31,45 @@ int cmd_list_sessions_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_list_sessions_entry = { const struct cmd_entry cmd_list_sessions_entry = {
"list-sessions", "ls", "list-sessions", "ls",
"", 0, 0, "F:", 0, 0,
"", "[-F format]",
0, 0,
NULL, NULL,
NULL, NULL,
cmd_list_sessions_exec cmd_list_sessions_exec
}; };
/* ARGSUSED */
int int
cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx) cmd_list_sessions_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct args *args = self->args;
struct session *s; struct session *s;
struct session_group *sg; u_int n;
char *tim, tmp[64]; struct format_tree *ft;
u_int idx; const char *template;
time_t t; char *line;
template = args_get(args, 'F');
if (template == NULL) {
template = "#{session_name}: #{session_windows} windows "
"(created #{session_created_string}) [#{session_width}x"
"#{session_height}]#{?session_grouped, (group ,}"
"#{session_group}#{?session_grouped,),}"
"#{?session_attached, (attached),}";
}
n = 0;
RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(s, sessions, &sessions) {
sg = session_group_find(s); ft = format_create();
if (sg == NULL) format_add(ft, "line", "%u", n);
*tmp = '\0'; format_session(ft, s);
else {
idx = session_group_index(sg);
xsnprintf(tmp, sizeof tmp, " (group %u)", idx);
}
t = s->creation_time.tv_sec; line = format_expand(ft, template);
tim = ctime(&t); ctx->print(ctx, "%s", line);
*strchr(tim, '\n') = '\0'; xfree(line);
ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s%s", format_free(ft);
s->name, winlink_count(&s->windows), tim, s->sx, s->sy, n++;
tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
} }
return (0); return (0);

View File

@ -28,13 +28,14 @@
int cmd_list_windows_exec(struct cmd *, struct cmd_ctx *); int cmd_list_windows_exec(struct cmd *, struct cmd_ctx *);
void cmd_list_windows_server(struct cmd_ctx *); void cmd_list_windows_server(struct cmd *, struct cmd_ctx *);
void cmd_list_windows_session(struct session *, struct cmd_ctx *, int); void cmd_list_windows_session(
struct cmd *, struct session *, struct cmd_ctx *, int);
const struct cmd_entry cmd_list_windows_entry = { const struct cmd_entry cmd_list_windows_entry = {
"list-windows", "lsw", "list-windows", "lsw",
"at:", 0, 0, "aF:t:", 0, 0,
"[-a] " CMD_TARGET_SESSION_USAGE, "[-a] [-F format] " CMD_TARGET_SESSION_USAGE,
0, 0,
NULL, NULL,
NULL, NULL,
@ -48,45 +49,69 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
struct session *s; struct session *s;
if (args_has(args, 'a')) if (args_has(args, 'a'))
cmd_list_windows_server(ctx); cmd_list_windows_server(self, ctx);
else { else {
s = cmd_find_session(ctx, args_get(args, 't'), 0); s = cmd_find_session(ctx, args_get(args, 't'), 0);
if (s == NULL) if (s == NULL)
return (-1); return (-1);
cmd_list_windows_session(s, ctx, 0); cmd_list_windows_session(self, s, ctx, 0);
} }
return (0); return (0);
} }
void void
cmd_list_windows_server(struct cmd_ctx *ctx) cmd_list_windows_server(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct session *s; struct session *s;
RB_FOREACH(s, sessions, &sessions) RB_FOREACH(s, sessions, &sessions)
cmd_list_windows_session(s, ctx, 1); cmd_list_windows_session(self, s, ctx, 1);
} }
void void
cmd_list_windows_session(struct session *s, struct cmd_ctx *ctx, int type) cmd_list_windows_session(
struct cmd *self, struct session *s, struct cmd_ctx *ctx, int type)
{ {
struct winlink *wl; struct args *args = self->args;
char *layout; struct winlink *wl;
u_int n;
struct format_tree *ft;
const char *template;
char *line;
RB_FOREACH(wl, winlinks, &s->windows) { template = args_get(args, 'F');
layout = layout_dump(wl->window); if (template == NULL) {
if (type) { switch (type) {
ctx->print(ctx, "%s:%d: %s [%ux%u] [layout %s]%s", case 0:
s->name, wl->idx, wl->window->name, wl->window->sx, template = "#{window_index}: "
wl->window->sy, layout, "#{window_name} "
wl == s->curw ? " (active)" : ""); "[#{window_width}x#{window_height}] "
} else { "[layout #{window_layout}]"
ctx->print(ctx, "%d: %s [%ux%u] [layout %s]%s", "#{?window_active, (active),}";
wl->idx, wl->window->name, wl->window->sx, break;
wl->window->sy, layout, case 1:
wl == s->curw ? " (active)" : ""); template = "#{session_name):#{window_index}: "
"#{window_name} "
"[#{window_width}x#{window_height}] "
"[layout #{window_layout}]"
"#{?window_active, (active),}";
break;
} }
xfree(layout); }
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create();
format_add(ft, "line", "%u", n);
format_session(ft, s);
format_winlink(ft, s, wl);
line = format_expand(ft, template);
ctx->print(ctx, "%s", line);
xfree(line);
format_free(ft);
n++;
} }
} }

346
format.c Normal file
View File

@ -0,0 +1,346 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicm@users.sourceforge.net>
*
* 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 <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "tmux.h"
/*
* Build a list of key-value pairs and use them to expand #{key} entries in a
* string.
*/
int format_replace(struct format_tree *,
const char *, size_t, char **, size_t *, size_t *);
/* Format key-value replacement entry. */
RB_GENERATE(format_tree, format_entry, entry, format_cmp);
/* Format tree comparison function. */
int
format_cmp(struct format_entry *fe1, struct format_entry *fe2)
{
return (strcmp(fe1->key, fe2->key));
}
/* Single-character aliases. */
const char *format_aliases[26] = {
NULL, /* A */
NULL, /* B */
NULL, /* C */
"pane_id", /* D */
NULL, /* E */
"window_flags", /* F */
NULL, /* G */
"host", /* H */
"window_index", /* I */
NULL, /* J */
NULL, /* K */
NULL, /* L */
NULL, /* M */
NULL, /* N */
NULL, /* O */
"pane_index", /* P */
NULL, /* Q */
NULL, /* R */
"session_name", /* S */
"pane_title", /* T */
NULL, /* U */
NULL, /* V */
"window_name", /* W */
NULL, /* X */
NULL, /* Y */
NULL /* Z */
};
/* Create a new tree. */
struct format_tree *
format_create(void)
{
struct format_tree *ft;
char host[MAXHOSTNAMELEN];
ft = xmalloc(sizeof *ft);
RB_INIT(ft);
if (gethostname(host, sizeof host) == 0)
format_add(ft, "host", "%s", host);
return (ft);
}
/* Free a tree. */
void
format_free(struct format_tree *ft)
{
struct format_entry *fe, *fe_next;
fe_next = RB_MIN(format_tree, ft);
while (fe_next != NULL) {
fe = fe_next;
fe_next = RB_NEXT(format_tree, ft, fe);
RB_REMOVE(format_tree, ft, fe);
xfree(fe->value);
xfree(fe->key);
xfree(fe);
}
xfree (ft);
}
/* Add a key-value pair. */
void
format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
{
struct format_entry *fe;
va_list ap;
fe = xmalloc(sizeof *fe);
fe->key = xstrdup(key);
va_start(ap, fmt);
xvasprintf(&fe->value, fmt, ap);
va_end(ap);
RB_INSERT(format_tree, ft, fe);
}
/* Find a format entry. */
const char *
format_find(struct format_tree *ft, const char *key)
{
struct format_entry *fe, fe_find;
fe_find.key = (char *) key;
fe = RB_FIND(format_tree, ft, &fe_find);
if (fe == NULL)
return (NULL);
return (fe->value);
}
/*
* Replace a key/value pair in buffer. #{blah} is expanded directly,
* #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
*/
int
format_replace(struct format_tree *ft,
const char *key, size_t keylen, char **buf, size_t *len, size_t *off)
{
char *copy, *ptr;
const char *value;
size_t valuelen;
/* Make a copy of the key. */
copy = xmalloc(keylen + 1);
memcpy(copy, key, keylen);
copy[keylen] = '\0';
/*
* Is this a conditional? If so, check it exists and extract either the
* first or second element. If not, look up the key directly.
*/
if (*copy == '?') {
ptr = strchr(copy, ',');
if (ptr == NULL)
goto fail;
*ptr = '\0';
value = format_find(ft, copy + 1);
if (value != NULL && (value[0] != '0' || value[1] != '\0')) {
value = ptr + 1;
ptr = strchr(value, ',');
if (ptr == NULL)
goto fail;
*ptr = '\0';
} else {
ptr = strchr(ptr + 1, ',');
if (ptr == NULL)
goto fail;
value = ptr + 1;
}
} else {
value = format_find(ft, copy);
if (value == NULL)
value = "";
}
valuelen = strlen(value);
/* Expand the buffer and copy in the value. */
while (*len - *off < valuelen + 1) {
*buf = xrealloc(*buf, 2, *len);
*len *= 2;
}
memcpy(*buf + *off, value, valuelen);
*off += valuelen;
xfree(copy);
return (0);
fail:
xfree(copy);
return (-1);
}
/* Expand keys in a template. */
char *
format_expand(struct format_tree *ft, const char *fmt)
{
char *buf, *ptr;
const char *s;
size_t off, len, n;
int ch;
len = 64;
buf = xmalloc(len);
off = 0;
while (*fmt != '\0') {
if (*fmt != '#') {
while (len - off < 2) {
buf = xrealloc(buf, 2, len);
len *= 2;
}
buf[off++] = *fmt++;
continue;
}
fmt++;
ch = (u_char) *fmt++;
switch (ch) {
case '{':
ptr = strchr(fmt, '}');
if (ptr == NULL)
break;
n = ptr - fmt;
if (format_replace(ft, fmt, n, &buf, &len, &off) != 0)
break;
fmt += n + 1;
continue;
default:
if (ch >= 'A' && ch <= 'Z') {
s = format_aliases[ch - 'A'];
if (s != NULL) {
n = strlen(s);
if (format_replace (
ft, s, n, &buf, &len, &off) != 0)
break;
continue;
}
}
while (len - off < 2) {
buf = xrealloc(buf, 2, len);
len *= 2;
}
buf[off++] = ch;
continue;
}
break;
}
buf[off] = '\0';
return (buf);
}
/* Set default format keys for a session. */
void
format_session(struct format_tree *ft, struct session *s)
{
struct session_group *sg;
char *tim;
time_t t;
format_add(ft, "session_name", "%s", s->name);
format_add(ft, "session_windows", "%u", winlink_count(&s->windows));
format_add(ft, "session_width", "%u", s->sx);
format_add(ft, "session_height", "%u", s->sy);
sg = session_group_find(s);
format_add(ft, "session_grouped", "%d", sg != NULL);
if (sg != NULL)
format_add(ft, "session_group", "%u", session_group_index(sg));
t = s->creation_time.tv_sec;
format_add(ft, "session_created", "%ld", (long) t);
tim = ctime(&t);
*strchr(tim, '\n') = '\0';
format_add(ft, "session_created_string", "%s", tim);
if (s->flags & SESSION_UNATTACHED)
format_add(ft, "session_attached", "%d", 0);
else
format_add(ft, "session_attached", "%d", 1);
}
/* Set default format keys for a winlink. */
void
format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
{
struct window *w = wl->window;
char *layout, *flags;
layout = layout_dump(w);
flags = window_printable_flags(s, wl);
format_add(ft, "window_index", "%d", wl->idx);
format_add(ft, "window_name", "%s", w->name);
format_add(ft, "window_width", "%u", w->sx);
format_add(ft, "window_height", "%u", w->sy);
format_add(ft, "window_flags", "%s", flags);
format_add(ft, "window_layout", "%s", layout);
format_add(ft, "window_active", "%d", wl == s->curw);
xfree(flags);
xfree(layout);
}
/* Set default format keys for a window pane. */
void
format_window_pane(struct format_tree *ft, struct window_pane *wp)
{
struct grid *gd = wp->base.grid;
struct grid_line *gl;
unsigned long long size;
u_int i;
size = 0;
for (i = 0; i < gd->hsize; i++) {
gl = &gd->linedata[i];
size += gl->cellsize * sizeof *gl->celldata;
size += gl->utf8size * sizeof *gl->utf8data;
}
size += gd->hsize * sizeof *gd->linedata;
format_add(ft, "pane_width", "%u", wp->sx);
format_add(ft, "pane_height", "%u", wp->sy);
format_add(ft, "pane_title", "%s", wp->base.title);
format_add(ft, "history_size", "%u", gd->hsize);
format_add(ft, "history_limit", "%u", gd->hlimit);
format_add(ft, "history_bytes", "%llu", size);
format_add(ft, "pane_id", "%%%u", wp->id);
format_add(ft, "pane_active", "%d", wp == wp->window->active);
format_add(ft, "pane_dead", "%d", wp->fd == -1);
}

77
tmux.1
View File

@ -617,6 +617,7 @@ is specified, list only clients connected to that session.
List the syntax of all commands supported by List the syntax of all commands supported by
.Nm . .Nm .
.It Ic list-sessions .It Ic list-sessions
.Op Fl F Ar format
.D1 (alias: Ic ls ) .D1 (alias: Ic ls )
List all sessions managed by the server. List all sessions managed by the server.
.It Ic lock-client Op Fl t Ar target-client .It Ic lock-client Op Fl t Ar target-client
@ -626,6 +627,11 @@ Lock
see the see the
.Ic lock-server .Ic lock-server
command. command.
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.It Ic lock-session Op Fl t Ar target-session .It Ic lock-session Op Fl t Ar target-session
.D1 (alias: Ic locks ) .D1 (alias: Ic locks )
Lock all clients attached to Lock all clients attached to
@ -1147,6 +1153,7 @@ If
is given, the newly linked window is not selected. is given, the newly linked window is not selected.
.It Xo Ic list-panes .It Xo Ic list-panes
.Op Fl as .Op Fl as
.Op Fl F Ar format
.Op Fl t Ar target .Op Fl t Ar target
.Xc .Xc
.D1 (alias: Ic lsp ) .D1 (alias: Ic lsp )
@ -1165,6 +1172,7 @@ If neither is given,
is a window (or the current window). is a window (or the current window).
.It Xo Ic list-windows .It Xo Ic list-windows
.Op Fl a .Op Fl a
.Op Fl F Ar format
.Op Fl t Ar target-session .Op Fl t Ar target-session
.Xc .Xc
.D1 (alias: Ic lsw ) .D1 (alias: Ic lsw )
@ -1173,6 +1181,11 @@ If
is given, list all windows on the server. is given, list all windows on the server.
Otherwise, list windows in the current session or in Otherwise, list windows in the current session or in
.Ar target-session . .Ar target-session .
For the meaning of the
.Fl F
flag, see the
.Sx FORMATS
section.
.It Xo Ic move-window .It Xo Ic move-window
.Op Fl dk .Op Fl dk
.Op Fl s Ar src-window .Op Fl s Ar src-window
@ -2535,6 +2548,70 @@ or the global window options if
.Fl g .Fl g
is used. is used.
.El .El
.Sh FORMATS
The
.Ic list-sessions ,
.Ic list-windows
and
.Ic list-panes
commands accept the
.Fl F
flag with a
.Ar format
argument.
This is a string which controls the output format of the command.
Special character sequences are replaced as documented under the
.Ic status-left
option and an additional long form is accepted.
Replacement variables are enclosed in
.Ql #{
and
.Ql } ,
for example
.Ql #{session_name}
is equivalent to
.Ql #S .
Conditionals are also accepted by prefixing with
.Ql ?
and separating two alternatives with a comma;
if the specified variable exists and is not zero, the first alternative
is chosen, otherwise the second is used. For example
.Ql #{?session_attached,attached,not attached}
will include the string
.Ql attached
if the session is attached and the string
.Ql not attached
if it is unattached.
.Pp
The following variables are available, where appropriate:
.Bl -column "session_created_string" "Replaced with" -offset indent
.It Sy "Variable name" Ta Sy "Replaced with"
.It Li "host" Ta "Hostname of local host"
.It Li "line" Ta "Line number in the list"
.It Li "pane_active" Ta "1 if active pane"
.It Li "pane_dead" Ta "1 if pane is dead"
.It Li "pane_height" Ta "Height of pane"
.It Li "pane_id" Ta "Unique pane id"
.It Li "pane_title" Ta "Title of pane"
.It Li "pane_width" Ta "Width of pane"
.It Li "session_attached" Ta "1 if session attached"
.It Li "session_created" Ta "Integer time session created"
.It Li "session_created_string" Ta "String time session created"
.It Li "session_group" Ta "Number of session group"
.It Li "session_grouped" Ta "1 if session in a group"
.It Li "session_height" Ta "Height of session"
.It Li "session_name" Ta "Name of session"
.It Li "session_width" Ta "Width of session"
.It Li "session_windows" Ta "Number of windows in session"
.It Li "window_active" Ta "1 if window active"
.It Li "window_flags" Ta "Window flags"
.It Li "window_height" Ta "Height of window"
.It Li "window_index" Ta "Index of window"
.It Li "window_layout" Ta "Window layout description"
.It Li "window_name" Ta "Name of window"
.It Li "window_width" Ta "Width of window"
.El
.Pp
.Sh ENVIRONMENT .Sh ENVIRONMENT
When the server is started, When the server is started,
.Nm .Nm

23
tmux.h
View File

@ -1303,6 +1303,15 @@ struct options_table_entry {
long long default_num; long long default_num;
}; };
/* Tree of format entries. */
struct format_entry {
char *key;
char *value;
RB_ENTRY(format_entry) entry;
};
RB_HEAD(format_tree, format_entry);
/* List of configuration causes. */ /* List of configuration causes. */
ARRAY_DECL(causelist, char *); ARRAY_DECL(causelist, char *);
@ -1345,6 +1354,20 @@ extern struct causelist cfg_causes;
void printflike2 cfg_add_cause(struct causelist *, const char *, ...); void printflike2 cfg_add_cause(struct causelist *, const char *, ...);
int load_cfg(const char *, struct cmd_ctx *, struct causelist *); int load_cfg(const char *, struct cmd_ctx *, struct causelist *);
/* format.c */
int format_cmp(struct format_entry *, struct format_entry *);
RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp);
struct format_tree *format_create(void);
void format_free(struct format_tree *);
void format_add(
struct format_tree *, const char *, const char *, ...);
const char *format_find(struct format_tree *, const char *);
char *format_expand(struct format_tree *, const char *);
void format_session(struct format_tree *, struct session *);
void format_winlink(
struct format_tree *, struct session *, struct winlink *);
void format_window_pane(struct format_tree *, struct window_pane *);
/* mode-key.c */ /* mode-key.c */
extern const struct mode_key_table mode_key_tables[]; extern const struct mode_key_table mode_key_tables[];
extern struct mode_key_tree mode_key_tree_vi_edit; extern struct mode_key_tree mode_key_tree_vi_edit;