Hi

Yes, I think making it always ordered by time with both automatically
and explicitly named buffers mixed together will be fine - if anyone
comes up with a serious need to make delete-buffer skip !automatic then
we can consider a flag at that point. So effectively it's back to a
stack again :-).

If you want to have persistent buffers with something you paste very
often, then creating them in .tmux.conf will mean their order is low and
they will always sit at the bottom of the list.

This also means the order doesn't change during rename anymore.

Only need to change paste_cmp_times, paste_(get|free)_top and remove the
RB_INSERT and REMOVE from paste_rename:


Index: cmd-capture-pane.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-capture-pane.c,v
retrieving revision 1.26
diff -u -p -r1.26 cmd-capture-pane.c
--- cmd-capture-pane.c  24 Apr 2014 09:14:43 -0000      1.26
+++ cmd-capture-pane.c  9 May 2014 08:54:55 -0000
@@ -38,7 +38,7 @@ char          *cmd_capture_pane_history(struct a
 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 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,23 +191,15 @@ cmd_capture_pane_exec(struct cmd *self, 
                    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);
-                       free(buf);
-                       free(cause);
-                       return (CMD_RETURN_ERROR);
-               }
+               bufname = NULL;
+               if (args_has(args, 'b'))
+                       bufname = args_get(args, 'b');
 
-               if (paste_replace(buffer, buf, len) != 0) {
-                       cmdq_error(cmdq, "no buffer %d", buffer);
+               if (paste_set(buf, len, bufname, &cause) != 0) {
+                       cmdq_error(cmdq, "%s", cause);
                        free(buf);
+                       free(cause);
                        return (CMD_RETURN_ERROR);
                }
        }
Index: cmd-choose-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-choose-buffer.c,v
retrieving revision 1.17
diff -u -p -r1.17 cmd-choose-buffer.c
--- cmd-choose-buffer.c 24 Apr 2014 09:14:43 -0000      1.17
+++ cmd-choose-buffer.c 9 May 2014 08:54:55 -0000
@@ -75,19 +75,20 @@ cmd_choose_buffer_exec(struct cmd *self,
                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);
 
Index: cmd-delete-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-delete-buffer.c,v
retrieving revision 1.11
diff -u -p -r1.11 cmd-delete-buffer.c
--- cmd-delete-buffer.c 24 Apr 2014 09:14:43 -0000      1.11
+++ cmd-delete-buffer.c 9 May 2014 08:54:55 -0000
@@ -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);
        }
 
Index: cmd-list-buffers.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-list-buffers.c,v
retrieving revision 1.20
diff -u -p -r1.20 cmd-list-buffers.c
--- cmd-list-buffers.c  24 Apr 2014 09:14:43 -0000      1.20
+++ cmd-list-buffers.c  9 May 2014 08:54:55 -0000
@@ -44,17 +44,15 @@ cmd_list_buffers_exec(unused struct cmd 
        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);
