diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index f67f7a0c..0942da9a 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -24,15 +24,15 @@ #include "tmux.h" /* - * Add or set a paste buffer. + * Add, set, or append to a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", - "b:", 1, 1, - CMD_BUFFER_USAGE " data", + "ab:", 1, 1, + "[-a] " CMD_BUFFER_USAGE " data", 0, NULL, cmd_set_buffer_exec @@ -41,35 +41,55 @@ const struct cmd_entry cmd_set_buffer_entry = { enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - u_int limit; - char *pdata, *cause; - size_t psize; - int buffer; + struct args *args = self->args; + struct paste_buffer *pb; + u_int limit; + char *pdata, *cause; + size_t psize, newsize; + int buffer; limit = options_get_number(&global_options, "buffer-limit"); - pdata = xstrdup(args->argv[0]); - psize = strlen(pdata); + psize = 0; + pdata = NULL; - if (!args_has(args, 'b')) { + pb = NULL; + buffer = -1; + + 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); + } + } else if (args_has(args, 'a')) { + pb = paste_get_top(&global_buffers); + if (pb != NULL) + buffer = 0; + } + + if (args_has(args, 'a') && pb != NULL) { + psize = pb->size; + pdata = xmalloc(psize); + memcpy(pdata, pb->data, psize); + } + + newsize = strlen(args->argv[0]); + + pdata = xrealloc(pdata, 1, psize + newsize); + memcpy(pdata + psize, args->argv[0], newsize); + psize += newsize; + + if (buffer == -1) paste_add(&global_buffers, pdata, psize, limit); - return (CMD_RETURN_NORMAL); - } - - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - cmdq_error(cmdq, "buffer %s", cause); - free(cause); - free(pdata); - return (CMD_RETURN_ERROR); - } - - if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { - cmdq_error(cmdq, "no buffer %d", buffer); - free(pdata); - return (CMD_RETURN_ERROR); - } + else + paste_replace(&global_buffers, buffer, pdata, psize); return (CMD_RETURN_NORMAL); } diff --git a/mode-key.c b/mode-key.c index 80a2464d..7f2b9471 100644 --- a/mode-key.c +++ b/mode-key.c @@ -100,6 +100,7 @@ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { /* Copy keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { + { MODEKEYCOPY_APPENDSELECTION, "append-selection" }, { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, @@ -272,6 +273,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { ':', 0, MODEKEYCOPY_GOTOLINE }, { '?', 0, MODEKEYCOPY_SEARCHUP }, + { 'A', 0, MODEKEYCOPY_APPENDSELECTION }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, diff --git a/paste.c b/paste.c index 946935a3..4ec87f35 100644 --- a/paste.c +++ b/paste.c @@ -172,7 +172,7 @@ paste_print(struct paste_buffer *pb, size_t width) /* Paste into a window pane, filtering '\n' according to separator. */ void -paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, +paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, const char *sep, int bracket) { const char *data = pb->data, *end = data + pb->size, *lf; diff --git a/tmux.1 b/tmux.1 index 81626faa..8fd63dc4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -851,6 +851,7 @@ option). 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 "Back to indentation" Ta "^" Ta "M-m" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" @@ -3543,12 +3544,16 @@ The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer +.Op Fl a .Op Fl b Ar buffer-index .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . +The +.Fl a +option appends to rather than overwriting the buffer. .It Xo Ic show-buffer .Op Fl b Ar buffer-index .Xc diff --git a/tmux.h b/tmux.h index fe4a697b..b30f2080 100644 --- a/tmux.h +++ b/tmux.h @@ -540,6 +540,7 @@ enum mode_key_cmd { MODEKEYCHOICE_UP, /* Copy keys. */ + MODEKEYCOPY_APPENDSELECTION, MODEKEYCOPY_BACKTOINDENTATION, MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, diff --git a/window-copy.c b/window-copy.c index 76c9c3ce..7d7f3a20 100644 --- a/window-copy.c +++ b/window-copy.c @@ -58,6 +58,7 @@ 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_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); @@ -414,6 +415,13 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + if (sess != NULL) { + window_copy_append_selection(wp, data->numprefix); + window_pane_reset_mode(wp); + return; + } + break; case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; @@ -1491,6 +1499,46 @@ window_copy_copy_selection(struct window_pane *wp, int idx) window_copy_copy_buffer(wp, idx, buf, len); } +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; + + buf = window_copy_get_selection(wp, &len); + if (buf == NULL) + return; + + if (options_get_number(&global_options, "set-clipboard")) { + screen_write_start(&ctx, wp, NULL); + screen_write_setselection(&ctx, buf, len); + screen_write_stop(&ctx); + } + + 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; + } + + pb = paste_get_index(&global_buffers, idx); + 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(&global_buffers, idx, buf, len) != 0) + free(buf); +} + void window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, u_int sx, u_int ex)