aboutsummaryrefslogtreecommitdiff
path: root/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'status.c')
-rw-r--r--status.c594
1 files changed, 409 insertions, 185 deletions
diff --git a/status.c b/status.c
index 33f6c47a..a4844294 100644
--- a/status.c
+++ b/status.c
@@ -37,9 +37,17 @@ static const char *status_prompt_up_history(u_int *);
static const char *status_prompt_down_history(u_int *);
static void status_prompt_add_history(const char *);
-static char **status_prompt_complete_list(u_int *, const char *);
-static char *status_prompt_complete_prefix(char **, u_int);
-static char *status_prompt_complete(struct session *, const char *);
+static char *status_prompt_complete(struct client *, const char *, u_int);
+static char *status_prompt_complete_window_menu(struct client *,
+ struct session *, const char *, u_int, char);
+
+struct status_prompt_menu {
+ struct client *c;
+ u_int start;
+ u_int size;
+ char **list;
+ char flag;
+};
/* Status prompt history. */
#define PROMPT_HISTORY 100
@@ -321,7 +329,7 @@ status_redraw(struct client *c)
struct screen_write_ctx ctx;
struct grid_cell gc;
u_int lines, i, n, width = c->tty.sx;
- int flags, force = 0, changed = 0;
+ int flags, force = 0, changed = 0, fg, bg;
struct options_entry *o;
union options_value *ov;
struct format_tree *ft;
@@ -338,8 +346,21 @@ status_redraw(struct client *c)
if (c->tty.sy == 0 || lines == 0)
return (1);
+ /* Create format tree. */
+ flags = FORMAT_STATUS;
+ if (c->flags & CLIENT_STATUSFORCE)
+ flags |= FORMAT_FORCE;
+ ft = format_create(c, NULL, FORMAT_NONE, flags);
+ format_defaults(ft, c, NULL, NULL, NULL);
+
/* Set up default colour. */
- style_apply(&gc, s->options, "status-style");
+ style_apply(&gc, s->options, "status-style", ft);
+ fg = options_get_number(s->options, "status-fg");
+ if (fg != 8)
+ gc.fg = fg;
+ bg = options_get_number(s->options, "status-bg");
+ if (bg != 8)
+ gc.bg = bg;
if (!grid_cells_equal(&gc, &sl->style)) {
force = 1;
memcpy(&sl->style, &gc, sizeof sl->style);
@@ -353,13 +374,6 @@ status_redraw(struct client *c)
}
screen_write_start(&ctx, NULL, &sl->screen);
- /* Create format tree. */
- flags = FORMAT_STATUS;
- if (c->flags & CLIENT_STATUSFORCE)
- flags |= FORMAT_FORCE;
- ft = format_create(c, NULL, FORMAT_NONE, flags);
- format_defaults(ft, c, NULL, NULL, NULL);
-
/* Write the status lines. */
o = options_get(s->options, "status-format");
if (o == NULL) {
@@ -476,6 +490,7 @@ status_message_redraw(struct client *c)
size_t len;
u_int lines, offset;
struct grid_cell gc;
+ struct format_tree *ft;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@@ -490,7 +505,9 @@ status_message_redraw(struct client *c)
if (len > c->tty.sx)
len = c->tty.sx;
- style_apply(&gc, s->options, "message-style");
+ ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
+ style_apply(&gc, s->options, "message-style", ft);
+ format_free(ft);
screen_write_start(&ctx, NULL, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
@@ -622,6 +639,7 @@ status_prompt_redraw(struct client *c)
u_int i, lines, offset, left, start, width;
u_int pcursor, pwidth;
struct grid_cell gc, cursorgc;
+ struct format_tree *ft;
if (c->tty.sx == 0 || c->tty.sy == 0)
return (0);
@@ -632,10 +650,12 @@ status_prompt_redraw(struct client *c)
lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0);
+ ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
if (c->prompt_mode == PROMPT_COMMAND)
- style_apply(&gc, s->options, "message-command-style");
+ style_apply(&gc, s->options, "message-command-style", ft);
else
- style_apply(&gc, s->options, "message-style");
+ style_apply(&gc, s->options, "message-style", ft);
+ format_free(ft);
memcpy(&cursorgc, &gc, sizeof cursorgc);
cursorgc.attr ^= GRID_ATTR_REVERSE;
@@ -733,6 +753,7 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
if (c->prompt_mode == PROMPT_ENTRY) {
switch (key) {
case '\003': /* C-c */
+ case '\007': /* C-g */
case '\010': /* C-h */
case '\011': /* Tab */
case '\025': /* C-u */
@@ -813,6 +834,9 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key)
case 'p':
*new_key = '\031'; /* C-y */
return (1);
+ case 'q':
+ *new_key = '\003'; /* C-c */
+ return (1);
case 's':
case KEYC_DC:
case 'x':
@@ -909,15 +933,86 @@ status_prompt_paste(struct client *c)
return (1);
}
+/* Finish completion. */
+static int
+status_prompt_replace_complete(struct client *c, const char *s)
+{
+ char word[64], *allocated = NULL;
+ size_t size, n, off, idx, used;
+ struct utf8_data *first, *last, *ud;
+
+ /* Work out where the cursor currently is. */
+ idx = c->prompt_index;
+ if (idx != 0)
+ idx--;
+ size = utf8_strlen(c->prompt_buffer);
+
+ /* Find the word we are in. */
+ first = &c->prompt_buffer[idx];
+ while (first > c->prompt_buffer && !status_prompt_space(first))
+ first--;
+ while (first->size != 0 && status_prompt_space(first))
+ first++;
+ last = &c->prompt_buffer[idx];
+ while (last->size != 0 && !status_prompt_space(last))
+ last++;
+ while (last > c->prompt_buffer && status_prompt_space(last))
+ last--;
+ if (last->size != 0)
+ last++;
+ if (last < first)
+ return (0);
+ if (s == NULL) {
+ used = 0;
+ for (ud = first; ud < last; ud++) {
+ if (used + ud->size >= sizeof word)
+ break;
+ memcpy(word + used, ud->data, ud->size);
+ used += ud->size;
+ }
+ if (ud != last)
+ return (0);
+ word[used] = '\0';
+ }
+
+ /* Try to complete it. */
+ if (s == NULL) {
+ allocated = status_prompt_complete(c, word,
+ first - c->prompt_buffer);
+ if (allocated == NULL)
+ return (0);
+ s = allocated;
+ }
+
+ /* Trim out word. */
+ n = size - (last - c->prompt_buffer) + 1; /* with \0 */
+ memmove(first, last, n * sizeof *c->prompt_buffer);
+ size -= last - first;
+
+ /* Insert the new word. */
+ size += strlen(s);
+ off = first - c->prompt_buffer;
+ c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
+ sizeof *c->prompt_buffer);
+ first = c->prompt_buffer + off;
+ memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
+ for (idx = 0; idx < strlen(s); idx++)
+ utf8_set(&first[idx], s[idx]);
+ c->prompt_index = (first - c->prompt_buffer) + strlen(s);
+
+ free(allocated);
+ return (1);
+}
+
/* Handle keys in prompt. */
int
status_prompt_key(struct client *c, key_code key)
{
struct options *oo = c->session->options;
- char *s, *cp, word[64], prefix = '=';
+ char *s, *cp, prefix = '=';
const char *histstr, *ws = NULL, *keystring;
- size_t size, n, off, idx, used;
- struct utf8_data tmp, *first, *last, *ud;
+ size_t size, idx;
+ struct utf8_data tmp;
int keys;
if (c->prompt_flags & PROMPT_KEY) {
@@ -982,63 +1077,9 @@ process_key:
}
break;
case '\011': /* Tab */
- if (c->prompt_buffer[0].size == 0)
- break;
-
- idx = c->prompt_index;
- if (idx != 0)
- idx--;
-
- /* Find the word we are in. */
- first = &c->prompt_buffer[idx];
- while (first > c->prompt_buffer && !status_prompt_space(first))
- first--;
- while (first->size != 0 && status_prompt_space(first))
- first++;
- last = &c->prompt_buffer[idx];
- while (last->size != 0 && !status_prompt_space(last))
- last++;
- while (last > c->prompt_buffer && status_prompt_space(last))
- last--;
- if (last->size != 0)
- last++;
- if (last <= first)
- break;
-
- used = 0;
- for (ud = first; ud < last; ud++) {
- if (used + ud->size >= sizeof word)
- break;
- memcpy(word + used, ud->data, ud->size);
- used += ud->size;
- }
- if (ud != last)
- break;
- word[used] = '\0';
-
- /* And try to complete it. */
- if ((s = status_prompt_complete(c->session, word)) == NULL)
- break;
-
- /* Trim out word. */
- n = size - (last - c->prompt_buffer) + 1; /* with \0 */
- memmove(first, last, n * sizeof *c->prompt_buffer);
- size -= last - first;
-
- /* Insert the new word. */
- size += strlen(s);
- off = first - c->prompt_buffer;
- c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
- sizeof *c->prompt_buffer);
- first = c->prompt_buffer + off;
- memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
- for (idx = 0; idx < strlen(s); idx++)
- utf8_set(&first[idx], s[idx]);
-
- c->prompt_index = (first - c->prompt_buffer) + strlen(s);
- free(s);
-
- goto changed;
+ if (status_prompt_replace_complete(c, NULL))
+ goto changed;
+ break;
case KEYC_BSPACE:
case '\010': /* C-h */
if (c->prompt_index != 0) {
@@ -1320,14 +1361,13 @@ status_prompt_add_history(const char *line)
}
/* Build completion list. */
-char **
-status_prompt_complete_list(u_int *size, const char *s)
+static char **
+status_prompt_complete_list(u_int *size, const char *s, int at_start)
{
char **list = NULL;
const char **layout, *value, *cp;
const struct cmd_entry **cmdent;
const struct options_table_entry *oe;
- u_int idx;
size_t slen = strlen(s), valuelen;
struct options_entry *o;
struct options_array_item *a;
@@ -1342,17 +1382,10 @@ status_prompt_complete_list(u_int *size, const char *s)
list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrdup((*cmdent)->name);
}
- }
- for (oe = options_table; oe->name != NULL; oe++) {
- if (strncmp(oe->name, s, slen) == 0) {
- list = xreallocarray(list, (*size) + 1, sizeof *list);
- list[(*size)++] = xstrdup(oe->name);
- }
- }
- for (layout = layouts; *layout != NULL; layout++) {
- if (strncmp(*layout, s, slen) == 0) {
+ if ((*cmdent)->alias != NULL &&
+ strncmp((*cmdent)->alias, s, slen) == 0) {
list = xreallocarray(list, (*size) + 1, sizeof *list);
- list[(*size)++] = xstrdup(*layout);
+ list[(*size)++] = xstrdup((*cmdent)->alias);
}
}
o = options_get_only(global_options, "command-alias");
@@ -1373,8 +1406,21 @@ status_prompt_complete_list(u_int *size, const char *s)
a = options_array_next(a);
}
}
- for (idx = 0; idx < (*size); idx++)
- log_debug("complete %u: %s", idx, list[idx]);
+ if (at_start)
+ return (list);
+
+ for (oe = options_table; oe->name != NULL; oe++) {
+ if (strncmp(oe->name, s, slen) == 0) {
+ list = xreallocarray(list, (*size) + 1, sizeof *list);
+ list[(*size)++] = xstrdup(oe->name);
+ }
+ }
+ for (layout = layouts; *layout != NULL; layout++) {
+ if (strncmp(*layout, s, slen) == 0) {
+ list = xreallocarray(list, (*size) + 1, sizeof *list);
+ list[(*size)++] = xstrdup(*layout);
+ }
+ }
return (list);
}
@@ -1399,124 +1445,302 @@ status_prompt_complete_prefix(char **list, u_int size)
return (out);
}
+/* Complete word menu callback. */
+static void
+status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key,
+ void *data)
+{
+ struct status_prompt_menu *spm = data;
+ struct client *c = spm->c;
+ u_int i;
+ char *s;
+
+ if (key != KEYC_NONE) {
+ idx += spm->start;
+ if (spm->flag == '\0')
+ s = xstrdup(spm->list[idx]);
+ else
+ xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]);
+ if (c->prompt_flags & PROMPT_WINDOW) {
+ free(c->prompt_buffer);
+ c->prompt_buffer = utf8_fromcstr(s);
+ c->prompt_index = utf8_strlen(c->prompt_buffer);
+ c->flags |= CLIENT_REDRAWSTATUS;
+ } else if (status_prompt_replace_complete(c, s))
+ c->flags |= CLIENT_REDRAWSTATUS;
+ free(s);
+ }
+
+ for (i = 0; i < spm->size; i++)
+ free(spm->list[i]);
+ free(spm->list);
+}
+
+/* Show complete word menu. */
+static int
+status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
+ u_int offset, char flag)
+{
+ struct menu *menu;
+ struct menu_item item;
+ struct status_prompt_menu *spm;
+ u_int lines = status_line_size(c), height, i;
+ u_int py;
+
+ if (size <= 1)
+ return (0);
+ if (c->tty.sy - lines < 3)
+ return (0);
+
+ spm = xmalloc(sizeof *spm);
+ spm->c = c;
+ spm->size = size;
+ spm->list = list;
+ spm->flag = flag;
+
+ height = c->tty.sy - lines - 2;
+ if (height > 10)
+ height = 10;
+ if (height > size)
+ height = size;
+ spm->start = size - height;
+
+ menu = menu_create("");
+ for (i = spm->start; i < size; i++) {
+ item.name = list[i];
+ item.key = '0' + (i - spm->start);
+ item.command = NULL;
+ menu_add_item(menu, &item, NULL, NULL, NULL);
+ }
+
+ if (options_get_number(c->session->options, "status-position") == 0)
+ py = lines;
+ else
+ py = c->tty.sy - 3 - height;
+ offset += utf8_cstrwidth(c->prompt_string);
+ if (offset > 2)
+ offset -= 2;
+ else
+ offset = 0;
+
+ if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
+ py, c, NULL, status_prompt_menu_callback, spm) != 0) {
+ menu_free(menu);
+ free(spm);
+ return (0);
+ }
+ return (1);
+}
+
+/* Show complete word menu. */
+static char *
+status_prompt_complete_window_menu(struct client *c, struct session *s,
+ const char *word, u_int offset, char flag)
+{
+ struct menu *menu;
+ struct menu_item item;
+ struct status_prompt_menu *spm;
+ struct winlink *wl;
+ char **list = NULL, *tmp;
+ u_int lines = status_line_size(c), height;
+ u_int py, size = 0;
+
+ if (c->tty.sy - lines < 3)
+ return (NULL);
+
+ spm = xmalloc(sizeof *spm);
+ spm->c = c;
+ spm->flag = flag;
+
+ height = c->tty.sy - lines - 2;
+ if (height > 10)
+ height = 10;
+ spm->start = 0;
+
+ menu = menu_create("");
+ RB_FOREACH(wl, winlinks, &s->windows) {
+ if (word != NULL && *word != '\0') {
+ xasprintf(&tmp, "%d", wl->idx);
+ if (strncmp(tmp, word, strlen(word)) != 0) {
+ free(tmp);
+ continue;
+ }
+ free(tmp);
+ }
+
+ list = xreallocarray(list, size + 1, sizeof *list);
+ if (c->prompt_flags & PROMPT_WINDOW) {
+ xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name);
+ xasprintf(&list[size++], "%d", wl->idx);
+ } else {
+ xasprintf(&tmp, "%s:%d (%s)", s->name, wl->idx,
+ wl->window->name);
+ xasprintf(&list[size++], "%s:%d", s->name, wl->idx);
+ }
+ item.name = tmp;
+ item.key = '0' + size - 1;
+ item.command = NULL;
+ menu_add_item(menu, &item, NULL, NULL, NULL);
+ free(tmp);
+
+ if (size == height)
+ break;
+ }
+ if (size == 0) {
+ menu_free(menu);
+ return (NULL);
+ }
+ if (size == 1) {
+ menu_free(menu);
+ if (flag != '\0') {
+ xasprintf(&tmp, "-%c%s", flag, list[0]);
+ free(list[0]);
+ } else
+ tmp = list[0];
+ free(list);
+ return (tmp);
+ }
+ if (height > size)
+ height = size;
+
+ spm->size = size;
+ spm->list = list;
+
+ if (options_get_number(c->session->options, "status-position") == 0)
+ py = lines;
+ else
+ py = c->tty.sy - 3 - height;
+ offset += utf8_cstrwidth(c->prompt_string);
+ if (offset > 2)
+ offset -= 2;
+ else
+ offset = 0;
+
+ if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
+ py, c, NULL, status_prompt_menu_callback, spm) != 0) {
+ menu_free(menu);
+ free(spm);
+ return (NULL);
+ }
+ return (NULL);
+}
+
+/* Sort complete list. */
+static int
+status_prompt_complete_sort(const void *a, const void *b)
+{
+ const char **aa = (const char **)a, **bb = (const char **)b;
+
+ return (strcmp(*aa, *bb));
+}
+
+/* Complete a session. */
+static char *
+status_prompt_complete_session(char ***list, u_int *size, const char *s,
+ char flag)
+{
+ struct session *loop;
+ char *out, *tmp;
+
+ RB_FOREACH(loop, sessions, &sessions) {
+ if (*s != '\0' && strncmp(loop->name, s, strlen(s)) != 0)
+ continue;
+ *list = xreallocarray(*list, (*size) + 2, sizeof **list);
+ xasprintf(&(*list)[(*size)++], "%s:", loop->name);
+ }
+ out = status_prompt_complete_prefix(*list, *size);
+ if (out != NULL && flag != '\0') {
+ xasprintf(&tmp, "-%c%s", flag, out);
+ free(out);
+ out = tmp;
+ }
+ return (out);
+}
+
/* Complete word. */
static char *
-status_prompt_complete(struct session *session, const char *s)
+status_prompt_complete(struct client *c, const char *word, u_int offset)
{
- char **list = NULL;
- const char *colon;
+ struct session *session;
+ const char *s, *colon;
+ char **list = NULL, *copy = NULL, *out = NULL;
+ char flag = '\0';
u_int size = 0, i;
- struct session *s_loop;
- struct winlink *wl;
- struct window *w;
- char *copy, *out, *tmp;
- if (*s == '\0')
+ if (*word == '\0' &&
+ ((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0))
return (NULL);
- out = NULL;
- if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) {
- list = status_prompt_complete_list(&size, s);
+ if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) &&
+ strncmp(word, "-t", 2) != 0 &&
+ strncmp(word, "-s", 2) != 0) {
+ list = status_prompt_complete_list(&size, word, offset == 0);
if (size == 0)
out = NULL;
else if (size == 1)
xasprintf(&out, "%s ", list[0]);
else
out = status_prompt_complete_prefix(list, size);
- for (i = 0; i < size; i++)
- free(list[i]);
- free(list);
- return (out);
+ goto found;
}
- copy = xstrdup(s);
-
- colon = ":";
- if (copy[strlen(copy) - 1] == ':')
- copy[strlen(copy) - 1] = '\0';
- else
- colon = "";
- s = copy + 2;
- RB_FOREACH(s_loop, sessions, &sessions) {
- if (strncmp(s_loop->name, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 2, sizeof *list);
- list[size++] = s_loop->name;
- }
+ if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) {
+ s = word;
+ flag = '\0';
+ } else {
+ s = word + 2;
+ flag = word[1];
+ offset += 2;
}
- if (size == 1) {
- out = xstrdup(list[0]);
- if (session_find(list[0]) != NULL)
- colon = ":";
- } else if (size != 0)
- out = status_prompt_complete_prefix(list, size);
- if (out != NULL) {
- xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
- free(out);
- out = tmp;
+
+ /* If this is a window completion, open the window menu. */
+ if (c->prompt_flags & PROMPT_WINDOW) {
+ out = status_prompt_complete_window_menu(c, c->session, s,
+ offset, '\0');
goto found;
}
+ colon = strchr(s, ':');
- colon = "";
- if (*s == ':') {
- RB_FOREACH(wl, winlinks, &session->windows) {
- xasprintf(&tmp, ":%s", wl->window->name);
- if (strncmp(tmp, s, strlen(s)) == 0){
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
-
- xasprintf(&tmp, ":%d", wl->idx);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- }
- } else {
- RB_FOREACH(s_loop, sessions, &sessions) {
- RB_FOREACH(wl, winlinks, &s_loop->windows) {
- w = wl->window;
-
- xasprintf(&tmp, "%s:%s", s_loop->name, w->name);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
+ /* If there is no colon, complete as a session. */
+ if (colon == NULL) {
+ out = status_prompt_complete_session(&list, &size, s, flag);
+ goto found;
+ }
- xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx);
- if (strncmp(tmp, s, strlen(s)) == 0) {
- list = xreallocarray(list, size + 1,
- sizeof *list);
- list[size++] = tmp;
- continue;
- }
- free(tmp);
- }
+ /* If there is a colon but no period, find session and show a menu. */
+ if (strchr(colon + 1, '.') == NULL) {
+ if (*s == ':')
+ session = c->session;
+ else {
+ copy = xstrdup(s);
+ *strchr(copy, ':') = '\0';
+ session = session_find(copy);
+ free(copy);
+ if (session == NULL)
+ goto found;
}
+ out = status_prompt_complete_window_menu(c, session, colon + 1,
+ offset, flag);
+ if (out == NULL)
+ return (NULL);
}
- if (size == 1) {
- out = xstrdup(list[0]);
- colon = " ";
- } else if (size != 0)
- out = status_prompt_complete_prefix(list, size);
- if (out != NULL) {
- xasprintf(&tmp, "-%c%s%s", copy[1], out, colon);
- out = tmp;
- }
-
- for (i = 0; i < size; i++)
- free((void *)list[i]);
found:
- free(copy);
- free(list);
+ if (size != 0) {
+ qsort(list, size, sizeof *list, status_prompt_complete_sort);
+ for (i = 0; i < size; i++)
+ log_debug("complete %u: %s", i, list[i]);
+ }
+
+ if (out != NULL && strcmp(word, out) == 0) {
+ free(out);
+ out = NULL;
+ }
+ if (out != NULL ||
+ !status_prompt_complete_list_menu(c, list, size, offset, flag)) {
+ for (i = 0; i < size; i++)
+ free(list[i]);
+ free(list);
+ }
return (out);
}