I've attached a new patch which drops the buffer_stickiness format and
includes changes to the man page.
On Thu, May 1, 2014 at 5:33 PM, Nicholas Marriott
<nicholas.marri...@gmail.com> wrote:
> Hi
>
> This looks good thanks, but I'd lose the buffer_stickiness format and just add
> buffer_name.
>
> (Even if we want it flag formats should be 0 or 1 so that the #{?,}
> conditional works.)
>
> Also this is going to need some manpage changes.
>
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index 4961463..bbaf70c 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -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,23 +191,15 @@ 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);
- 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);
}
}
diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c
index 18d2834..6b146fa 100644
--- a/cmd-choose-buffer.c
+++ b/cmd-choose-buffer.c
@@ -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);
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 e05fe09..9957de6 100644
--- a/cmd-delete-buffer.c
+++ b/cmd-delete-buffer.c
@@ -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);
}
diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c
index f3987dd..c5a6e32 100644
--- a/cmd-list-buffers.c
+++ b/cmd-list-buffers.c
@@ -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);
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 16ff40d..5bc5247 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -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;
-
- 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);
- }
- }
+ int ch, error, cwd, fd;
+
+ 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);
+ 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;
+ char *pdata, *cause;
+ const char *bufname = data;
+ 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);
}
diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c
index 4bc2cfd..f6c9d0a 100644
--- a/cmd-paste-buffer.c
+++ b/cmd-paste-buffer.c
@@ -35,7 +35,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
@@ -48,31 +48,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);
}
}
@@ -91,10 +82,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);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index b5ca62e..8dd52c9 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -58,10 +58,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')) {
@@ -70,16 +70,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);
}
}
diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index b0fc3fe..9b8057d 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -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] [-n new_buffer_name] " CMD_BUFFER_USAGE " 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);
}
diff --git a/format.c b/format.c
index 6f988b9..1f17b96 100644
--- a/format.c
+++ b/format.c
@@ -610,6 +610,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);
diff --git a/key-bindings.c b/key-bindings.c
index f725508..6a7d14c 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 0f29b6b..74c30eb 100644
--- a/paste.c
+++ b/paste.c
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/time.h>
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -29,122 +30,229 @@
* string!
*/
-ARRAY_DECL(, struct paste_buffer *) paste_buffers = ARRAY_INITIALIZER;
+u_int paste_unsticky_idx;
+u_int paste_num_unsticky;
+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;
+ 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)
+{
+ return (a->crtime < b->crtime ? -1 : a->crtime > b->crtime);
}
-/* 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_name_tree, &paste_by_name));
+ return (RB_NEXT(paste_name_tree, &paste_by_name, pb));
}
-/* Get an item by its index. */
+/* Get the most recent unsticky buffer */
struct paste_buffer *
-paste_get_index(u_int idx)
+paste_get_top(void)
{
- if (idx >= ARRAY_LENGTH(&paste_buffers))
- return (NULL);
- return (ARRAY_ITEM(&paste_buffers, idx));
+ return (RB_MAX(paste_time_tree, &paste_by_time));
}
-/* Free the top item on the stack. */
+/* Free the most recent unsticky 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. */
+/* Free an item 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);
+ if (pb->sticky == 0) {
+ RB_REMOVE(paste_time_tree, &paste_by_time, pb);
+ paste_num_unsticky--;
+ }
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 unsticky buffer, freeing the oldest unsticky 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;
+ u_int limit, next_crtime;
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");
+ while (paste_num_unsticky >= limit) {
+ pb = RB_MIN(paste_time_tree, &paste_by_time);
+ paste_free_name(pb->name);
+ }
+
+ next_crtime = 0;
+ if (paste_num_unsticky > 0) {
+ pb = RB_MAX(paste_time_tree, &paste_by_time);
+ next_crtime = pb->crtime + 1;
}
pb = xmalloc(sizeof *pb);
- ARRAY_INSERT(&paste_buffers, 0, pb);
+ pb->name = NULL;
+ do {
+ free(pb->name);
+ xasprintf(&pb->name, "buffer%04u", paste_unsticky_idx);
+ paste_unsticky_idx++;
+ } while (paste_get_name(pb->name) != NULL);
pb->data = data;
pb->size = size;
+ pb->sticky = 0;
+ pb->crtime = next_crtime;
+
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+ RB_INSERT(paste_time_tree, &paste_by_time, pb);
+ paste_num_unsticky++;
}
+/* Get an item by its name. */
+struct paste_buffer *
+paste_get_name(const char *name)
+{
+ struct paste_buffer pbfind;
+
+ if (name == NULL || *name == '\0')
+ return (NULL);
+
+ pbfind.name = (char*)name;
+ return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
+}
+
+/* Rename a paste buffer. */
+int
+paste_rename(const char *oldname, const char *newname, char **cause)
+{
+ struct paste_buffer *pb;
+
+ *cause = NULL;
+
+ if (oldname == NULL || *oldname == '\0') {
+ *cause = xstrdup("no buffer");
+ return (-1);
+ }
+
+ if (newname == NULL || *newname == '\0') {
+ *cause = xstrdup("new name is empty");
+ return (-1);
+ }
+
+ if ((pb = paste_get_name(oldname)) == NULL) {
+ xasprintf(cause, "no buffer %s", oldname);
+ return (-1);
+ }
+
+ if (paste_get_name(newname) != NULL) {
+ *cause = xstrdup("buffer with new name already exists");
+ return (-1);
+ }
+
+ RB_REMOVE(paste_name_tree, &paste_by_name, pb);
+ free(pb->name);
+
+ pb->name = xstrdup(newname);
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
+
+ if (pb->sticky == 0) {
+ RB_REMOVE(paste_time_tree, &paste_by_time, pb);
+ paste_num_unsticky--;
+ pb->sticky = 1;
+ }
+
+ 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 (idx >= ARRAY_LENGTH(&paste_buffers))
- return (-1);
+ if (name == NULL) {
+ paste_add(data, size);
+ return (0);
+ }
- pb = ARRAY_ITEM(&paste_buffers, idx);
- free(pb->data);
+ pb = paste_get_name(name);
+
+ if (pb != NULL) {
+ free(pb->data);
+ pb->data = data;
+ pb->size = size;
+ return (0);
+ }
+
+ if (*name == '\0') {
+ if (cause != NULL)
+ *cause = xstrdup("empty buffer name");
+ return (-1);
+ }
+
+ pb = xmalloc(sizeof *pb);
pb->data = data;
pb->size = size;
+ pb->name = xstrdup(name);
+ pb->sticky = 1;
+
+ RB_INSERT(paste_name_tree, &paste_by_name, pb);
return (0);
}
diff --git a/tmux.1 b/tmux.1
index c05eacf..5cfe518 100644
--- a/tmux.1
+++ b/tmux.1
@@ -271,6 +271,8 @@ Choose which buffer to paste interactively from a list.
List all key bindings.
.It D
Choose a client to detach.
+.It P
+Prompt for a buffer-name to paste.
.It \&[
Enter copy mode to copy text or view the history.
.It \&]
@@ -852,7 +854,9 @@ The following keys are supported as appropriate for the mode:
.Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent
.It Sy "Function" Ta Sy "vi" Ta Sy "emacs"
.It Li "Append selection" Ta "A" Ta ""
+.It Li "Append to named buffer" Ta "C-a" Ta ""
.It Li "Back to indentation" Ta "^" Ta "M-m"
+.It Li "Begin enter name of buffer" Ta \&" Ta ""
.It Li "Bottom of history" Ta "G" Ta "M-<"
.It Li "Clear selection" Ta "Escape" Ta "C-g"
.It Li "Copy selection" Ta "Enter" Ta "M-w"
@@ -934,9 +938,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
@@ -1094,7 +1095,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
@@ -3371,19 +3372,32 @@ is given, otherwise the active pane for the session attached to
.El
.Sh BUFFERS
.Nm
-maintains a stack of
-.Em paste buffers .
-Up to the value of the
+maintains a list of
+.Em paste buffers ,
+with each buffer having a name. If a buffer name is not specified at the time
+the buffer is created, the buffer is automatically given a name of the form
+bufferXXXX, where XXXX is a number. The first such buffer would be buffer0000,
+the 2nd buffer0001, etc. Up to the value of the
+.Ic buffer-limit
+option auto-named buffers are kept; when a new auto-named buffer is added,
+the oldest is removed.
+.Pp
+Buffers that are explicitly given a name when they are created are not subject
+to the
+.Ic buffer-limit .
+There can be an unlimited number of such buffers, and they must be explicitly
+deleted. If an auto-named buffer is renamed, it ceases to be subject to the
.Ic buffer-limit
-option are kept; when a new buffer is added, the buffer at the bottom of the
-stack is removed.
+setting, and must be explicitly deleted.
+.Pp
Buffers may be added using
.Ic copy-mode
or the
.Ic set-buffer
command, and pasted into a window using the
.Ic paste-buffer
-command.
+command. If a buffer command is entered and no buffer is specified, the most
+recently added auto-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
@@ -3404,7 +3418,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
@@ -3419,11 +3433,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 auto-named buffer if not specified.
.It Xo Ic list-buffers
.Op Fl F Ar format
.Xc
@@ -3435,7 +3449,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 )
@@ -3443,7 +3457,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
@@ -3452,7 +3466,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
@@ -3467,7 +3481,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 )
@@ -3478,7 +3492,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 )
@@ -3486,9 +3501,17 @@ Set the contents of the specified buffer to
.Ar data .
The
.Fl a
-option appends to rather than overwriting the buffer.
+option appends to rather than overwriting the buffer. The
+.Fl n
+option renames the buffer to
+.Ar new-buffer-name .
+Do not specify
+.Ar data
+with the
+.Fl n
+flag.
.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.
diff --git a/tmux.h b/tmux.h
index fde94af..fa1c7b3 100644
--- a/tmux.h
+++ b/tmux.h
@@ -82,7 +82,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 \
@@ -115,7 +115,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 \
@@ -496,6 +497,7 @@ enum mode_key_cmd {
MODEKEYEDIT_DELETEWORD,
MODEKEYEDIT_ENDOFLINE,
MODEKEYEDIT_ENTER,
+ MODEKEYEDIT_ENTERAPPEND,
MODEKEYEDIT_HISTORYDOWN,
MODEKEYEDIT_HISTORYUP,
MODEKEYEDIT_NEXTSPACE,
@@ -579,6 +581,7 @@ enum mode_key_cmd {
MODEKEYCOPY_SEARCHREVERSE,
MODEKEYCOPY_SEARCHUP,
MODEKEYCOPY_SELECTLINE,
+ MODEKEYCOPY_STARTNAMEDBUFFER,
MODEKEYCOPY_STARTNUMBERPREFIX,
MODEKEYCOPY_STARTOFLINE,
MODEKEYCOPY_STARTSELECTION,
@@ -1032,7 +1035,13 @@ struct layout_cell {
/* Paste buffer. */
struct paste_buffer {
char *data;
+ char *name;
+ u_char sticky;
size_t size;
+ u_int crtime;
+
+ RB_ENTRY(paste_buffer) name_entry;
+ RB_ENTRY(paste_buffer) time_entry;
};
/* Environment variable. */
@@ -1493,7 +1502,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;
@@ -1705,13 +1714,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);
diff --git a/window-copy.c b/window-copy.c
index 508001d..38364d3 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -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_buffer(
+ struct window_pane *, const char *, 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);
+ 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);
@@ -94,6 +95,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,
@@ -417,7 +419,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 +545,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 +556,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;
}
@@ -676,6 +678,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 +714,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 +822,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, data->inputstr);
+ *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 +834,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, data->inputstr);
+ *data->inputstr = '\0';
+ window_pane_reset_mode(wp);
+ return (0);
+ default:
+ break;
+ }
+ break;
case MODEKEY_OTHER:
if (key < 32 || key > 126)
break;
@@ -918,7 +942,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,10 +1476,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)
+window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
+ size_t len)
{
- u_int limit;
- struct screen_write_ctx ctx;
+ struct screen_write_ctx ctx;
if (options_get_number(&global_options, "set-clipboard")) {
screen_write_start(&ctx, wp, NULL);
@@ -1463,16 +1487,15 @@ 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);
+
+ return;
}
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 +1509,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 +1522,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 +1543,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);
}
------------------------------------------------------------------------------
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE
Instantly run your Selenium tests across 300+ browser/OS combos. Get
unparalleled scalability from the best Selenium testing platform available.
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users