Index: cmd-load-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-load-buffer.c,v
retrieving revision 1.28
diff -u -p -r1.28 cmd-load-buffer.c
--- cmd-load-buffer.c   24 Apr 2014 09:14:43 -0000      1.28
+++ cmd-load-buffer.c   9 May 2014 08:54:55 -0000
@@ -50,30 +50,19 @@ cmd_load_buffer_exec(struct cmd *self, s
        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, s
 
        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,25 +138,20 @@ cmd_load_buffer_callback(struct client *
                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);
Index: cmd-paste-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-paste-buffer.c,v
retrieving revision 1.24
diff -u -p -r1.24 cmd-paste-buffer.c
--- cmd-paste-buffer.c  24 Apr 2014 09:14:43 -0000      1.24
+++ cmd-paste-buffer.c  9 May 2014 08:54:55 -0000
@@ -36,7 +36,7 @@ void  cmd_paste_buffer_filter(struct wind
 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 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, 
 
        /* 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);
Index: cmd-save-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-save-buffer.c,v
retrieving revision 1.24
diff -u -p -r1.24 cmd-save-buffer.c
--- cmd-save-buffer.c   24 Apr 2014 09:14:43 -0000      1.24
+++ cmd-save-buffer.c   9 May 2014 08:54:55 -0000
@@ -59,10 +59,10 @@ cmd_save_buffer_exec(struct cmd *self, s
        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, s
                        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);
                }
        }
Index: cmd-set-buffer.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/cmd-set-buffer.c,v
retrieving revision 1.17
diff -u -p -r1.17 cmd-set-buffer.c
--- cmd-set-buffer.c    24 Apr 2014 09:14:43 -0000      1.17
+++ cmd-set-buffer.c    9 May 2014 08:54:55 -0000
@@ -31,8 +31,8 @@ enum cmd_retval        cmd_set_buffer_exec(str
 
 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, st
 {
        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, st
        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);
 }
Index: format.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/format.c,v
retrieving revision 1.44
diff -u -p -r1.44 format.c
--- format.c    17 Apr 2014 15:37:55 -0000      1.44
+++ format.c    9 May 2014 08:54:55 -0000
@@ -608,6 +608,7 @@ format_paste_buffer(struct format_tree *
        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);
Index: paste.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/paste.c,v
retrieving revision 1.18
diff -u -p -r1.18 paste.c
--- paste.c     24 Apr 2014 09:14:43 -0000      1.18
+++ paste.c     9 May 2014 08:54:55 -0000
@@ -26,126 +26,236 @@
 #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;
+
+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);
 
-/* 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 *a, const struct paste_buffer *b)
 {
-       struct paste_buffer     *pb;
+       return (strcmp(a->name, b->name));
+}
 
-       pb = paste_get_index(*idx);
-       (*idx)++;
-       return (pb);
+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);
 }
 
-/* Get the top item on the stack. */
+/* Walk paste buffers by name. */
 struct paste_buffer *
-paste_get_top(void)
+paste_walk(struct paste_buffer *pb)
 {
-       if (ARRAY_LENGTH(&paste_buffers) == 0)
-               return (NULL);
-       return (ARRAY_FIRST(&paste_buffers));
+       if (pb == NULL)
+               return (RB_MIN(paste_time_tree, &paste_by_time));
+       return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
 }
 
-/* Get an item by its index. */
+/* Get the most recent automatic buffer */
 struct paste_buffer *
-paste_get_index(u_int idx)
+paste_get_top(void)
 {
-       if (idx >= ARRAY_LENGTH(&paste_buffers))
+       struct paste_buffer     *pb;
+
+       pb = RB_MIN(paste_time_tree, &paste_by_time);
+       if (pb == NULL)
                return (NULL);
-       return (ARRAY_ITEM(&paste_buffers, idx));
+       return (pb);
 }
 
-/* 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);
+       return (paste_free_name(pb->name));
+}
 
-       pb = ARRAY_FIRST(&paste_buffers);
-       ARRAY_REMOVE(&paste_buffers, 0);
+/* Get a paste buffer by name. */
+struct paste_buffer *
+paste_get_name(const char *name)
+{
+       struct paste_buffer     pbfind;
 
-       free(pb->data);
-       free(pb);
+       if (name == NULL || *name == '\0')
+               return (NULL);
 
-       return (0);
+       pbfind.name = (char*)name;
+       return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
 }
 
-/* Free an item by index. */
+/* Free a paste buffer by name. */
 int
-paste_free_index(u_int idx)
+paste_free_name(const char *name)
 {
-       struct paste_buffer     *pb;
+       struct paste_buffer     *pb, pbfind;
 
-       if (idx >= ARRAY_LENGTH(&paste_buffers))
+       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;
+
+       RB_INSERT(paste_name_tree, &paste_by_name, pb);
+       RB_INSERT(paste_time_tree, &paste_by_time, pb);
 
        return (0);
 }
Index: tmux.1
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.1,v
retrieving revision 1.391
diff -u -p -r1.391 tmux.1
--- tmux.1      17 Apr 2014 15:48:02 -0000      1.391
+++ tmux.1      9 May 2014 08:54:56 -0000
@@ -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 
 .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 
 .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 is reached, the oldest automatically named buffer is deleted.
+Explicitly named are not subject to
 .Ic buffer-limit
-option are kept; when a new buffer is added, the buffer at the bottom of the
-stack is removed.
+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, wh
 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 
 .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
 .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 in
 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 code
 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
 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.
Index: tmux.h
===================================================================
RCS file: /cvs/src/usr.bin/tmux/tmux.h,v
retrieving revision 1.459
diff -u -p -r1.459 tmux.h
--- tmux.h      8 May 2014 07:54:47 -0000       1.459
+++ tmux.h      9 May 2014 08:54:56 -0000
@@ -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);
Index: window-copy.c
===================================================================
RCS file: /cvs/src/usr.bin/tmux/window-copy.c,v
retrieving revision 1.107
diff -u -p -r1.107 window-copy.c
--- window-copy.c       24 Apr 2014 09:14:43 -0000      1.107
+++ window-copy.c       9 May 2014 08:54:56 -0000
@@ -54,11 +54,12 @@ void        window_copy_update_cursor(struct wi
 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, 
        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, 
                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, 
                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_
 }
 
 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_pa
                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
        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 wind
                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);
 }
 




On Fri, May 09, 2014 at 02:13:04AM -0500, J Raynor wrote:
> Do you mean order everything by time when displaying the buffers in
> buffer-list and buffer-choose?  Or do you mean order everything by
> time in all cases, so that paste_get_top refers to the most recent
> buffer, even if it is !automatic?
> 
> If it's the former, that sounds fine.  If it's the latter, that would
> / mean running delete-buffer without args would delete the top buffer,
> even if it is !automatic.  I think that's fine since the user is still
> requesting the buffer be deleted (it isn't being aged away
> automatically).
> 
> I'm not sure that renaming a buffer should change its order, though.
> The order field seems akin to a file's mtime, and that doesn't change
> during a rename.
> 
> I'm in favor of ordering it all by time.
> 
> On Thu, May 8, 2014 at 3:20 AM, Nicholas Marriott
> <nicholas.marri...@gmail.com> wrote:
> > This has a couple more changes to fix some of these issues:
> >
> > - Make paste_rename silently replace any existing buffer. Also change
> >   paste_set to call paste_free_name to remove an existing buffer or
> >   it'll inherit stuff it doesn't want to.
> >
> > - paste_get_top and paste_free_top now ignore !automatic buffers.
> >
> > This makes it pretty consistent but I'm still not convinced we shouldn't
> > just order the whole lot by time... will people want to rename the top
> > buffer and still be able to immediately paste it with C-b ]? Or will
> > they only rename and then use it by name with C-b P or whatever?
> >
> > What do you think?
> >

------------------------------------------------------------------------------
Is your legacy SCM system holding you back? Join Perforce May 7 to find out:
&#149; 3 signs your SCM is hindering your productivity
&#149; Requirements for releasing software faster
&#149; Expert tips and advice for migrating your SCM now
http://p.sf.net/sfu/perforce
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to