mirror of
https://github.com/tmux/tmux.git
synced 2025-01-05 23:38:48 +00:00
Add support for named buffers. If you don't name a buffer, things work
much as before - buffers are automatically named "buffer0000", "buffer0001" and so on and ordered as a stack. Buffers can be named explicitly when creating ("loadb -b foo" etc) or renamed ("setb -b buffer0000 -n foo"). If buffers are named explicitly, they are not deleted when buffer-limit is reached. Diff from J Raynor.
This commit is contained in:
parent
f4ffaf5a7f
commit
3dbacbb62b
@ -38,7 +38,7 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *,
|
||||
const struct cmd_entry cmd_capture_pane_entry = {
|
||||
"capture-pane", "capturep",
|
||||
"ab:CeE:JpPqS:t:", 0, 0,
|
||||
"[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]"
|
||||
"[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]"
|
||||
CMD_TARGET_PANE_USAGE,
|
||||
0,
|
||||
NULL,
|
||||
@ -165,8 +165,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
struct client *c;
|
||||
struct window_pane *wp;
|
||||
char *buf, *cause;
|
||||
int buffer;
|
||||
u_int limit;
|
||||
const char *bufname;
|
||||
size_t len;
|
||||
|
||||
if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL)
|
||||
@ -192,25 +191,17 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
evbuffer_add(c->stdout_data, "\n", 1);
|
||||
server_push_stdout(c);
|
||||
} else {
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
if (!args_has(args, 'b')) {
|
||||
paste_add(buf, len, limit);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
||||
if (paste_set(buf, len, bufname, &cause) != 0) {
|
||||
cmdq_error(cmdq, "%s", cause);
|
||||
free(buf);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (paste_replace(buffer, buf, len) != 0) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
free(buf);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
action = xstrdup("paste-buffer -b '%%'");
|
||||
|
||||
idx = 0;
|
||||
while ((pb = paste_walk_stack(&idx)) != NULL) {
|
||||
pb = NULL;
|
||||
while ((pb = paste_walk(pb)) != NULL) {
|
||||
cdata = window_choose_data_create(TREE_OTHER, c, c->session);
|
||||
cdata->idx = idx - 1;
|
||||
cdata->idx = idx;
|
||||
|
||||
cdata->ft_template = xstrdup(template);
|
||||
format_add(cdata->ft, "line", "%u", idx - 1);
|
||||
format_paste_buffer(cdata->ft, pb, utf8flag);
|
||||
|
||||
xasprintf(&action_data, "%u", idx - 1);
|
||||
xasprintf(&action_data, "%s", pb->name);
|
||||
cdata->command = cmd_template_replace(action, action_data, 1);
|
||||
free(action_data);
|
||||
|
||||
window_choose_add(wl->window->active, cdata);
|
||||
idx++;
|
||||
}
|
||||
free(action);
|
||||
|
||||
|
@ -41,23 +41,16 @@ enum cmd_retval
|
||||
cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
struct args *args = self->args;
|
||||
char *cause;
|
||||
int buffer;
|
||||
const char *bufname;
|
||||
|
||||
if (!args_has(args, 'b')) {
|
||||
paste_free_top();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
bufname = args_get(args, 'b');
|
||||
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (paste_free_index(buffer) != 0) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
if (paste_free_name(bufname) != 0) {
|
||||
cmdq_error(cmdq, "no buffer %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
|
@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq)
|
||||
struct args *args = self->args;
|
||||
struct paste_buffer *pb;
|
||||
struct format_tree *ft;
|
||||
u_int idx;
|
||||
char *line;
|
||||
const char *template;
|
||||
|
||||
if ((template = args_get(args, 'F')) == NULL)
|
||||
template = LIST_BUFFERS_TEMPLATE;
|
||||
|
||||
idx = 0;
|
||||
while ((pb = paste_walk_stack(&idx)) != NULL) {
|
||||
pb = NULL;
|
||||
while ((pb = paste_walk(pb)) != NULL) {
|
||||
ft = format_create();
|
||||
format_add(ft, "line", "%u", idx - 1);
|
||||
format_paste_buffer(ft, pb, 0);
|
||||
|
||||
line = format_expand(ft, template);
|
||||
|
@ -50,30 +50,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
struct client *c = cmdq->client;
|
||||
struct session *s;
|
||||
FILE *f;
|
||||
const char *path;
|
||||
const char *path, *bufname;
|
||||
char *pdata, *new_pdata, *cause;
|
||||
size_t psize;
|
||||
u_int limit;
|
||||
int ch, error, buffer, *buffer_ptr, cwd, fd;
|
||||
int ch, error, cwd, fd;
|
||||
|
||||
if (!args_has(args, 'b'))
|
||||
buffer = -1;
|
||||
else {
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
||||
path = args->argv[0];
|
||||
if (strcmp(path, "-") == 0) {
|
||||
buffer_ptr = xmalloc(sizeof *buffer_ptr);
|
||||
*buffer_ptr = buffer;
|
||||
|
||||
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
|
||||
buffer_ptr, &cause);
|
||||
(void*)bufname, &cause);
|
||||
if (error != 0) {
|
||||
cmdq_error(cmdq, "%s: %s", path, cause);
|
||||
free(cause);
|
||||
@ -117,14 +106,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
|
||||
fclose(f);
|
||||
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
if (buffer == -1) {
|
||||
paste_add(pdata, psize, limit);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (paste_replace(buffer, pdata, psize) != 0) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
if (paste_set(pdata, psize, bufname, &cause) != 0) {
|
||||
cmdq_error(cmdq, "%s", cause);
|
||||
free(pdata);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
@ -140,10 +125,9 @@ error:
|
||||
void
|
||||
cmd_load_buffer_callback(struct client *c, int closed, void *data)
|
||||
{
|
||||
int *buffer = data;
|
||||
char *pdata;
|
||||
size_t psize;
|
||||
u_int limit;
|
||||
const char *bufname = data;
|
||||
char *pdata, *cause;
|
||||
size_t psize;
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
@ -154,26 +138,21 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data)
|
||||
return;
|
||||
|
||||
psize = EVBUFFER_LENGTH(c->stdin_data);
|
||||
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) {
|
||||
free(data);
|
||||
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
|
||||
pdata[psize] = '\0';
|
||||
evbuffer_drain(c->stdin_data, psize);
|
||||
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
if (*buffer == -1)
|
||||
paste_add(pdata, psize, limit);
|
||||
else if (paste_replace(*buffer, pdata, psize) != 0) {
|
||||
if (paste_set(pdata, psize, bufname, &cause) != 0) {
|
||||
/* No context so can't use server_client_msg_error. */
|
||||
evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer);
|
||||
evbuffer_add_printf(c->stderr_data, "%s", cause);
|
||||
server_push_stderr(c);
|
||||
free(pdata);
|
||||
free(cause);
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
out:
|
||||
cmdq_continue(c->cmdq);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ void cmd_paste_buffer_filter(struct window_pane *,
|
||||
const struct cmd_entry cmd_paste_buffer_entry = {
|
||||
"paste-buffer", "pasteb",
|
||||
"db:prs:t:", 0, 0,
|
||||
"[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE,
|
||||
"[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE,
|
||||
0,
|
||||
NULL,
|
||||
cmd_paste_buffer_exec
|
||||
@ -49,31 +49,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
struct window_pane *wp;
|
||||
struct session *s;
|
||||
struct paste_buffer *pb;
|
||||
const char *sepstr;
|
||||
char *cause;
|
||||
int buffer;
|
||||
const char *sepstr, *bufname;
|
||||
int pflag;
|
||||
|
||||
if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL)
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
if (!args_has(args, 'b'))
|
||||
buffer = -1;
|
||||
else {
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
||||
if (buffer == -1)
|
||||
if (bufname == NULL)
|
||||
pb = paste_get_top();
|
||||
else {
|
||||
pb = paste_get_index(buffer);
|
||||
pb = paste_get_name(bufname);
|
||||
if (pb == NULL) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
cmdq_error(cmdq, "no buffer %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
@ -92,10 +83,10 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
|
||||
/* Delete the buffer if -d. */
|
||||
if (args_has(args, 'd')) {
|
||||
if (buffer == -1)
|
||||
if (bufname == NULL)
|
||||
paste_free_top();
|
||||
else
|
||||
paste_free_index(buffer);
|
||||
paste_free_name(bufname);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
@ -59,10 +59,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
struct client *c = cmdq->client;
|
||||
struct session *s;
|
||||
struct paste_buffer *pb;
|
||||
const char *path;
|
||||
char *cause, *start, *end, *msg;
|
||||
const char *path, *bufname;
|
||||
char *start, *end, *msg;
|
||||
size_t size, used, msglen;
|
||||
int cwd, fd, buffer;
|
||||
int cwd, fd;
|
||||
FILE *f;
|
||||
|
||||
if (!args_has(args, 'b')) {
|
||||
@ -71,16 +71,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else {
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
pb = paste_get_index(buffer);
|
||||
bufname = args_get(args, 'b');
|
||||
pb = paste_get_name(bufname);
|
||||
if (pb == NULL) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
cmdq_error(cmdq, "no buffer %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *);
|
||||
|
||||
const struct cmd_entry cmd_set_buffer_entry = {
|
||||
"set-buffer", "setb",
|
||||
"ab:", 1, 1,
|
||||
"[-a] " CMD_BUFFER_USAGE " data",
|
||||
"ab:n:", 0, 1,
|
||||
"[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data",
|
||||
0,
|
||||
NULL,
|
||||
cmd_set_buffer_exec
|
||||
@ -43,38 +43,59 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
struct args *args = self->args;
|
||||
struct paste_buffer *pb;
|
||||
u_int limit;
|
||||
char *pdata, *cause;
|
||||
const char *bufname;
|
||||
size_t psize, newsize;
|
||||
int buffer;
|
||||
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
bufname = NULL;
|
||||
|
||||
if (args_has(args, 'n')) {
|
||||
if (args->argc > 0) {
|
||||
cmdq_error(cmdq, "don't provide data with n flag");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
||||
if (bufname == NULL) {
|
||||
pb = paste_get_top();
|
||||
if (pb == NULL) {
|
||||
cmdq_error(cmdq, "no buffer");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
bufname = pb->name;
|
||||
}
|
||||
|
||||
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
|
||||
cmdq_error(cmdq, "%s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args->argc != 1) {
|
||||
cmdq_error(cmdq, "no data specified");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
psize = 0;
|
||||
pdata = NULL;
|
||||
|
||||
pb = NULL;
|
||||
buffer = -1;
|
||||
|
||||
if ((newsize = strlen(args->argv[0])) == 0)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (args_has(args, 'b')) {
|
||||
buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(cmdq, "buffer %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
pb = paste_get_index(buffer);
|
||||
if (pb == NULL) {
|
||||
cmdq_error(cmdq, "no buffer %d", buffer);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
bufname = args_get(args, 'b');
|
||||
pb = paste_get_name(bufname);
|
||||
} else if (args_has(args, 'a')) {
|
||||
pb = paste_get_top();
|
||||
if (pb != NULL)
|
||||
buffer = 0;
|
||||
bufname = pb->name;
|
||||
}
|
||||
|
||||
if (args_has(args, 'a') && pb != NULL) {
|
||||
@ -87,10 +108,12 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
memcpy(pdata + psize, args->argv[0], newsize);
|
||||
psize += newsize;
|
||||
|
||||
if (buffer == -1)
|
||||
paste_add(pdata, psize, limit);
|
||||
else
|
||||
paste_replace(buffer, pdata, psize);
|
||||
if (paste_set(pdata, psize, bufname, &cause) != 0) {
|
||||
cmdq_error(cmdq, "%s", cause);
|
||||
free(pdata);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
1
format.c
1
format.c
@ -608,6 +608,7 @@ format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb,
|
||||
char *s;
|
||||
|
||||
format_add(ft, "buffer_size", "%zu", pb->size);
|
||||
format_add(ft, "buffer_name", "%s", pb->name);
|
||||
|
||||
s = paste_make_sample(pb, utf8flag);
|
||||
format_add(ft, "buffer_sample", "%s", s);
|
||||
|
218
paste.c
218
paste.c
@ -26,127 +26,237 @@
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* Stack of paste buffers. Note that paste buffer data is not necessarily a C
|
||||
* Set of paste buffers. Note that paste buffer data is not necessarily a C
|
||||
* string!
|
||||
*/
|
||||
|
||||
ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER;
|
||||
u_int paste_next_index;
|
||||
u_int paste_next_order;
|
||||
u_int paste_num_automatic;
|
||||
RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
|
||||
RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
|
||||
|
||||
/* Return each item of the stack in turn. */
|
||||
struct paste_buffer *
|
||||
paste_walk_stack(u_int *idx)
|
||||
int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
|
||||
RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
|
||||
RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
|
||||
|
||||
int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
|
||||
RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
|
||||
RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
|
||||
|
||||
int
|
||||
paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
|
||||
pb = paste_get_index(*idx);
|
||||
(*idx)++;
|
||||
return (pb);
|
||||
return (strcmp(a->name, b->name));
|
||||
}
|
||||
|
||||
/* Get the top item on the stack. */
|
||||
int
|
||||
paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
|
||||
{
|
||||
if (a->order > b->order)
|
||||
return (-1);
|
||||
if (a->order < b->order)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Walk paste buffers by name. */
|
||||
struct paste_buffer *
|
||||
paste_walk(struct paste_buffer *pb)
|
||||
{
|
||||
if (pb == NULL)
|
||||
return (RB_MIN(paste_time_tree, &paste_by_time));
|
||||
return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
|
||||
}
|
||||
|
||||
/* Get the most recent automatic buffer */
|
||||
struct paste_buffer *
|
||||
paste_get_top(void)
|
||||
{
|
||||
if (ARRAY_LENGTH(&paste_buffers) == 0)
|
||||
struct paste_buffer *pb;
|
||||
|
||||
pb = RB_MIN(paste_time_tree, &paste_by_time);
|
||||
if (pb == NULL)
|
||||
return (NULL);
|
||||
return (ARRAY_FIRST(&paste_buffers));
|
||||
return (pb);
|
||||
}
|
||||
|
||||
/* Get an item by its index. */
|
||||
struct paste_buffer *
|
||||
paste_get_index(u_int idx)
|
||||
{
|
||||
if (idx >= ARRAY_LENGTH(&paste_buffers))
|
||||
return (NULL);
|
||||
return (ARRAY_ITEM(&paste_buffers, idx));
|
||||
}
|
||||
|
||||
/* Free the top item on the stack. */
|
||||
/* Free the most recent buffer */
|
||||
int
|
||||
paste_free_top(void)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
|
||||
if (ARRAY_LENGTH(&paste_buffers) == 0)
|
||||
pb = paste_get_top();
|
||||
if (pb == NULL)
|
||||
return (-1);
|
||||
|
||||
pb = ARRAY_FIRST(&paste_buffers);
|
||||
ARRAY_REMOVE(&paste_buffers, 0);
|
||||
|
||||
free(pb->data);
|
||||
free(pb);
|
||||
|
||||
return (0);
|
||||
return (paste_free_name(pb->name));
|
||||
}
|
||||
|
||||
/* Free an item by index. */
|
||||
int
|
||||
paste_free_index(u_int idx)
|
||||
/* Get a paste buffer by name. */
|
||||
struct paste_buffer *
|
||||
paste_get_name(const char *name)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
struct paste_buffer pbfind;
|
||||
|
||||
if (idx >= ARRAY_LENGTH(&paste_buffers))
|
||||
if (name == NULL || *name == '\0')
|
||||
return (NULL);
|
||||
|
||||
pbfind.name = (char*)name;
|
||||
return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
|
||||
}
|
||||
|
||||
/* Free a paste buffer by name. */
|
||||
int
|
||||
paste_free_name(const char *name)
|
||||
{
|
||||
struct paste_buffer *pb, pbfind;
|
||||
|
||||
if (name == NULL || *name == '\0')
|
||||
return (-1);
|
||||
|
||||
pb = ARRAY_ITEM(&paste_buffers, idx);
|
||||
ARRAY_REMOVE(&paste_buffers, idx);
|
||||
pbfind.name = (char*)name;
|
||||
pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind);
|
||||
if (pb == NULL)
|
||||
return (-1);
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
|
||||
if (pb->automatic)
|
||||
paste_num_automatic--;
|
||||
|
||||
free(pb->data);
|
||||
free(pb->name);
|
||||
free(pb);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an item onto the top of the stack, freeing the bottom if at limit. Note
|
||||
* Add an automatic buffer, freeing the oldest automatic item if at limit. Note
|
||||
* that the caller is responsible for allocating data.
|
||||
*/
|
||||
void
|
||||
paste_add(char *data, size_t size, u_int limit)
|
||||
paste_add(char *data, size_t size)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
struct paste_buffer *pb, *pb1;
|
||||
u_int limit;
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
while (ARRAY_LENGTH(&paste_buffers) >= limit) {
|
||||
pb = ARRAY_LAST(&paste_buffers);
|
||||
free(pb->data);
|
||||
free(pb);
|
||||
ARRAY_TRUNC(&paste_buffers, 1);
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
|
||||
if (paste_num_automatic < limit)
|
||||
break;
|
||||
if (pb->automatic)
|
||||
paste_free_name(pb->name);
|
||||
}
|
||||
|
||||
pb = xmalloc(sizeof *pb);
|
||||
ARRAY_INSERT(&paste_buffers, 0, pb);
|
||||
|
||||
pb->name = NULL;
|
||||
do {
|
||||
free(pb->name);
|
||||
xasprintf(&pb->name, "buffer%04u", paste_next_index);
|
||||
paste_next_index++;
|
||||
} while (paste_get_name(pb->name) != NULL);
|
||||
|
||||
pb->data = data;
|
||||
pb->size = size;
|
||||
|
||||
pb->automatic = 1;
|
||||
paste_num_automatic++;
|
||||
|
||||
pb->order = paste_next_order++;
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
}
|
||||
|
||||
/* Rename a paste buffer. */
|
||||
int
|
||||
paste_rename(const char *oldname, const char *newname, char **cause)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
|
||||
if (cause != NULL)
|
||||
*cause = NULL;
|
||||
|
||||
if (oldname == NULL || *oldname == '\0') {
|
||||
if (cause != NULL)
|
||||
*cause = xstrdup("no buffer");
|
||||
return (-1);
|
||||
}
|
||||
if (newname == NULL || *newname == '\0') {
|
||||
if (cause != NULL)
|
||||
*cause = xstrdup("new name is empty");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb = paste_get_name(oldname);
|
||||
if (pb == NULL) {
|
||||
if (cause != NULL)
|
||||
xasprintf(cause, "no buffer %s", oldname);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
|
||||
free(pb->name);
|
||||
pb->name = xstrdup(newname);
|
||||
|
||||
if (pb->automatic)
|
||||
paste_num_automatic--;
|
||||
pb->automatic = 0;
|
||||
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace an item on the stack. Note that the caller is responsible for
|
||||
* Add or replace an item in the store. Note that the caller is responsible for
|
||||
* allocating data.
|
||||
*/
|
||||
int
|
||||
paste_replace(u_int idx, char *data, size_t size)
|
||||
paste_set(char *data, size_t size, const char *name, char **cause)
|
||||
{
|
||||
struct paste_buffer *pb;
|
||||
|
||||
if (cause != NULL)
|
||||
*cause = NULL;
|
||||
|
||||
if (size == 0) {
|
||||
free(data);
|
||||
return (0);
|
||||
}
|
||||
if (name == NULL) {
|
||||
paste_add(data, size);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (idx >= ARRAY_LENGTH(&paste_buffers))
|
||||
if (*name == '\0') {
|
||||
if (cause != NULL)
|
||||
*cause = xstrdup("empty buffer name");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb = ARRAY_ITEM(&paste_buffers, idx);
|
||||
free(pb->data);
|
||||
pb = paste_get_name(name);
|
||||
if (pb != NULL)
|
||||
paste_free_name(name);
|
||||
|
||||
pb = xmalloc(sizeof *pb);
|
||||
|
||||
pb->name = xstrdup(name);
|
||||
|
||||
pb->data = data;
|
||||
pb->size = size;
|
||||
|
||||
pb->automatic = 0;
|
||||
pb->order = paste_next_order++;
|
||||
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
63
tmux.1
63
tmux.1
@ -930,9 +930,6 @@ in emacs mode, and
|
||||
.Ql 10w
|
||||
in vi.
|
||||
.Pp
|
||||
When copying the selection, the repeat count indicates the buffer index to
|
||||
replace, if used.
|
||||
.Pp
|
||||
Mode key bindings are defined in a set of named tables:
|
||||
.Em vi-edit
|
||||
and
|
||||
@ -1090,7 +1087,7 @@ but a different format may be specified with
|
||||
.Fl F .
|
||||
.It Xo Ic capture-pane
|
||||
.Op Fl aepPq
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Op Fl E Ar end-line
|
||||
.Op Fl S Ar start-line
|
||||
.Op Fl t Ar target-pane
|
||||
@ -3366,19 +3363,40 @@ is given, otherwise the active pane for the session attached to
|
||||
.El
|
||||
.Sh BUFFERS
|
||||
.Nm
|
||||
maintains a stack of
|
||||
maintains a set of named
|
||||
.Em paste buffers .
|
||||
Up to the value of the
|
||||
Each buffer may be either explicitly or automatically named.
|
||||
Explicitly named buffers are named when created with the
|
||||
.Ic set-buffer
|
||||
or
|
||||
.Ic load-buffer
|
||||
commands, or by renaming an automatically named buffer with
|
||||
.Ic set-buffer
|
||||
.Fl n .
|
||||
Automatically named buffers are given a name such as
|
||||
.Ql buffer0001 ,
|
||||
.Ql buffer0002
|
||||
and so on.
|
||||
When the
|
||||
.Ic buffer-limit
|
||||
option are kept; when a new buffer is added, the buffer at the bottom of the
|
||||
stack is removed.
|
||||
option is reached, the oldest automatically named buffer is deleted.
|
||||
Explicitly named are not subject to
|
||||
.Ic buffer-limit
|
||||
and may be deleted with
|
||||
.Ic delete-buffer
|
||||
command.
|
||||
.Pp
|
||||
Buffers may be added using
|
||||
.Ic copy-mode
|
||||
or the
|
||||
.Ic set-buffer
|
||||
command, and pasted into a window using the
|
||||
and
|
||||
.Ic load-buffer
|
||||
commands, and pasted into a window using the
|
||||
.Ic paste-buffer
|
||||
command.
|
||||
If a buffer command is used and no buffer is specified, the most
|
||||
recently added automatically named buffer is assumed.
|
||||
.Pp
|
||||
A configurable history buffer is also maintained for each window.
|
||||
By default, up to 2000 lines are kept; this can be altered with the
|
||||
@ -3399,7 +3417,7 @@ Put a window into buffer choice mode, where a buffer may be chosen
|
||||
interactively from a list.
|
||||
After a buffer is selected,
|
||||
.Ql %%
|
||||
is replaced by the buffer index in
|
||||
is replaced by the buffer name in
|
||||
.Ar template
|
||||
and the result executed as a command.
|
||||
If
|
||||
@ -3414,11 +3432,11 @@ This command works only if at least one client is attached.
|
||||
.It Ic clear-history Op Fl t Ar target-pane
|
||||
.D1 (alias: Ic clearhist )
|
||||
Remove and free the history for the specified pane.
|
||||
.It Ic delete-buffer Op Fl b Ar buffer-index
|
||||
.It Ic delete-buffer Op Fl b Ar buffer-name
|
||||
.D1 (alias: Ic deleteb )
|
||||
Delete the buffer at
|
||||
.Ar buffer-index ,
|
||||
or the top buffer if not specified.
|
||||
Delete the buffer named
|
||||
.Ar buffer-name ,
|
||||
or the most recently added automatically named buffer if not specified.
|
||||
.It Xo Ic list-buffers
|
||||
.Op Fl F Ar format
|
||||
.Xc
|
||||
@ -3430,7 +3448,7 @@ flag, see the
|
||||
.Sx FORMATS
|
||||
section.
|
||||
.It Xo Ic load-buffer
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Ar path
|
||||
.Xc
|
||||
.D1 (alias: Ic loadb )
|
||||
@ -3438,7 +3456,7 @@ Load the contents of the specified paste buffer from
|
||||
.Ar path .
|
||||
.It Xo Ic paste-buffer
|
||||
.Op Fl dpr
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Op Fl s Ar separator
|
||||
.Op Fl t Ar target-pane
|
||||
.Xc
|
||||
@ -3447,7 +3465,7 @@ Insert the contents of a paste buffer into the specified pane.
|
||||
If not specified, paste into the current one.
|
||||
With
|
||||
.Fl d ,
|
||||
also delete the paste buffer from the stack.
|
||||
also delete the paste buffer.
|
||||
When output, any linefeed (LF) characters in the paste buffer are replaced with
|
||||
a separator, by default carriage return (CR).
|
||||
A custom separator may be specified using the
|
||||
@ -3462,7 +3480,7 @@ is specified, paste bracket control codes are inserted around the
|
||||
buffer if the application has requested bracketed paste mode.
|
||||
.It Xo Ic save-buffer
|
||||
.Op Fl a
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Ar path
|
||||
.Xc
|
||||
.D1 (alias: Ic saveb )
|
||||
@ -3473,7 +3491,8 @@ The
|
||||
option appends to rather than overwriting the file.
|
||||
.It Xo Ic set-buffer
|
||||
.Op Fl a
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Op Fl n Ar new-buffer-name
|
||||
.Ar data
|
||||
.Xc
|
||||
.D1 (alias: Ic setb )
|
||||
@ -3482,8 +3501,12 @@ Set the contents of the specified buffer to
|
||||
The
|
||||
.Fl a
|
||||
option appends to rather than overwriting the buffer.
|
||||
The
|
||||
.Fl n
|
||||
option renames the buffer to
|
||||
.Ar new-buffer-name .
|
||||
.It Xo Ic show-buffer
|
||||
.Op Fl b Ar buffer-index
|
||||
.Op Fl b Ar buffer-name
|
||||
.Xc
|
||||
.D1 (alias: Ic showb )
|
||||
Display the contents of the specified buffer.
|
||||
|
25
tmux.h
25
tmux.h
@ -85,7 +85,7 @@ extern char **environ;
|
||||
|
||||
/* Default template for choose-buffer. */
|
||||
#define CHOOSE_BUFFER_TEMPLATE \
|
||||
"#{line}: #{buffer_size} bytes: #{buffer_sample}"
|
||||
"#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}"
|
||||
|
||||
/* Default template for choose-client. */
|
||||
#define CHOOSE_CLIENT_TEMPLATE \
|
||||
@ -118,7 +118,8 @@ extern char **environ;
|
||||
|
||||
/* Default template for list-buffers. */
|
||||
#define LIST_BUFFERS_TEMPLATE \
|
||||
"#{line}: #{buffer_size} bytes: \"#{buffer_sample}\""
|
||||
"#{buffer_name}: #{buffer_size} bytes: " \
|
||||
"\"#{buffer_sample}\""
|
||||
|
||||
/* Default template for list-clients. */
|
||||
#define LIST_CLIENTS_TEMPLATE \
|
||||
@ -1036,6 +1037,13 @@ struct layout_cell {
|
||||
struct paste_buffer {
|
||||
char *data;
|
||||
size_t size;
|
||||
|
||||
char *name;
|
||||
int automatic;
|
||||
u_int order;
|
||||
|
||||
RB_ENTRY(paste_buffer) name_entry;
|
||||
RB_ENTRY(paste_buffer) time_entry;
|
||||
};
|
||||
|
||||
/* Environment variable. */
|
||||
@ -1499,7 +1507,7 @@ RB_HEAD(format_tree, format_entry);
|
||||
#define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]"
|
||||
#define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]"
|
||||
#define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]"
|
||||
#define CMD_BUFFER_USAGE "[-b buffer-index]"
|
||||
#define CMD_BUFFER_USAGE "[-b buffer-name]"
|
||||
|
||||
/* tmux.c */
|
||||
extern struct options global_options;
|
||||
@ -1711,13 +1719,14 @@ void tty_keys_free(struct tty *);
|
||||
int tty_keys_next(struct tty *);
|
||||
|
||||
/* paste.c */
|
||||
struct paste_buffer *paste_walk_stack(u_int *);
|
||||
struct paste_buffer *paste_walk(struct paste_buffer *);
|
||||
struct paste_buffer *paste_get_top(void);
|
||||
struct paste_buffer *paste_get_index(u_int);
|
||||
struct paste_buffer *paste_get_name(const char *);
|
||||
int paste_free_top(void);
|
||||
int paste_free_index(u_int);
|
||||
void paste_add(char *, size_t, u_int);
|
||||
int paste_replace(u_int, char *, size_t);
|
||||
int paste_free_name(const char *);
|
||||
void paste_add(char *, size_t);
|
||||
int paste_rename(const char *, const char *, char **);
|
||||
int paste_set(char *, size_t, const char *, char **);
|
||||
char *paste_make_sample(struct paste_buffer *, int);
|
||||
void paste_send_pane(struct paste_buffer *, struct window_pane *,
|
||||
const char *, int);
|
||||
|
@ -54,11 +54,12 @@ void window_copy_update_cursor(struct window_pane *, u_int, u_int);
|
||||
void window_copy_start_selection(struct window_pane *);
|
||||
int window_copy_update_selection(struct window_pane *, int);
|
||||
void *window_copy_get_selection(struct window_pane *, size_t *);
|
||||
void window_copy_copy_buffer(struct window_pane *, int, void *, size_t);
|
||||
void window_copy_copy_pipe(
|
||||
struct window_pane *, struct session *, int, const char *);
|
||||
void window_copy_copy_selection(struct window_pane *, int);
|
||||
void window_copy_append_selection(struct window_pane *, int);
|
||||
void window_copy_copy_buffer(struct window_pane *, const char *, void *,
|
||||
size_t);
|
||||
void window_copy_copy_pipe(struct window_pane *, struct session *,
|
||||
const char *, const char *);
|
||||
void window_copy_copy_selection(struct window_pane *, const char *);
|
||||
void window_copy_append_selection(struct window_pane *, const char *);
|
||||
void window_copy_clear_selection(struct window_pane *);
|
||||
void window_copy_copy_line(
|
||||
struct window_pane *, char **, size_t *, u_int, u_int, u_int);
|
||||
@ -417,7 +418,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
|
||||
switch (cmd) {
|
||||
case MODEKEYCOPY_APPENDSELECTION:
|
||||
if (sess != NULL) {
|
||||
window_copy_append_selection(wp, data->numprefix);
|
||||
window_copy_append_selection(wp, NULL);
|
||||
window_pane_reset_mode(wp);
|
||||
return;
|
||||
}
|
||||
@ -543,7 +544,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
|
||||
if (sess != NULL &&
|
||||
(cmd == MODEKEYCOPY_COPYLINE ||
|
||||
cmd == MODEKEYCOPY_COPYENDOFLINE)) {
|
||||
window_copy_copy_selection(wp, -1);
|
||||
window_copy_copy_selection(wp, NULL);
|
||||
window_pane_reset_mode(wp);
|
||||
return;
|
||||
}
|
||||
@ -554,14 +555,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
|
||||
break;
|
||||
case MODEKEYCOPY_COPYPIPE:
|
||||
if (sess != NULL) {
|
||||
window_copy_copy_pipe(wp, sess, data->numprefix, arg);
|
||||
window_copy_copy_pipe(wp, sess, NULL, arg);
|
||||
window_pane_reset_mode(wp);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MODEKEYCOPY_COPYSELECTION:
|
||||
if (sess != NULL) {
|
||||
window_copy_copy_selection(wp, data->numprefix);
|
||||
window_copy_copy_selection(wp, NULL);
|
||||
window_pane_reset_mode(wp);
|
||||
return;
|
||||
}
|
||||
@ -918,7 +919,7 @@ reset_mode:
|
||||
s->mode &= ~MODE_MOUSE_BUTTON;
|
||||
s->mode |= MODE_MOUSE_STANDARD;
|
||||
if (sess != NULL) {
|
||||
window_copy_copy_selection(wp, -1);
|
||||
window_copy_copy_selection(wp, NULL);
|
||||
window_pane_reset_mode(wp);
|
||||
}
|
||||
}
|
||||
@ -1452,9 +1453,9 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
|
||||
}
|
||||
|
||||
void
|
||||
window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
|
||||
window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
u_int limit;
|
||||
struct screen_write_ctx ctx;
|
||||
|
||||
if (options_get_number(&global_options, "set-clipboard")) {
|
||||
@ -1463,16 +1464,13 @@ window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len)
|
||||
screen_write_stop(&ctx);
|
||||
}
|
||||
|
||||
if (idx == -1) {
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
paste_add(buf, len, limit);
|
||||
} else if (paste_replace(idx, buf, len) != 0)
|
||||
if (paste_set(buf, len, bufname, NULL) != 0)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void
|
||||
window_copy_copy_pipe(
|
||||
struct window_pane *wp, struct session *sess, int idx, const char *arg)
|
||||
window_copy_copy_pipe(struct window_pane *wp, struct session *sess,
|
||||
const char *bufname, const char *arg)
|
||||
{
|
||||
void *buf;
|
||||
size_t len;
|
||||
@ -1486,11 +1484,11 @@ window_copy_copy_pipe(
|
||||
job = job_run(arg, sess, NULL, NULL, NULL);
|
||||
bufferevent_write(job->event, buf, len);
|
||||
|
||||
window_copy_copy_buffer(wp, idx, buf, len);
|
||||
window_copy_copy_buffer(wp, bufname, buf, len);
|
||||
}
|
||||
|
||||
void
|
||||
window_copy_copy_selection(struct window_pane *wp, int idx)
|
||||
window_copy_copy_selection(struct window_pane *wp, const char *bufname)
|
||||
{
|
||||
void* buf;
|
||||
size_t len;
|
||||
@ -1499,17 +1497,16 @@ window_copy_copy_selection(struct window_pane *wp, int idx)
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
window_copy_copy_buffer(wp, idx, buf, len);
|
||||
window_copy_copy_buffer(wp, bufname, buf, len);
|
||||
}
|
||||
|
||||
void
|
||||
window_copy_append_selection(struct window_pane *wp, int idx)
|
||||
window_copy_append_selection(struct window_pane *wp, const char *bufname)
|
||||
{
|
||||
char *buf;
|
||||
struct paste_buffer *pb;
|
||||
size_t len;
|
||||
u_int limit;
|
||||
struct screen_write_ctx ctx;
|
||||
char *buf;
|
||||
struct paste_buffer *pb;
|
||||
size_t len;
|
||||
struct screen_write_ctx ctx;
|
||||
|
||||
buf = window_copy_get_selection(wp, &len);
|
||||
if (buf == NULL)
|
||||
@ -1521,24 +1518,19 @@ window_copy_append_selection(struct window_pane *wp, int idx)
|
||||
screen_write_stop(&ctx);
|
||||
}
|
||||
|
||||
if (idx == -1)
|
||||
idx = 0;
|
||||
|
||||
if (idx == 0 && paste_get_top() == NULL) {
|
||||
limit = options_get_number(&global_options, "buffer-limit");
|
||||
paste_add(buf, len, limit);
|
||||
return;
|
||||
}
|
||||
|
||||
pb = paste_get_index(idx);
|
||||
if (bufname == NULL || *bufname == '\0') {
|
||||
pb = paste_get_top();
|
||||
if (pb != NULL)
|
||||
bufname = pb->name;
|
||||
} else
|
||||
pb = paste_get_name(bufname);
|
||||
if (pb != NULL) {
|
||||
buf = xrealloc(buf, 1, len + pb->size);
|
||||
memmove(buf + pb->size, buf, len);
|
||||
memcpy(buf, pb->data, pb->size);
|
||||
len += pb->size;
|
||||
}
|
||||
|
||||
if (paste_replace(idx, buf, len) != 0)
|
||||
if (paste_set(buf, len, bufname, NULL) != 0)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user