Hi

Thanks for this, but I'm not sure about the approach.

I don't like having separate named and non-named stacks, I definitely
think we only need one data structure - unnamed buffers should be the
same as named but with some automatically-generated generic name like
"buffer0101".

TBH if we have named buffers I'm not sure we need a stack at all. The
only special buffer is the most recent.

I think I would probably do something like this:

- drop the stack and have a) a tree of buffers by name and b) one
  most-recent-buffer pointer;

- every buffer has a name and a sticky flag (plus creation time to do
  buffer-limit);

- when you copy, if you give it a name then the sticky flag is
  automatically set;

- if you don't, it's named "buffer%04u" and the sticky flag is not set;

- probably the most-recent-buffer pointer is only set to the last
  unnamed buffer;

- -b stops taking an index and only takes a name;

- if you don't give -b it uses the most-recent-buffer;

- set-buffer grows flags to rename a buffer and change the sticky flag;

- buffer-limit is only applied to !sticky buffers, and will remove the
  oldest - either this can search (which'll be slower but I don't think
  it'll matter) or use a parallel tree keyed by time (faster but more
  code).

The stack is only really useful to show time order and make buffer-limit
fast. I doubt many people are actually popping off the stack or binding
keys to reach down into it. And if they are, it should be easy to script
the equivalent, or we can add some extra syntax to help out when we find
out what they are doing.

And if the unnamed buffers are named with an ever increasing suffix,
they will sort in time order automatically in the choose-buffer list.

What do you think?


On Fri, Mar 28, 2014 at 12:01:17AM -0500, J Raynor wrote:
> I've attached a patch which implements named buffers.
> 
> Commands that take a buffer arg, like save-buffer and paste-buffer,
> have been modified so that -b can take a number or a name.  So, you
> can do "setb -b myBuf someData".
> 
> buffer-limit applies to both the paste stack and the named buffers.
> So if buffer-limit is set to 20, you can have 20 items in the paste
> stack, and 20 named buffer items.
> 
> Named buffers have to be explicitly deleted.  If you try to add a new
> named buffer and you're at the buffer limit, it will fail to add a new
> named buffer.  This is unlike the paste stack which removes the oldest
> item.
> 
> When in copy mode, pressing the double-quote character allows you to
> enter a buffer name.  Once you've made a selection, typing "myBuf and
> then hitting Enter will save your selection to the buffer named myBuf.
>  You can hit Ctrl-a instead of Enter to append.  Both of these key
> bindings are only set by default in vi mode.
> 
> list-buffer and choose-buffer display the items in the paste stack
> first, and then the named buffers, which are sorted alphabetically.
> 
> I added the ability to do prefix-P to more quickly enter a buffer name
> (or number) for pasting.

> diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
> index e157e3c..d801daa 100644
> --- a/cmd-capture-pane.c
> +++ b/cmd-capture-pane.c
> @@ -164,7 +164,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>       struct args             *args = self->args;
>       struct client           *c;
>       struct window_pane      *wp;
> -     char                    *buf, *cause;
> +     char                    *buf, *cause, *bufname;
>       int                      buffer;
>       u_int                    limit;
>       size_t                   len;
> @@ -200,13 +200,17 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>  
>               buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause);
>               if (cause != NULL) {
> -                     cmdq_error(cmdq, "buffer %s", cause);
> -                     free(buf);
>                       free(cause);
> -                     return (CMD_RETURN_ERROR);
> -             }
> -
> -             if (paste_replace(&global_buffers, buffer, buf, len) != 0) {
> +                     bufname = xstrdup(args_get(args, 'b'));
> +                     if (paste_set_named_buffer(&named_buffers,
> +                         buf, len, limit, bufname, &cause) != 0) {
> +                             cmdq_error(cmdq, "%s", cause);
> +                             free(buf);
> +                             free(cause);
> +                             free(bufname);
> +                             return (CMD_RETURN_ERROR);
> +                     }
> +             } else if (paste_replace(&global_buffers, buffer, buf, len) != 
> 0) {
>                       cmdq_error(cmdq, "no buffer %d", buffer);
>                       free(buf);
>                       return (CMD_RETURN_ERROR);
> diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c
> index d79f6fd..a96fae1 100644
> --- a/cmd-choose-buffer.c
> +++ b/cmd-choose-buffer.c
> @@ -87,6 +87,22 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>  
>               window_choose_add(wl->window->active, cdata);
>       }
> +
> +     idx = 0;
> +     while ((pb = paste_walk_stack(&named_buffers, &idx)) != NULL) {
> +             cdata = window_choose_data_create(TREE_OTHER, c, c->session);
> +             cdata->idx = idx - 1;
> +
> +             cdata->ft_template = xstrdup(template);
> +             format_add(cdata->ft, "line", "%s", pb->name);
> +             format_paste_buffer(cdata->ft, pb);
> +
> +             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);
> +     }
>       free(action);
>  
>       window_choose_ready(wl->window->active, 0, NULL);
> diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c
> index 759d578..0acecc5 100644
> --- a/cmd-command-prompt.c
> +++ b/cmd-command-prompt.c
> @@ -76,6 +76,9 @@ cmd_command_prompt_key_binding(struct cmd *self, int key)
>               self->args = args_create(1, "select-window -t ':%%'");
>               args_set(self->args, 'p', "index");
>               break;
> +     case 'P':
> +             self->args = args_create(1, "paste-buffer -b '%%'");
> +             break;
>       default:
>               self->args = args_create(0);
>               break;
> diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c
> index b8f55db..d4f97c1 100644
> --- a/cmd-delete-buffer.c
> +++ b/cmd-delete-buffer.c
> @@ -43,6 +43,7 @@ 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(&global_buffers);
> @@ -51,9 +52,14 @@ cmd_delete_buffer_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>  
>       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 = args_get(args, 'b');
> +             if (paste_free_name(&named_buffers, bufname) != 0) {
> +                     cmdq_error(cmdq, "no buffer %s", bufname);
> +                     return (CMD_RETURN_ERROR);
> +             }
> +
> +             return (CMD_RETURN_NORMAL);
>       }
>  
>       if (paste_free_index(&global_buffers, buffer) != 0) {
> diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c
> index 02a4183..98d013f 100644
> --- a/cmd-list-buffers.c
> +++ b/cmd-list-buffers.c
> @@ -64,5 +64,18 @@ cmd_list_buffers_exec(unused struct cmd *self, struct 
> cmd_q *cmdq)
>               format_free(ft);
>       }
>  
> +     idx = 0;
> +     while ((pb = paste_walk_stack(&named_buffers, &idx)) != NULL) {
> +             ft = format_create();
> +             format_add(ft, "line", "%s", pb->name);
> +             format_paste_buffer(ft, pb);
> +
> +             line = format_expand(ft, template);
> +             cmdq_print(cmdq, "%s", line);
> +             free(line);
> +
> +             format_free(ft);
> +     }
> +
>       return (CMD_RETURN_NORMAL);
>  }
> diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
> index 2cb01b9..9093fa1 100644
> --- a/cmd-load-buffer.c
> +++ b/cmd-load-buffer.c
> @@ -51,29 +51,29 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
>       struct session  *s;
>       FILE            *f;
>       const char      *path;
> -     char            *pdata, *new_pdata, *cause;
> +     char            *pdata, *new_pdata, *cause, *bufname;
>       size_t           psize;
>       u_int            limit;
> -     int              ch, error, buffer, *buffer_ptr, cwd, fd;
> +     int              ch, error, buffer, cwd, fd;
>  
> +     bufname = NULL;
>       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 = xstrdup(args_get(args, 'b'));
>               }
>       }
>  
>       path = args->argv[0];
>       if (strcmp(path, "-") == 0) {
> -             buffer_ptr = xmalloc(sizeof *buffer_ptr);
> -             *buffer_ptr = buffer;
> +             if (bufname == NULL && args_has(args, 'b'))
> +                     bufname = xstrdup(args_get(args, 'b'));
>  
>               error = server_set_stdin_callback(c, cmd_load_buffer_callback,
> -                 buffer_ptr, &cause);
> +                 bufname, &cause);
>               if (error != 0) {
>                       cmdq_error(cmdq, "%s: %s", path, cause);
>                       free(cause);
> @@ -118,6 +118,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>       fclose(f);
>  
>       limit = options_get_number(&global_options, "buffer-limit");
> +
> +     if (bufname != NULL) {
> +             if(paste_set_named_buffer(&named_buffers,
> +                 pdata, psize, limit, bufname, &cause) != 0) {
> +                     cmdq_error(cmdq, "%s", cause);
> +                     free(pdata);
> +                     free(cause);
> +                     free(bufname);
> +                     return (CMD_RETURN_ERROR);
> +             }
> +             return (CMD_RETURN_NORMAL);
> +     }
> +
>       if (buffer == -1) {
>               paste_add(&global_buffers, pdata, psize, limit);
>               return (CMD_RETURN_NORMAL);
> @@ -140,8 +153,8 @@ error:
>  void
>  cmd_load_buffer_callback(struct client *c, int closed, void *data)
>  {
> -     int     *buffer = data;
> -     char    *pdata;
> +     int     buffer;
> +     char    *pdata, *cause, *bufname, *bufarg = data;
>       size_t   psize;
>       u_int    limit;
>  
> @@ -163,11 +176,28 @@ cmd_load_buffer_callback(struct client *c, int closed, 
> void *data)
>       evbuffer_drain(c->stdin_data, psize);
>  
>       limit = options_get_number(&global_options, "buffer-limit");
> -     if (*buffer == -1)
> +     bufname = NULL;
> +     if (bufarg == NULL)
> +             buffer = -1;
> +     else {
> +             buffer = strtonum(bufarg, 0, INT_MAX, &cause);
> +             if (cause != NULL)
> +                     bufname = xstrdup(bufarg);
> +     }
> +
> +     if (bufname != NULL) {
> +             if (paste_set_named_buffer(&named_buffers,
> +                 pdata, psize, limit, bufname, &cause) != 0) {
> +                     evbuffer_add_printf(c->stderr_data, "%s", cause);
> +                     server_push_stderr(c);
> +                     free(pdata);
> +                     free(cause);
> +             }
> +     } else if (buffer == -1)
>               paste_add(&global_buffers, pdata, psize, limit);
> -     else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
> +     else if (paste_replace(&global_buffers, buffer, pdata, psize) != 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, "no buffer %d\n", buffer);
>               server_push_stderr(c);
>               free(pdata);
>       }
> diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c
> index 5305b7e..14434d1 100644
> --- a/cmd-paste-buffer.c
> +++ b/cmd-paste-buffer.c
> @@ -48,7 +48,7 @@ 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;
> +     const char              *sepstr, *bufname;
>       char                    *cause;
>       int                      buffer;
>       int                      pflag;
> @@ -56,18 +56,24 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q 
> *cmdq)
>       if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL)
>               return (CMD_RETURN_ERROR);
>  
> +     bufname = NULL;
>       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 = args_get(args, 'b');
>               }
>       }
>  
> -     if (buffer == -1)
> +     if (bufname != NULL) {
> +             pb = paste_get_name(&named_buffers, bufname);
> +             if (pb == NULL) {
> +                     cmdq_error(cmdq, "no buffer %s", bufname);
> +                     return (CMD_RETURN_ERROR);
> +             }
> +     } else if (buffer == -1)
>               pb = paste_get_top(&global_buffers);
>       else {
>               pb = paste_get_index(&global_buffers, buffer);
> @@ -91,7 +97,9 @@ 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_name(&named_buffers, bufname);
> +             else if (buffer == -1)
>                       paste_free_top(&global_buffers);
>               else
>                       paste_free_index(&global_buffers, buffer);
> diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
> index 3788fc2..9933f60 100644
> --- a/cmd-save-buffer.c
> +++ b/cmd-save-buffer.c
> @@ -58,7 +58,7 @@ 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;
> +     const char              *path, *bufname;
>       char                    *cause, *start, *end, *msg;
>       size_t                   size, used, msglen;
>       int                      cwd, fd, buffer;
> @@ -72,15 +72,19 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
>       } 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(&global_buffers, buffer);
> -             if (pb == NULL) {
> -                     cmdq_error(cmdq, "no buffer %d", buffer);
> -                     return (CMD_RETURN_ERROR);
> +                     bufname = args_get(args, 'b');
> +                     pb = paste_get_name(&named_buffers, bufname);
> +                     if (pb == NULL) {
> +                             cmdq_error(cmdq, "no buffer %s", bufname);
> +                             return (CMD_RETURN_ERROR);
> +                     }
> +             } else {
> +                     pb = paste_get_index(&global_buffers, buffer);
> +                     if (pb == NULL) {
> +                             cmdq_error(cmdq, "no buffer %d", buffer);
> +                             return (CMD_RETURN_ERROR);
> +                     }
>               }
>       }
>  
> diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
> index e7f9b95..63fc076 100644
> --- a/cmd-set-buffer.c
> +++ b/cmd-set-buffer.c
> @@ -44,7 +44,7 @@ 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;
> +     char                    *pdata, *cause, *bufname;
>       size_t                   psize, newsize;
>       int                      buffer;
>  
> @@ -55,6 +55,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
>  
>       pb = NULL;
>       buffer = -1;
> +     bufname = NULL;
>  
>       if ((newsize = strlen(args->argv[0])) == 0)
>               return (CMD_RETURN_NORMAL);
> @@ -62,14 +63,15 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
>       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(&global_buffers, buffer);
> -             if (pb == NULL) {
> -                     cmdq_error(cmdq, "no buffer %d", buffer);
> -                     return (CMD_RETURN_ERROR);
> +                     bufname = xstrdup(args_get(args, 'b'));
> +                     pb = paste_get_name(&named_buffers, bufname);
> +             } else {
> +                     pb = paste_get_index(&global_buffers, buffer);
> +                     if (pb == NULL) {
> +                             cmdq_error(cmdq, "no buffer %d", buffer);
> +                             return (CMD_RETURN_ERROR);
> +                     }
>               }
>       } else if (args_has(args, 'a')) {
>               pb = paste_get_top(&global_buffers);
> @@ -87,7 +89,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
>       memcpy(pdata + psize, args->argv[0], newsize);
>       psize += newsize;
>  
> -     if (buffer == -1)
> +     if (bufname != NULL) {
> +             if (paste_set_named_buffer(&named_buffers,
> +                 pdata, psize, limit, bufname, &cause) != 0) {
> +                     cmdq_error(cmdq, "%s", cause);
> +                     free(pdata);
> +                     free(cause);
> +                     free(bufname);
> +                     return (CMD_RETURN_ERROR);
> +             }
> +     } else if (buffer == -1)
>               paste_add(&global_buffers, pdata, psize, limit);
>       else
>               paste_replace(&global_buffers, buffer, pdata, psize);
> diff --git a/key-bindings.c b/key-bindings.c
> index 86048ea..93fbaa8 100644
> --- a/key-bindings.c
> +++ b/key-bindings.c
> @@ -130,6 +130,7 @@ key_bindings_init(void)
>               { '?',                    0, &cmd_list_keys_entry },
>               { 'D',                    0, &cmd_choose_client_entry },
>               { 'L',                    0, &cmd_switch_client_entry },
> +             { 'P',                    0, &cmd_command_prompt_entry },
>               { '[',                    0, &cmd_copy_mode_entry },
>               { '\'',                   0, &cmd_command_prompt_entry },
>               { '\002', /* C-b */       0, &cmd_send_prefix_entry },
> diff --git a/mode-key.c b/mode-key.c
> index 57be2d8..b9ad8e4 100644
> --- a/mode-key.c
> +++ b/mode-key.c
> @@ -51,6 +51,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = {
>       { MODEKEYEDIT_DELETEWORD, "delete-word" },
>       { MODEKEYEDIT_ENDOFLINE, "end-of-line" },
>       { MODEKEYEDIT_ENTER, "enter" },
> +     { MODEKEYEDIT_ENTERAPPEND, "enter-append" },
>       { MODEKEYEDIT_HISTORYDOWN, "history-down" },
>       { MODEKEYEDIT_HISTORYUP, "history-up" },
>       { MODEKEYEDIT_NEXTSPACE, "next-space" },
> @@ -141,6 +142,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
>       { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" },
>       { MODEKEYCOPY_SEARCHUP, "search-backward" },
>       { MODEKEYCOPY_SELECTLINE, "select-line" },
> +     { MODEKEYCOPY_STARTNAMEDBUFFER, "start-named-buffer" },
>       { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" },
>       { MODEKEYCOPY_STARTOFLINE, "start-of-line" },
>       { MODEKEYCOPY_STARTSELECTION, "begin-selection" },
> @@ -160,6 +162,7 @@ const struct mode_key_entry mode_key_vi_edit[] = {
>       { '\033' /* Escape */,      0, MODEKEYEDIT_SWITCHMODE },
>       { '\n',                     0, MODEKEYEDIT_ENTER },
>       { '\r',                     0, MODEKEYEDIT_ENTER },
> +     { '\001' /* C-a */,         0, MODEKEYEDIT_ENTERAPPEND },
>       { KEYC_BSPACE,              0, MODEKEYEDIT_BACKSPACE },
>       { KEYC_DC,                  0, MODEKEYEDIT_DELETE },
>       { KEYC_DOWN,                0, MODEKEYEDIT_HISTORYDOWN },
> @@ -257,6 +260,7 @@ struct mode_key_tree mode_key_tree_vi_choice;
>  /* vi copy mode keys. */
>  const struct mode_key_entry mode_key_vi_copy[] = {
>       { ' ',                      0, MODEKEYCOPY_STARTSELECTION },
> +     { '"',                      0, MODEKEYCOPY_STARTNAMEDBUFFER },
>       { '$',                      0, MODEKEYCOPY_ENDOFLINE },
>       { ',',                      0, MODEKEYCOPY_JUMPREVERSE },
>       { ';',                      0, MODEKEYCOPY_JUMPAGAIN },
> diff --git a/paste.c b/paste.c
> index 28f1230..f294bad 100644
> --- a/paste.c
> +++ b/paste.c
> @@ -21,6 +21,7 @@
>  
>  #include <stdlib.h>
>  #include <string.h>
> +#include <ctype.h>
>  
>  #include "tmux.h"
>  
> @@ -29,6 +30,8 @@
>   * string!
>   */
>  
> +int compare_named_buffer_names(const void *, const void *);
> +
>  /* Return each item of the stack in turn. */
>  struct paste_buffer *
>  paste_walk_stack(struct paste_stack *ps, u_int *idx)
> @@ -89,11 +92,31 @@ paste_free_index(struct paste_stack *ps, u_int idx)
>       ARRAY_REMOVE(ps, idx);
>  
>       free(pb->data);
> +     free(pb->name);
>       free(pb);
>  
>       return (0);
>  }
>  
> +/* Free an item by name. */
> +int
> +paste_free_name(struct paste_stack *ps, const char *name)
> +{
> +     struct paste_buffer     *pb;
> +     u_int                    idx;
> +
> +     if (name == NULL || *name == '\0')
> +             return (-1);
> +
> +     idx = 0;
> +     while ((pb = paste_walk_stack(ps, &idx)) != NULL)
> +             if (pb->name != NULL && strcmp(name, pb->name) == 0)
> +                     return (paste_free_index(ps, idx - 1));
> +
> +
> +     return (-1);
> +}
> +
>  /*
>   * Add an item onto the top of the stack, freeing the bottom if at limit. 
> Note
>   * that the caller is responsible for allocating data.
> @@ -109,6 +132,7 @@ paste_add(struct paste_stack *ps, char *data, size_t 
> size, u_int limit)
>       while (ARRAY_LENGTH(ps) >= limit) {
>               pb = ARRAY_LAST(ps);
>               free(pb->data);
> +             free(pb->name);
>               free(pb);
>               ARRAY_TRUNC(ps, 1);
>       }
> @@ -118,6 +142,7 @@ paste_add(struct paste_stack *ps, char *data, size_t 
> size, u_int limit)
>  
>       pb->data = data;
>       pb->size = size;
> +     pb->name = NULL;
>  }
>  
>  
> @@ -194,3 +219,105 @@ paste_send_pane(struct paste_buffer *pb, struct 
> window_pane *wp,
>       if (bracket)
>               bufferevent_write(wp->event, "\033[201~", 6);
>  }
> +
> +int
> +compare_named_buffer_names(const void *a, const void *b)
> +{
> +     struct paste_buffer     *x, *y;
> +
> +     x = *(struct paste_buffer **)a;
> +     y = *(struct paste_buffer **)b;
> +
> +     return (strcmp(x->name, y->name));
> +}
> +
> +/* Get an item by its name. */
> +struct paste_buffer *
> +paste_get_name(struct paste_stack *ps, const char *name)
> +{
> +     struct paste_buffer     *pb;
> +     u_int                    idx = 0;
> +
> +     if (name == NULL || *name == '\0')
> +             return (NULL);
> +
> +     while ((pb = paste_walk_stack(ps, &idx)) != NULL)
> +             if (pb->name != NULL && strcmp(name, pb->name) == 0)
> +                     break;
> +
> +     return (pb);
> +}
> +
> +int
> +paste_set_named_buffer(struct paste_stack *ps,  char *data, size_t size,
> +    u_int limit, char *name, char **cause)
> +{
> +     struct paste_buffer     *pb, **psdata;
> +     char                    *c, *errstr;
> +
> +     if (cause != NULL)
> +             *cause = NULL;
> +
> +     if (name == NULL || *name == '\0') {
> +             if (cause != NULL)
> +                     *cause = xstrdup("empty buffer name");
> +             return (-1);
> +     }
> +
> +
> +
> +     if (size == 0) {
> +             free(data);
> +             free(name);
> +             return (0);
> +     }
> +
> +     pb = paste_get_name(ps, name);
> +
> +     if (pb != NULL) {
> +             free(name);
> +             free(pb->data);
> +             pb->data = data;
> +             pb->size = size;
> +             return (0);
> +     }
> +
> +     if (ARRAY_LENGTH(ps) >= limit) {
> +             if (cause != NULL)
> +                     *cause = xstrdup("buffer-limit reached");
> +             return (-1);
> +     }
> +
> +     if (strlen(name) > 16) {
> +             if (cause != NULL)
> +                     *cause = xstrdup("buffer name too long");
> +             return (-1);
> +     }
> +
> +     strtonum(name, 0, INT_MAX, &errstr);
> +     if (errstr == NULL) {
> +             if (cause != NULL)
> +                     *cause = xstrdup("buffer name can't be a number");
> +             return (-1);
> +     }
> +
> +     for (c = name; *c != '\0'; c++) {
> +             if (!isprint(*c) || isspace(*c)) {
> +                     if (cause != NULL)
> +                             *cause = xstrdup("bad characters in buffer 
> name");
> +                     return (-1);
> +             }
> +     }
> +
> +     pb = xmalloc(sizeof *pb);
> +     ARRAY_INSERT(ps, 0, pb);
> +
> +     pb->data = data;
> +     pb->size = size;
> +     pb->name = name;
> +
> +     psdata = ARRAY_DATA(ps);
> +     qsort(psdata, ARRAY_LENGTH(ps), sizeof pb, &compare_named_buffer_names);
> +     return (0);
> +}
> +
> diff --git a/server.c b/server.c
> index f372de4..0bb11b4 100644
> --- a/server.c
> +++ b/server.c
> @@ -51,6 +51,7 @@ struct event         server_ev_accept;
>  struct event  server_ev_second;
>  
>  struct paste_stack global_buffers;
> +struct paste_stack named_buffers;
>  
>  int           server_create_socket(void);
>  void          server_loop(void);
> @@ -147,6 +148,7 @@ server_start(int lockfd, char *lockfile)
>       RB_INIT(&dead_sessions);
>       TAILQ_INIT(&session_groups);
>       ARRAY_INIT(&global_buffers);
> +     ARRAY_INIT(&named_buffers);
>       mode_key_init_trees();
>       key_bindings_init();
>       utf8_build();
> diff --git a/tmux.h b/tmux.h
> index 5aac390..e3980c5 100644
> --- a/tmux.h
> +++ b/tmux.h
> @@ -496,6 +496,7 @@ enum mode_key_cmd {
>       MODEKEYEDIT_DELETEWORD,
>       MODEKEYEDIT_ENDOFLINE,
>       MODEKEYEDIT_ENTER,
> +     MODEKEYEDIT_ENTERAPPEND,
>       MODEKEYEDIT_HISTORYDOWN,
>       MODEKEYEDIT_HISTORYUP,
>       MODEKEYEDIT_NEXTSPACE,
> @@ -579,6 +580,7 @@ enum mode_key_cmd {
>       MODEKEYCOPY_SEARCHREVERSE,
>       MODEKEYCOPY_SEARCHUP,
>       MODEKEYCOPY_SELECTLINE,
> +     MODEKEYCOPY_STARTNAMEDBUFFER,
>       MODEKEYCOPY_STARTNUMBERPREFIX,
>       MODEKEYCOPY_STARTOFLINE,
>       MODEKEYCOPY_STARTSELECTION,
> @@ -1031,6 +1033,7 @@ struct layout_cell {
>  /* Paste buffer. */
>  struct paste_buffer {
>       char            *data;
> +     char            *name;
>       size_t           size;
>  };
>  ARRAY_DECL(paste_stack, struct paste_buffer *);
> @@ -1707,9 +1710,13 @@ int    tty_keys_next(struct tty *);
>  struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *);
>  struct paste_buffer *paste_get_top(struct paste_stack *);
>  struct paste_buffer *paste_get_index(struct paste_stack *, u_int);
> +struct paste_buffer *paste_get_name(struct paste_stack *, const char *);
>  int           paste_free_top(struct paste_stack *);
>  int           paste_free_index(struct paste_stack *, u_int);
> +int           paste_free_name(struct paste_stack *, const char *);
>  void          paste_add(struct paste_stack *, char *, size_t, u_int);
> +int           paste_set_named_buffer(struct paste_stack *, char *, size_t,
> +                  u_int, char *, char **);
>  int           paste_replace(struct paste_stack *, u_int, char *, size_t);
>  char         *paste_print(struct paste_buffer *, size_t);
>  void          paste_send_pane(struct paste_buffer *, struct window_pane *,
> @@ -1884,6 +1891,7 @@ const char *key_string_lookup_key(int);
>  extern struct clients clients;
>  extern struct clients dead_clients;
>  extern struct paste_stack global_buffers;
> +extern struct paste_stack named_buffers;
>  int   server_start(int, char *);
>  void  server_update_socket(void);
>  void  server_add_accept(int);
> diff --git a/window-copy.c b/window-copy.c
> index e3164f6..f7dac03 100644
> --- a/window-copy.c
> +++ b/window-copy.c
> @@ -94,6 +94,7 @@ const struct window_mode window_copy_mode = {
>  
>  enum window_copy_input_type {
>       WINDOW_COPY_OFF,
> +     WINDOW_COPY_NAMEDBUFFER,
>       WINDOW_COPY_NUMERICPREFIX,
>       WINDOW_COPY_SEARCHUP,
>       WINDOW_COPY_SEARCHDOWN,
> @@ -676,6 +677,7 @@ window_copy_key(struct window_pane *wp, struct session 
> *sess, int key)
>               case WINDOW_COPY_JUMPBACK:
>               case WINDOW_COPY_JUMPTOFORWARD:
>               case WINDOW_COPY_JUMPTOBACK:
> +             case WINDOW_COPY_NAMEDBUFFER:
>               case WINDOW_COPY_NUMERICPREFIX:
>                       break;
>               case WINDOW_COPY_SEARCHUP:
> @@ -711,6 +713,11 @@ window_copy_key(struct window_pane *wp, struct session 
> *sess, int key)
>               data->inputprompt = "Goto Line";
>               *data->inputstr = '\0';
>               goto input_on;
> +     case MODEKEYCOPY_STARTNAMEDBUFFER:
> +             data->inputtype = WINDOW_COPY_NAMEDBUFFER;
> +             data->inputprompt = "Buffer";
> +             *data->inputstr = '\0';
> +             goto input_on;
>       case MODEKEYCOPY_STARTNUMBERPREFIX:
>               key &= KEYC_MASK_KEY;
>               if (key >= '0' && key <= '9') {
> @@ -814,6 +821,11 @@ window_copy_key_input(struct window_pane *wp, int key)
>                       data->searchtype = data->inputtype;
>                       data->searchstr = xstrdup(data->inputstr);
>                       break;
> +             case WINDOW_COPY_NAMEDBUFFER:
> +                     window_copy_copy_selection(wp, -1);
> +                     *data->inputstr = '\0';
> +                     window_pane_reset_mode(wp);
> +                     return (0);
>               case WINDOW_COPY_GOTOLINE:
>                       window_copy_goto_line(wp, data->inputstr);
>                       *data->inputstr = '\0';
> @@ -821,6 +833,17 @@ window_copy_key_input(struct window_pane *wp, int key)
>               }
>               data->numprefix = -1;
>               return (1);
> +     case MODEKEYEDIT_ENTERAPPEND:
> +             switch (data->inputtype) {
> +             case WINDOW_COPY_NAMEDBUFFER:
> +                     window_copy_append_selection(wp, -1);
> +                     *data->inputstr = '\0';
> +                     window_pane_reset_mode(wp);
> +                     return (0);
> +             default:
> +                     break;
> +             }
> +             break;
>       case MODEKEY_OTHER:
>               if (key < 32 || key > 126)
>                       break;
> @@ -1451,8 +1474,10 @@ 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)
>  {
> -     u_int                   limit;
> -     struct screen_write_ctx ctx;
> +     u_int                            limit;
> +     char                            *bufname;
> +     struct screen_write_ctx          ctx;
> +     struct window_copy_mode_data    *data = wp->modedata;
>  
>       if (options_get_number(&global_options, "set-clipboard")) {
>               screen_write_start(&ctx, wp, NULL);
> @@ -1460,10 +1485,21 @@ 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");
> +     limit = options_get_number(&global_options, "buffer-limit");
> +
> +     if (data->inputstr != NULL && *data->inputstr != '\0') {
> +             bufname = xstrdup(data->inputstr);
> +             if (paste_set_named_buffer(&named_buffers,
> +                 buf, len, limit, bufname, NULL) != 0) {
> +                     free(buf);
> +                     free(bufname);
> +             }
> +             return;
> +     }
> +
> +     if (idx == -1)
>               paste_add(&global_buffers, buf, len, limit);
> -     } else if (paste_replace(&global_buffers, idx, buf, len) != 0)
> +     else if (paste_replace(&global_buffers, idx, buf, len) != 0)
>               free(buf);
>  }
>  
> @@ -1502,11 +1538,12 @@ window_copy_copy_selection(struct window_pane *wp, 
> int idx)
>  void
>  window_copy_append_selection(struct window_pane *wp, int idx)
>  {
> -     char                    *buf;
> -     struct paste_buffer     *pb;
> -     size_t                   len;
> -     u_int                    limit;
> -     struct screen_write_ctx  ctx;
> +     char                            *buf, *bufname;
> +     struct paste_buffer             *pb;
> +     size_t                           len;
> +     u_int                            limit;
> +     struct screen_write_ctx          ctx;
> +     struct window_copy_mode_data    *data = wp->modedata;
>  
>       buf = window_copy_get_selection(wp, &len);
>       if (buf == NULL)
> @@ -1521,13 +1558,21 @@ window_copy_append_selection(struct window_pane *wp, 
> int idx)
>       if (idx == -1)
>               idx = 0;
>  
> -     if (idx == 0 && paste_get_top(&global_buffers) == NULL) {
> -             limit = options_get_number(&global_options, "buffer-limit");
> -             paste_add(&global_buffers, buf, len, limit);
> -             return;
> -     }
> +     limit = options_get_number(&global_options, "buffer-limit");
> +
> +     bufname = NULL;
> +     if (data->inputstr != NULL && *data->inputstr != '\0')
> +             bufname = xstrdup(data->inputstr);
> +
> +     if (bufname == NULL) {
> +             if (idx == 0 && paste_get_top(&global_buffers) == NULL) {
> +                     paste_add(&global_buffers, buf, len, limit);
> +                     return;
> +             } else
> +                     pb = paste_get_index(&global_buffers, idx);
> +     } else
> +             pb = paste_get_name(&named_buffers, bufname);
>  
> -     pb = paste_get_index(&global_buffers, idx);
>       if (pb != NULL) {
>               buf = xrealloc(buf, 1, len + pb->size);
>               memmove(buf + pb->size, buf, len);
> @@ -1535,8 +1580,17 @@ window_copy_append_selection(struct window_pane *wp, 
> int idx)
>               len += pb->size;
>       }
>  
> -     if (paste_replace(&global_buffers, idx, buf, len) != 0)
> -             free(buf);
> +     if (bufname == NULL) {
> +             if (paste_replace(&global_buffers, idx, buf, len) != 0)
> +                     free(buf);
> +     } else {
> +             if (paste_set_named_buffer(&named_buffers,
> +                 buf, len, limit, bufname, NULL) != 0) {
> +                     free(buf);
> +                     free(bufname);
> +             }
> +     }
> +
>  }
>  
>  void

> ------------------------------------------------------------------------------

> _______________________________________________
> tmux-users mailing list
> tmux-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/tmux-users


------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and their
applications. Written by three acclaimed leaders in the field,
this first edition is now available. Download your free book today!
http://p.sf.net/sfu/NeoTech
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to