mirror of
https://github.com/tmux/tmux.git
synced 2025-09-02 13:37:12 +00:00
Support UTF-8 entry into the command prompt.
This commit is contained in:
316
status.c
316
status.c
@ -662,18 +662,21 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
|
|||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
int keys;
|
int keys;
|
||||||
time_t t;
|
time_t t;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
ft = format_create(NULL, 0);
|
ft = format_create(NULL, 0);
|
||||||
format_defaults(ft, c, NULL, NULL, NULL);
|
format_defaults(ft, c, NULL, NULL, NULL);
|
||||||
|
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
|
tmp = format_expand_time(ft, input, t);
|
||||||
|
|
||||||
status_message_clear(c);
|
status_message_clear(c);
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
|
|
||||||
c->prompt_string = format_expand_time(ft, msg, t);
|
c->prompt_string = format_expand_time(ft, msg, t);
|
||||||
|
|
||||||
c->prompt_buffer = format_expand_time(ft, input, t);
|
c->prompt_buffer = utf8_fromcstr(tmp);
|
||||||
c->prompt_index = strlen(c->prompt_buffer);
|
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||||
|
|
||||||
c->prompt_callbackfn = callbackfn;
|
c->prompt_callbackfn = callbackfn;
|
||||||
c->prompt_freefn = freefn;
|
c->prompt_freefn = freefn;
|
||||||
@ -692,6 +695,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input,
|
|||||||
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
|
c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,22 +727,26 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
|
|||||||
{
|
{
|
||||||
struct format_tree *ft;
|
struct format_tree *ft;
|
||||||
time_t t;
|
time_t t;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
ft = format_create(NULL, 0);
|
ft = format_create(NULL, 0);
|
||||||
format_defaults(ft, c, NULL, NULL, NULL);
|
format_defaults(ft, c, NULL, NULL, NULL);
|
||||||
|
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
|
tmp = format_expand_time(ft, input, t);
|
||||||
|
|
||||||
free(c->prompt_string);
|
free(c->prompt_string);
|
||||||
c->prompt_string = format_expand_time(ft, msg, t);
|
c->prompt_string = format_expand_time(ft, msg, t);
|
||||||
|
|
||||||
free(c->prompt_buffer);
|
free(c->prompt_buffer);
|
||||||
c->prompt_buffer = format_expand_time(ft, input, t);
|
c->prompt_buffer = utf8_fromcstr(tmp);
|
||||||
c->prompt_index = strlen(c->prompt_buffer);
|
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||||
|
|
||||||
c->prompt_hindex = 0;
|
c->prompt_hindex = 0;
|
||||||
|
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
format_free(ft);
|
format_free(ft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -746,57 +754,80 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
|
|||||||
int
|
int
|
||||||
status_prompt_redraw(struct client *c)
|
status_prompt_redraw(struct client *c)
|
||||||
{
|
{
|
||||||
struct screen_write_ctx ctx;
|
struct screen_write_ctx ctx;
|
||||||
struct session *s = c->session;
|
struct session *s = c->session;
|
||||||
struct screen old_status;
|
struct screen old_status;
|
||||||
size_t i, size, left, len, off;
|
u_int i, offset, left, start, pcursor, pwidth, width;
|
||||||
struct grid_cell gc;
|
struct grid_cell gc, cursorgc;
|
||||||
|
|
||||||
if (c->tty.sx == 0 || c->tty.sy == 0)
|
if (c->tty.sx == 0 || c->tty.sy == 0)
|
||||||
return (0);
|
return (0);
|
||||||
memcpy(&old_status, &c->status, sizeof old_status);
|
memcpy(&old_status, &c->status, sizeof old_status);
|
||||||
screen_init(&c->status, c->tty.sx, 1, 0);
|
screen_init(&c->status, c->tty.sx, 1, 0);
|
||||||
|
|
||||||
len = screen_write_strlen("%s", c->prompt_string);
|
|
||||||
if (len > c->tty.sx)
|
|
||||||
len = c->tty.sx;
|
|
||||||
off = 0;
|
|
||||||
|
|
||||||
/* Change colours for command mode. */
|
|
||||||
if (c->prompt_mdata.mode == 1)
|
if (c->prompt_mdata.mode == 1)
|
||||||
style_apply(&gc, s->options, "message-command-style");
|
style_apply(&gc, s->options, "message-command-style");
|
||||||
else
|
else
|
||||||
style_apply(&gc, s->options, "message-style");
|
style_apply(&gc, s->options, "message-style");
|
||||||
|
|
||||||
|
memcpy(&cursorgc, &gc, sizeof cursorgc);
|
||||||
|
cursorgc.attr ^= GRID_ATTR_REVERSE;
|
||||||
|
|
||||||
|
start = screen_write_strlen("%s", c->prompt_string);
|
||||||
|
if (start > c->tty.sx)
|
||||||
|
start = c->tty.sx;
|
||||||
|
|
||||||
screen_write_start(&ctx, NULL, &c->status);
|
screen_write_start(&ctx, NULL, &c->status);
|
||||||
|
|
||||||
screen_write_cursormove(&ctx, 0, 0);
|
screen_write_cursormove(&ctx, 0, 0);
|
||||||
screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string);
|
screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string);
|
||||||
|
while (c->status.cx < screen_size_x(&c->status))
|
||||||
|
screen_write_putc(&ctx, &gc, ' ');
|
||||||
|
screen_write_cursormove(&ctx, start, 0);
|
||||||
|
|
||||||
left = c->tty.sx - len;
|
left = c->tty.sx - start;
|
||||||
if (left != 0) {
|
if (left == 0)
|
||||||
size = screen_write_strlen("%s", c->prompt_buffer);
|
goto finished;
|
||||||
if (c->prompt_index >= left) {
|
|
||||||
off = c->prompt_index - left + 1;
|
pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index);
|
||||||
if (c->prompt_index == size)
|
pwidth = utf8_strwidth(c->prompt_buffer, -1);
|
||||||
left--;
|
if (pcursor >= left) {
|
||||||
size = left;
|
/*
|
||||||
|
* The cursor would be outside the screen so start drawing
|
||||||
|
* with it on the right.
|
||||||
|
*/
|
||||||
|
offset = (pcursor - left) + 1;
|
||||||
|
pwidth = left;
|
||||||
|
} else
|
||||||
|
offset = 0;
|
||||||
|
if (pwidth > left)
|
||||||
|
pwidth = left;
|
||||||
|
|
||||||
|
width = 0;
|
||||||
|
for (i = 0; c->prompt_buffer[i].size != 0; i++) {
|
||||||
|
if (width < offset) {
|
||||||
|
width += c->prompt_buffer[i].width;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer +
|
if (width >= offset + pwidth)
|
||||||
off);
|
break;
|
||||||
|
width += c->prompt_buffer[i].width;
|
||||||
|
if (width > offset + pwidth)
|
||||||
|
break;
|
||||||
|
|
||||||
for (i = len + size; i < c->tty.sx; i++)
|
if (i != c->prompt_index) {
|
||||||
screen_write_putc(&ctx, &gc, ' ');
|
utf8_copy(&gc.data, &c->prompt_buffer[i]);
|
||||||
|
screen_write_cell(&ctx, &gc);
|
||||||
|
} else {
|
||||||
|
utf8_copy(&cursorgc.data, &c->prompt_buffer[i]);
|
||||||
|
screen_write_cell(&ctx, &cursorgc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (c->status.cx < screen_size_x(&c->status) && c->prompt_index >= i)
|
||||||
|
screen_write_putc(&ctx, &cursorgc, ' ');
|
||||||
|
|
||||||
|
finished:
|
||||||
screen_write_stop(&ctx);
|
screen_write_stop(&ctx);
|
||||||
|
|
||||||
/* Apply fake cursor. */
|
|
||||||
off = len + c->prompt_index - off;
|
|
||||||
grid_view_get_cell(c->status.grid, off, 0, &gc);
|
|
||||||
gc.attr ^= GRID_ATTR_REVERSE;
|
|
||||||
grid_view_set_cell(c->status.grid, off, 0, &gc);
|
|
||||||
|
|
||||||
if (grid_compare(c->status.grid, old_status.grid) == 0) {
|
if (grid_compare(c->status.grid, old_status.grid) == 0) {
|
||||||
screen_free(&old_status);
|
screen_free(&old_status);
|
||||||
return (0);
|
return (0);
|
||||||
@ -805,19 +836,37 @@ status_prompt_redraw(struct client *c)
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is this a separator? */
|
||||||
|
static int
|
||||||
|
status_prompt_in_list(const char *ws, const struct utf8_data *ud)
|
||||||
|
{
|
||||||
|
if (ud->size != 1 || ud->width != 1)
|
||||||
|
return (0);
|
||||||
|
return (strchr(ws, *ud->data) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this a space? */
|
||||||
|
static int
|
||||||
|
status_prompt_space(const struct utf8_data *ud)
|
||||||
|
{
|
||||||
|
if (ud->size != 1 || ud->width != 1)
|
||||||
|
return (0);
|
||||||
|
return (*ud->data == ' ');
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle keys in prompt. */
|
/* Handle keys in prompt. */
|
||||||
void
|
void
|
||||||
status_prompt_key(struct client *c, key_code key)
|
status_prompt_key(struct client *c, key_code key)
|
||||||
{
|
{
|
||||||
struct session *sess = c->session;
|
struct options *oo = c->session->options;
|
||||||
struct options *oo = sess->options;
|
|
||||||
struct paste_buffer *pb;
|
struct paste_buffer *pb;
|
||||||
char *s, *first, *last, word[64], swapc;
|
char *s, word[64];
|
||||||
const char *histstr, *bufdata, *wsep = NULL;
|
const char *histstr, *bufdata, *ws = NULL;
|
||||||
u_char ch;
|
u_char ch;
|
||||||
size_t size, n, off, idx, bufsize;
|
size_t size, n, off, idx, bufsize, used;
|
||||||
|
struct utf8_data tmp, *first, *last, *ud;
|
||||||
|
|
||||||
size = strlen(c->prompt_buffer);
|
size = utf8_strlen(c->prompt_buffer);
|
||||||
switch (mode_key_lookup(&c->prompt_mdata, key, NULL, NULL)) {
|
switch (mode_key_lookup(&c->prompt_mdata, key, NULL, NULL)) {
|
||||||
case MODEKEYEDIT_CURSORLEFT:
|
case MODEKEYEDIT_CURSORLEFT:
|
||||||
if (c->prompt_index > 0) {
|
if (c->prompt_index > 0) {
|
||||||
@ -856,7 +905,7 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_COMPLETE:
|
case MODEKEYEDIT_COMPLETE:
|
||||||
if (*c->prompt_buffer == '\0')
|
if (c->prompt_buffer[0].size == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
idx = c->prompt_index;
|
idx = c->prompt_index;
|
||||||
@ -864,40 +913,50 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
idx--;
|
idx--;
|
||||||
|
|
||||||
/* Find the word we are in. */
|
/* Find the word we are in. */
|
||||||
first = c->prompt_buffer + idx;
|
first = &c->prompt_buffer[idx];
|
||||||
while (first > c->prompt_buffer && *first != ' ')
|
while (first > c->prompt_buffer && !status_prompt_space(first))
|
||||||
first--;
|
first--;
|
||||||
while (*first == ' ')
|
while (first->size != 0 && status_prompt_space(first))
|
||||||
first++;
|
first++;
|
||||||
last = c->prompt_buffer + idx;
|
last = &c->prompt_buffer[idx];
|
||||||
while (*last != '\0' && *last != ' ')
|
while (last->size != 0 && !status_prompt_space(last))
|
||||||
last++;
|
last++;
|
||||||
while (*last == ' ')
|
while (last > c->prompt_buffer && status_prompt_space(last))
|
||||||
last--;
|
last--;
|
||||||
if (*last != '\0')
|
if (last->size != 0)
|
||||||
last++;
|
last++;
|
||||||
if (last <= first ||
|
if (last <= first)
|
||||||
((size_t) (last - first)) > (sizeof word) - 1)
|
|
||||||
break;
|
break;
|
||||||
memcpy(word, first, last - first);
|
|
||||||
word[last - first] = '\0';
|
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. */
|
/* And try to complete it. */
|
||||||
if ((s = status_prompt_complete(sess, word)) == NULL)
|
if ((s = status_prompt_complete(c->session, word)) == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Trim out word. */
|
/* Trim out word. */
|
||||||
n = size - (last - c->prompt_buffer) + 1; /* with \0 */
|
n = size - (last - c->prompt_buffer) + 1; /* with \0 */
|
||||||
memmove(first, last, n);
|
memmove(first, last, n * sizeof *c->prompt_buffer);
|
||||||
size -= last - first;
|
size -= last - first;
|
||||||
|
|
||||||
/* Insert the new word. */
|
/* Insert the new word. */
|
||||||
size += strlen(s);
|
size += strlen(s);
|
||||||
off = first - c->prompt_buffer;
|
off = first - c->prompt_buffer;
|
||||||
c->prompt_buffer = xrealloc(c->prompt_buffer, size + 1);
|
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1,
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
first = c->prompt_buffer + off;
|
first = c->prompt_buffer + off;
|
||||||
memmove(first + strlen(s), first, n);
|
memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer);
|
||||||
memcpy(first, s, strlen(s));
|
for (idx = 0; idx < strlen(s); idx++)
|
||||||
|
utf8_set(&first[idx], s[idx]);
|
||||||
|
|
||||||
c->prompt_index = (first - c->prompt_buffer) + strlen(s);
|
c->prompt_index = (first - c->prompt_buffer) + strlen(s);
|
||||||
free(s);
|
free(s);
|
||||||
@ -907,11 +966,12 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
case MODEKEYEDIT_BACKSPACE:
|
case MODEKEYEDIT_BACKSPACE:
|
||||||
if (c->prompt_index != 0) {
|
if (c->prompt_index != 0) {
|
||||||
if (c->prompt_index == size)
|
if (c->prompt_index == size)
|
||||||
c->prompt_buffer[--c->prompt_index] = '\0';
|
c->prompt_buffer[--c->prompt_index].size = 0;
|
||||||
else {
|
else {
|
||||||
memmove(c->prompt_buffer + c->prompt_index - 1,
|
memmove(c->prompt_buffer + c->prompt_index - 1,
|
||||||
c->prompt_buffer + c->prompt_index,
|
c->prompt_buffer + c->prompt_index,
|
||||||
size + 1 - c->prompt_index);
|
(size + 1 - c->prompt_index) *
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
c->prompt_index--;
|
c->prompt_index--;
|
||||||
}
|
}
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
@ -922,38 +982,39 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
if (c->prompt_index != size) {
|
if (c->prompt_index != size) {
|
||||||
memmove(c->prompt_buffer + c->prompt_index,
|
memmove(c->prompt_buffer + c->prompt_index,
|
||||||
c->prompt_buffer + c->prompt_index + 1,
|
c->prompt_buffer + c->prompt_index + 1,
|
||||||
size + 1 - c->prompt_index);
|
(size + 1 - c->prompt_index) *
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_DELETELINE:
|
case MODEKEYEDIT_DELETELINE:
|
||||||
case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
|
case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE:
|
||||||
*c->prompt_buffer = '\0';
|
c->prompt_buffer[0].size = 0;
|
||||||
c->prompt_index = 0;
|
c->prompt_index = 0;
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_DELETETOENDOFLINE:
|
case MODEKEYEDIT_DELETETOENDOFLINE:
|
||||||
case MODEKEYEDIT_SWITCHMODECHANGELINE:
|
case MODEKEYEDIT_SWITCHMODECHANGELINE:
|
||||||
if (c->prompt_index < size) {
|
if (c->prompt_index < size) {
|
||||||
c->prompt_buffer[c->prompt_index] = '\0';
|
c->prompt_buffer[c->prompt_index].size = 0;
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_DELETEWORD:
|
case MODEKEYEDIT_DELETEWORD:
|
||||||
wsep = options_get_string(oo, "word-separators");
|
ws = options_get_string(oo, "word-separators");
|
||||||
idx = c->prompt_index;
|
idx = c->prompt_index;
|
||||||
|
|
||||||
/* Find a non-separator. */
|
/* Find a non-separator. */
|
||||||
while (idx != 0) {
|
while (idx != 0) {
|
||||||
idx--;
|
idx--;
|
||||||
if (!strchr(wsep, c->prompt_buffer[idx]))
|
if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the separator at the beginning of the word. */
|
/* Find the separator at the beginning of the word. */
|
||||||
while (idx != 0) {
|
while (idx != 0) {
|
||||||
idx--;
|
idx--;
|
||||||
if (strchr(wsep, c->prompt_buffer[idx])) {
|
if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
|
||||||
/* Go back to the word. */
|
/* Go back to the word. */
|
||||||
idx++;
|
idx++;
|
||||||
break;
|
break;
|
||||||
@ -962,53 +1023,55 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
|
|
||||||
memmove(c->prompt_buffer + idx,
|
memmove(c->prompt_buffer + idx,
|
||||||
c->prompt_buffer + c->prompt_index,
|
c->prompt_buffer + c->prompt_index,
|
||||||
size + 1 - c->prompt_index);
|
(size + 1 - c->prompt_index) *
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
memset(c->prompt_buffer + size - (c->prompt_index - idx),
|
memset(c->prompt_buffer + size - (c->prompt_index - idx),
|
||||||
'\0', c->prompt_index - idx);
|
'\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer);
|
||||||
c->prompt_index = idx;
|
c->prompt_index = idx;
|
||||||
|
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_NEXTSPACE:
|
case MODEKEYEDIT_NEXTSPACE:
|
||||||
wsep = " ";
|
ws = " ";
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case MODEKEYEDIT_NEXTWORD:
|
case MODEKEYEDIT_NEXTWORD:
|
||||||
if (wsep == NULL)
|
if (ws == NULL)
|
||||||
wsep = options_get_string(oo, "word-separators");
|
ws = options_get_string(oo, "word-separators");
|
||||||
|
|
||||||
/* Find a separator. */
|
/* Find a separator. */
|
||||||
while (c->prompt_index != size) {
|
while (c->prompt_index != size) {
|
||||||
c->prompt_index++;
|
idx = ++c->prompt_index;
|
||||||
if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
|
if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the word right after the separation. */
|
/* Find the word right after the separator. */
|
||||||
while (c->prompt_index != size) {
|
while (c->prompt_index != size) {
|
||||||
c->prompt_index++;
|
idx = ++c->prompt_index;
|
||||||
if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
|
if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_NEXTSPACEEND:
|
case MODEKEYEDIT_NEXTSPACEEND:
|
||||||
wsep = " ";
|
ws = " ";
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case MODEKEYEDIT_NEXTWORDEND:
|
case MODEKEYEDIT_NEXTWORDEND:
|
||||||
if (wsep == NULL)
|
if (ws == NULL)
|
||||||
wsep = options_get_string(oo, "word-separators");
|
ws = options_get_string(oo, "word-separators");
|
||||||
|
|
||||||
/* Find a word. */
|
/* Find a word. */
|
||||||
while (c->prompt_index != size) {
|
while (c->prompt_index != size) {
|
||||||
c->prompt_index++;
|
idx = ++c->prompt_index;
|
||||||
if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
|
if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the separator at the end of the word. */
|
/* Find the separator at the end of the word. */
|
||||||
while (c->prompt_index != size) {
|
while (c->prompt_index != size) {
|
||||||
c->prompt_index++;
|
idx = ++c->prompt_index;
|
||||||
if (strchr(wsep, c->prompt_buffer[c->prompt_index]))
|
if (status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,23 +1083,23 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_PREVIOUSSPACE:
|
case MODEKEYEDIT_PREVIOUSSPACE:
|
||||||
wsep = " ";
|
ws = " ";
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case MODEKEYEDIT_PREVIOUSWORD:
|
case MODEKEYEDIT_PREVIOUSWORD:
|
||||||
if (wsep == NULL)
|
if (ws == NULL)
|
||||||
wsep = options_get_string(oo, "word-separators");
|
ws = options_get_string(oo, "word-separators");
|
||||||
|
|
||||||
/* Find a non-separator. */
|
/* Find a non-separator. */
|
||||||
while (c->prompt_index != 0) {
|
while (c->prompt_index != 0) {
|
||||||
c->prompt_index--;
|
idx = --c->prompt_index;
|
||||||
if (!strchr(wsep, c->prompt_buffer[c->prompt_index]))
|
if (!status_prompt_in_list(ws, &c->prompt_buffer[idx]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the separator at the beginning of the word. */
|
/* Find the separator at the beginning of the word. */
|
||||||
while (c->prompt_index != 0) {
|
while (c->prompt_index != 0) {
|
||||||
c->prompt_index--;
|
idx = --c->prompt_index;
|
||||||
if (strchr(wsep, c->prompt_buffer[c->prompt_index])) {
|
if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) {
|
||||||
/* Go back to the word. */
|
/* Go back to the word. */
|
||||||
c->prompt_index++;
|
c->prompt_index++;
|
||||||
break;
|
break;
|
||||||
@ -1050,8 +1113,8 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
if (histstr == NULL)
|
if (histstr == NULL)
|
||||||
break;
|
break;
|
||||||
free(c->prompt_buffer);
|
free(c->prompt_buffer);
|
||||||
c->prompt_buffer = xstrdup(histstr);
|
c->prompt_buffer = utf8_fromcstr(histstr);
|
||||||
c->prompt_index = strlen(c->prompt_buffer);
|
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_HISTORYDOWN:
|
case MODEKEYEDIT_HISTORYDOWN:
|
||||||
@ -1059,8 +1122,8 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
if (histstr == NULL)
|
if (histstr == NULL)
|
||||||
break;
|
break;
|
||||||
free(c->prompt_buffer);
|
free(c->prompt_buffer);
|
||||||
c->prompt_buffer = xstrdup(histstr);
|
c->prompt_buffer = utf8_fromcstr(histstr);
|
||||||
c->prompt_index = strlen(c->prompt_buffer);
|
c->prompt_index = utf8_strlen(c->prompt_buffer);
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_PASTE:
|
case MODEKEYEDIT_PASTE:
|
||||||
@ -1069,20 +1132,28 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
bufdata = paste_buffer_data(pb, &bufsize);
|
bufdata = paste_buffer_data(pb, &bufsize);
|
||||||
for (n = 0; n < bufsize; n++) {
|
for (n = 0; n < bufsize; n++) {
|
||||||
ch = (u_char)bufdata[n];
|
ch = (u_char)bufdata[n];
|
||||||
if (ch < 32 || ch == 127)
|
if (ch < 32 || ch >= 127)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1);
|
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
if (c->prompt_index == size) {
|
if (c->prompt_index == size) {
|
||||||
memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
|
for (idx = 0; idx < n; idx++) {
|
||||||
|
ud = &c->prompt_buffer[c->prompt_index + idx];
|
||||||
|
utf8_set(ud, bufdata[idx]);
|
||||||
|
}
|
||||||
c->prompt_index += n;
|
c->prompt_index += n;
|
||||||
c->prompt_buffer[c->prompt_index] = '\0';
|
c->prompt_buffer[c->prompt_index].size = 0;
|
||||||
} else {
|
} else {
|
||||||
memmove(c->prompt_buffer + c->prompt_index + n,
|
memmove(c->prompt_buffer + c->prompt_index + n,
|
||||||
c->prompt_buffer + c->prompt_index,
|
c->prompt_buffer + c->prompt_index,
|
||||||
size + 1 - c->prompt_index);
|
(size + 1 - c->prompt_index) *
|
||||||
memcpy(c->prompt_buffer + c->prompt_index, bufdata, n);
|
sizeof *c->prompt_buffer);
|
||||||
|
for (idx = 0; idx < n; idx++) {
|
||||||
|
ud = &c->prompt_buffer[c->prompt_index + idx];
|
||||||
|
utf8_set(ud, bufdata[idx]);
|
||||||
|
}
|
||||||
c->prompt_index += n;
|
c->prompt_index += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,42 +1164,55 @@ status_prompt_key(struct client *c, key_code key)
|
|||||||
if (idx < size)
|
if (idx < size)
|
||||||
idx++;
|
idx++;
|
||||||
if (idx >= 2) {
|
if (idx >= 2) {
|
||||||
swapc = c->prompt_buffer[idx - 2];
|
utf8_copy(&tmp, &c->prompt_buffer[idx - 2]);
|
||||||
c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1];
|
utf8_copy(&c->prompt_buffer[idx - 2],
|
||||||
c->prompt_buffer[idx - 1] = swapc;
|
&c->prompt_buffer[idx - 1]);
|
||||||
|
utf8_copy(&c->prompt_buffer[idx - 1], &tmp);
|
||||||
c->prompt_index = idx;
|
c->prompt_index = idx;
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_ENTER:
|
case MODEKEYEDIT_ENTER:
|
||||||
if (*c->prompt_buffer != '\0')
|
s = utf8_tocstr(c->prompt_buffer);
|
||||||
status_prompt_add_history(c->prompt_buffer);
|
if (*s != '\0')
|
||||||
if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
|
status_prompt_add_history(s);
|
||||||
|
if (c->prompt_callbackfn(c->prompt_data, s) == 0)
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
|
free(s);
|
||||||
break;
|
break;
|
||||||
case MODEKEYEDIT_CANCEL:
|
case MODEKEYEDIT_CANCEL:
|
||||||
if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
|
if (c->prompt_callbackfn(c->prompt_data, NULL) == 0)
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
break;
|
break;
|
||||||
case MODEKEY_OTHER:
|
case MODEKEY_OTHER:
|
||||||
if (key <= 0x1f || key >= 0x7f)
|
if (key <= 0x1f || key >= KEYC_BASE)
|
||||||
break;
|
break;
|
||||||
c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2);
|
if (utf8_split(key, &tmp) != UTF8_DONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2,
|
||||||
|
sizeof *c->prompt_buffer);
|
||||||
|
|
||||||
if (c->prompt_index == size) {
|
if (c->prompt_index == size) {
|
||||||
c->prompt_buffer[c->prompt_index++] = key;
|
utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
|
||||||
c->prompt_buffer[c->prompt_index] = '\0';
|
c->prompt_index++;
|
||||||
|
c->prompt_buffer[c->prompt_index].size = 0;
|
||||||
} else {
|
} else {
|
||||||
memmove(c->prompt_buffer + c->prompt_index + 1,
|
memmove(c->prompt_buffer + c->prompt_index + 1,
|
||||||
c->prompt_buffer + c->prompt_index,
|
c->prompt_buffer + c->prompt_index,
|
||||||
size + 1 - c->prompt_index);
|
(size + 1 - c->prompt_index) *
|
||||||
c->prompt_buffer[c->prompt_index++] = key;
|
sizeof *c->prompt_buffer);
|
||||||
|
utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp);
|
||||||
|
c->prompt_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->prompt_flags & PROMPT_SINGLE) {
|
if (c->prompt_flags & PROMPT_SINGLE) {
|
||||||
if (c->prompt_callbackfn(c->prompt_data,
|
s = utf8_tocstr(c->prompt_buffer);
|
||||||
c->prompt_buffer) == 0)
|
if (strlen(s) != 1)
|
||||||
status_prompt_clear(c);
|
status_prompt_clear(c);
|
||||||
|
else if (c->prompt_callbackfn(c->prompt_data, s) == 0)
|
||||||
|
status_prompt_clear(c);
|
||||||
|
free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
c->flags |= CLIENT_STATUS;
|
c->flags |= CLIENT_STATUS;
|
||||||
@ -1247,7 +1331,7 @@ status_prompt_complete_prefix(const char **list, u_int size)
|
|||||||
|
|
||||||
/* Complete word. */
|
/* Complete word. */
|
||||||
static char *
|
static char *
|
||||||
status_prompt_complete(struct session *sess, const char *s)
|
status_prompt_complete(struct session *session, const char *s)
|
||||||
{
|
{
|
||||||
const char **list = NULL, *colon;
|
const char **list = NULL, *colon;
|
||||||
u_int size = 0, i;
|
u_int size = 0, i;
|
||||||
@ -1300,7 +1384,7 @@ status_prompt_complete(struct session *sess, const char *s)
|
|||||||
|
|
||||||
colon = "";
|
colon = "";
|
||||||
if (*s == ':') {
|
if (*s == ':') {
|
||||||
RB_FOREACH(wl, winlinks, &sess->windows) {
|
RB_FOREACH(wl, winlinks, &session->windows) {
|
||||||
xasprintf(&tmp, ":%s", wl->window->name);
|
xasprintf(&tmp, ":%s", wl->window->name);
|
||||||
if (strncmp(tmp, s, strlen(s)) == 0){
|
if (strncmp(tmp, s, strlen(s)) == 0){
|
||||||
list = xreallocarray(list, size + 1,
|
list = xreallocarray(list, size + 1,
|
||||||
|
4
tmux.h
4
tmux.h
@ -1300,7 +1300,7 @@ struct client {
|
|||||||
TAILQ_HEAD(, message_entry) message_log;
|
TAILQ_HEAD(, message_entry) message_log;
|
||||||
|
|
||||||
char *prompt_string;
|
char *prompt_string;
|
||||||
char *prompt_buffer;
|
struct utf8_data *prompt_buffer;
|
||||||
size_t prompt_index;
|
size_t prompt_index;
|
||||||
int (*prompt_callbackfn)(void *, const char *);
|
int (*prompt_callbackfn)(void *, const char *);
|
||||||
void (*prompt_freefn)(void *);
|
void (*prompt_freefn)(void *);
|
||||||
@ -2345,6 +2345,8 @@ enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *);
|
|||||||
enum utf8_state utf8_split(wchar_t, struct utf8_data *);
|
enum utf8_state utf8_split(wchar_t, struct utf8_data *);
|
||||||
int utf8_strvis(char *, const char *, size_t, int);
|
int utf8_strvis(char *, const char *, size_t, int);
|
||||||
char *utf8_sanitize(const char *);
|
char *utf8_sanitize(const char *);
|
||||||
|
size_t utf8_strlen(const struct utf8_data *);
|
||||||
|
u_int utf8_strwidth(const struct utf8_data *, ssize_t);
|
||||||
struct utf8_data *utf8_fromcstr(const char *);
|
struct utf8_data *utf8_fromcstr(const char *);
|
||||||
char *utf8_tocstr(struct utf8_data *);
|
char *utf8_tocstr(struct utf8_data *);
|
||||||
u_int utf8_cstrwidth(const char *);
|
u_int utf8_cstrwidth(const char *);
|
||||||
|
27
utf8.c
27
utf8.c
@ -236,6 +236,33 @@ utf8_sanitize(const char *src)
|
|||||||
return (dst);
|
return (dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get UTF-8 buffer length. */
|
||||||
|
size_t
|
||||||
|
utf8_strlen(const struct utf8_data *s)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; s[i].size != 0; i++)
|
||||||
|
/* nothing */;
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get UTF-8 string width. */
|
||||||
|
u_int
|
||||||
|
utf8_strwidth(const struct utf8_data *s, ssize_t n)
|
||||||
|
{
|
||||||
|
ssize_t i;
|
||||||
|
u_int width;
|
||||||
|
|
||||||
|
width = 0;
|
||||||
|
for (i = 0; s[i].size != 0; i++) {
|
||||||
|
if (n != -1 && n == i)
|
||||||
|
break;
|
||||||
|
width += s[i].width;
|
||||||
|
}
|
||||||
|
return (width);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert a string into a buffer of UTF-8 characters. Terminated by size == 0.
|
* Convert a string into a buffer of UTF-8 characters. Terminated by size == 0.
|
||||||
* Caller frees.
|
* Caller frees.
|
||||||
|
Reference in New Issue
Block a user