4 * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
29 static struct screen
*window_buffer_init(struct window_mode_entry
*,
30 struct cmd_find_state
*, struct args
*);
31 static void window_buffer_free(struct window_mode_entry
*);
32 static void window_buffer_resize(struct window_mode_entry
*, u_int
,
34 static void window_buffer_update(struct window_mode_entry
*);
35 static void window_buffer_key(struct window_mode_entry
*,
36 struct client
*, struct session
*,
37 struct winlink
*, key_code
, struct mouse_event
*);
39 #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'"
41 #define WINDOW_BUFFER_DEFAULT_FORMAT \
42 "#{t/p:buffer_created}: #{buffer_sample}"
44 #define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
45 "#{?#{e|<:#{line},10}," \
48 "#{?#{e|<:#{line},36}," \
49 "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
55 static const struct menu_item window_buffer_menu_items
[] = {
56 { "Paste", 'p', NULL
},
57 { "Paste Tagged", 'P', NULL
},
58 { "", KEYC_NONE
, NULL
},
60 { "Tag All", '\024', NULL
},
61 { "Tag None", 'T', NULL
},
62 { "", KEYC_NONE
, NULL
},
63 { "Delete", 'd', NULL
},
64 { "Delete Tagged", 'D', NULL
},
65 { "", KEYC_NONE
, NULL
},
66 { "Cancel", 'q', NULL
},
68 { NULL
, KEYC_NONE
, NULL
}
71 const struct window_mode window_buffer_mode
= {
72 .name
= "buffer-mode",
73 .default_format
= WINDOW_BUFFER_DEFAULT_FORMAT
,
75 .init
= window_buffer_init
,
76 .free
= window_buffer_free
,
77 .resize
= window_buffer_resize
,
78 .update
= window_buffer_update
,
79 .key
= window_buffer_key
,
82 enum window_buffer_sort_type
{
83 WINDOW_BUFFER_BY_TIME
,
84 WINDOW_BUFFER_BY_NAME
,
85 WINDOW_BUFFER_BY_SIZE
,
87 static const char *window_buffer_sort_list
[] = {
92 static struct mode_tree_sort_criteria
*window_buffer_sort
;
94 struct window_buffer_itemdata
{
100 struct window_buffer_modedata
{
101 struct window_pane
*wp
;
102 struct cmd_find_state fs
;
104 struct mode_tree_data
*data
;
109 struct window_buffer_itemdata
**item_list
;
113 struct window_buffer_editdata
{
116 struct paste_buffer
*pb
;
119 static struct window_buffer_itemdata
*
120 window_buffer_add_item(struct window_buffer_modedata
*data
)
122 struct window_buffer_itemdata
*item
;
124 data
->item_list
= xreallocarray(data
->item_list
, data
->item_size
+ 1,
125 sizeof *data
->item_list
);
126 item
= data
->item_list
[data
->item_size
++] = xcalloc(1, sizeof *item
);
131 window_buffer_free_item(struct window_buffer_itemdata
*item
)
133 free((void *)item
->name
);
138 window_buffer_cmp(const void *a0
, const void *b0
)
140 const struct window_buffer_itemdata
*const *a
= a0
;
141 const struct window_buffer_itemdata
*const *b
= b0
;
144 if (window_buffer_sort
->field
== WINDOW_BUFFER_BY_TIME
)
145 result
= (*b
)->order
- (*a
)->order
;
146 else if (window_buffer_sort
->field
== WINDOW_BUFFER_BY_SIZE
)
147 result
= (*b
)->size
- (*a
)->size
;
149 /* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
151 result
= strcmp((*a
)->name
, (*b
)->name
);
153 if (window_buffer_sort
->reversed
)
159 window_buffer_build(void *modedata
, struct mode_tree_sort_criteria
*sort_crit
,
160 __unused
uint64_t *tag
, const char *filter
)
162 struct window_buffer_modedata
*data
= modedata
;
163 struct window_buffer_itemdata
*item
;
165 struct paste_buffer
*pb
;
167 struct format_tree
*ft
;
168 struct session
*s
= NULL
;
169 struct winlink
*wl
= NULL
;
170 struct window_pane
*wp
= NULL
;
172 for (i
= 0; i
< data
->item_size
; i
++)
173 window_buffer_free_item(data
->item_list
[i
]);
174 free(data
->item_list
);
175 data
->item_list
= NULL
;
179 while ((pb
= paste_walk(pb
)) != NULL
) {
180 item
= window_buffer_add_item(data
);
181 item
->name
= xstrdup(paste_buffer_name(pb
));
182 paste_buffer_data(pb
, &item
->size
);
183 item
->order
= paste_buffer_order(pb
);
186 window_buffer_sort
= sort_crit
;
187 qsort(data
->item_list
, data
->item_size
, sizeof *data
->item_list
,
190 if (cmd_find_valid_state(&data
->fs
)) {
196 for (i
= 0; i
< data
->item_size
; i
++) {
197 item
= data
->item_list
[i
];
199 pb
= paste_get_name(item
->name
);
202 ft
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
203 format_defaults(ft
, NULL
, s
, wl
, wp
);
204 format_defaults_paste_buffer(ft
, pb
);
206 if (filter
!= NULL
) {
207 cp
= format_expand(ft
, filter
);
208 if (!format_true(cp
)) {
216 text
= format_expand(ft
, data
->format
);
217 mode_tree_add(data
->data
, NULL
, item
, item
->order
, item
->name
,
227 window_buffer_draw(__unused
void *modedata
, void *itemdata
,
228 struct screen_write_ctx
*ctx
, u_int sx
, u_int sy
)
230 struct window_buffer_itemdata
*item
= itemdata
;
231 struct paste_buffer
*pb
;
232 const char *pdata
, *start
, *end
;
235 u_int i
, cx
= ctx
->s
->cx
, cy
= ctx
->s
->cy
;
237 pb
= paste_get_name(item
->name
);
241 pdata
= end
= paste_buffer_data(pb
, &psize
);
242 for (i
= 0; i
< sy
; i
++) {
244 while (end
!= pdata
+ psize
&& *end
!= '\n')
246 buf
= xreallocarray(buf
, 4, end
- start
+ 1);
247 utf8_strvis(buf
, start
, end
- start
,
248 VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
);
250 screen_write_cursormove(ctx
, cx
, cy
+ i
, 0);
251 screen_write_nputs(ctx
, sx
, &grid_default_cell
, "%s",
255 if (end
== pdata
+ psize
)
263 window_buffer_search(__unused
void *modedata
, void *itemdata
, const char *ss
)
265 struct window_buffer_itemdata
*item
= itemdata
;
266 struct paste_buffer
*pb
;
270 if ((pb
= paste_get_name(item
->name
)) == NULL
)
272 if (strstr(item
->name
, ss
) != NULL
)
274 bufdata
= paste_buffer_data(pb
, &bufsize
);
275 return (memmem(bufdata
, bufsize
, ss
, strlen(ss
)) != NULL
);
279 window_buffer_menu(void *modedata
, struct client
*c
, key_code key
)
281 struct window_buffer_modedata
*data
= modedata
;
282 struct window_pane
*wp
= data
->wp
;
283 struct window_mode_entry
*wme
;
285 wme
= TAILQ_FIRST(&wp
->modes
);
286 if (wme
== NULL
|| wme
->data
!= modedata
)
288 window_buffer_key(wme
, c
, NULL
, NULL
, key
, NULL
);
292 window_buffer_get_key(void *modedata
, void *itemdata
, u_int line
)
294 struct window_buffer_modedata
*data
= modedata
;
295 struct window_buffer_itemdata
*item
= itemdata
;
296 struct format_tree
*ft
;
297 struct session
*s
= NULL
;
298 struct winlink
*wl
= NULL
;
299 struct window_pane
*wp
= NULL
;
300 struct paste_buffer
*pb
;
304 if (cmd_find_valid_state(&data
->fs
)) {
309 pb
= paste_get_name(item
->name
);
313 ft
= format_create(NULL
, NULL
, FORMAT_NONE
, 0);
314 format_defaults(ft
, NULL
, NULL
, 0, NULL
);
315 format_defaults(ft
, NULL
, s
, wl
, wp
);
316 format_defaults_paste_buffer(ft
, pb
);
317 format_add(ft
, "line", "%u", line
);
319 expanded
= format_expand(ft
, data
->key_format
);
320 key
= key_string_lookup_string(expanded
);
326 static struct screen
*
327 window_buffer_init(struct window_mode_entry
*wme
, struct cmd_find_state
*fs
,
330 struct window_pane
*wp
= wme
->wp
;
331 struct window_buffer_modedata
*data
;
334 wme
->data
= data
= xcalloc(1, sizeof *data
);
336 cmd_find_copy_state(&data
->fs
, fs
);
338 if (args
== NULL
|| !args_has(args
, 'F'))
339 data
->format
= xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT
);
341 data
->format
= xstrdup(args_get(args
, 'F'));
342 if (args
== NULL
|| !args_has(args
, 'K'))
343 data
->key_format
= xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT
);
345 data
->key_format
= xstrdup(args_get(args
, 'K'));
346 if (args
== NULL
|| args_count(args
) == 0)
347 data
->command
= xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND
);
349 data
->command
= xstrdup(args_string(args
, 0));
351 data
->data
= mode_tree_start(wp
, args
, window_buffer_build
,
352 window_buffer_draw
, window_buffer_search
, window_buffer_menu
, NULL
,
353 window_buffer_get_key
, data
, window_buffer_menu_items
,
354 window_buffer_sort_list
, nitems(window_buffer_sort_list
), &s
);
355 mode_tree_zoom(data
->data
, args
);
357 mode_tree_build(data
->data
);
358 mode_tree_draw(data
->data
);
364 window_buffer_free(struct window_mode_entry
*wme
)
366 struct window_buffer_modedata
*data
= wme
->data
;
372 mode_tree_free(data
->data
);
374 for (i
= 0; i
< data
->item_size
; i
++)
375 window_buffer_free_item(data
->item_list
[i
]);
376 free(data
->item_list
);
379 free(data
->key_format
);
386 window_buffer_resize(struct window_mode_entry
*wme
, u_int sx
, u_int sy
)
388 struct window_buffer_modedata
*data
= wme
->data
;
390 mode_tree_resize(data
->data
, sx
, sy
);
394 window_buffer_update(struct window_mode_entry
*wme
)
396 struct window_buffer_modedata
*data
= wme
->data
;
398 mode_tree_build(data
->data
);
399 mode_tree_draw(data
->data
);
400 data
->wp
->flags
|= PANE_REDRAW
;
404 window_buffer_do_delete(void *modedata
, void *itemdata
,
405 __unused
struct client
*c
, __unused key_code key
)
407 struct window_buffer_modedata
*data
= modedata
;
408 struct window_buffer_itemdata
*item
= itemdata
;
409 struct paste_buffer
*pb
;
411 if (item
== mode_tree_get_current(data
->data
))
412 mode_tree_down(data
->data
, 0);
413 if ((pb
= paste_get_name(item
->name
)) != NULL
)
418 window_buffer_do_paste(void *modedata
, void *itemdata
, struct client
*c
,
419 __unused key_code key
)
421 struct window_buffer_modedata
*data
= modedata
;
422 struct window_buffer_itemdata
*item
= itemdata
;
424 if (paste_get_name(item
->name
) != NULL
)
425 mode_tree_run_command(c
, NULL
, data
->command
, item
->name
);
429 window_buffer_finish_edit(struct window_buffer_editdata
*ed
)
436 window_buffer_edit_close_cb(char *buf
, size_t len
, void *arg
)
438 struct window_buffer_editdata
*ed
= arg
;
441 struct paste_buffer
*pb
;
442 struct window_pane
*wp
;
443 struct window_buffer_modedata
*data
;
444 struct window_mode_entry
*wme
;
446 if (buf
== NULL
|| len
== 0) {
447 window_buffer_finish_edit(ed
);
451 pb
= paste_get_name(ed
->name
);
452 if (pb
== NULL
|| pb
!= ed
->pb
) {
453 window_buffer_finish_edit(ed
);
457 oldbuf
= paste_buffer_data(pb
, &oldlen
);
458 if (oldlen
!= '\0' &&
459 oldbuf
[oldlen
- 1] != '\n' &&
460 buf
[len
- 1] == '\n')
463 paste_replace(pb
, buf
, len
);
465 wp
= window_pane_find_by_id(ed
->wp_id
);
467 wme
= TAILQ_FIRST(&wp
->modes
);
468 if (wme
->mode
== &window_buffer_mode
) {
470 mode_tree_build(data
->data
);
471 mode_tree_draw(data
->data
);
473 wp
->flags
|= PANE_REDRAW
;
475 window_buffer_finish_edit(ed
);
479 window_buffer_start_edit(struct window_buffer_modedata
*data
,
480 struct window_buffer_itemdata
*item
, struct client
*c
)
482 struct paste_buffer
*pb
;
485 struct window_buffer_editdata
*ed
;
487 if ((pb
= paste_get_name(item
->name
)) == NULL
)
489 buf
= paste_buffer_data(pb
, &len
);
491 ed
= xcalloc(1, sizeof *ed
);
492 ed
->wp_id
= data
->wp
->id
;
493 ed
->name
= xstrdup(paste_buffer_name(pb
));
496 if (popup_editor(c
, buf
, len
, window_buffer_edit_close_cb
, ed
) != 0)
497 window_buffer_finish_edit(ed
);
501 window_buffer_key(struct window_mode_entry
*wme
, struct client
*c
,
502 __unused
struct session
*s
, __unused
struct winlink
*wl
, key_code key
,
503 struct mouse_event
*m
)
505 struct window_pane
*wp
= wme
->wp
;
506 struct window_buffer_modedata
*data
= wme
->data
;
507 struct mode_tree_data
*mtd
= data
->data
;
508 struct window_buffer_itemdata
*item
;
511 if (paste_get_top(NULL
) == NULL
) {
516 finished
= mode_tree_key(mtd
, c
, &key
, m
, NULL
, NULL
);
519 item
= mode_tree_get_current(mtd
);
520 window_buffer_start_edit(data
, item
, c
);
523 item
= mode_tree_get_current(mtd
);
524 window_buffer_do_delete(data
, item
, c
, key
);
525 mode_tree_build(mtd
);
528 mode_tree_each_tagged(mtd
, window_buffer_do_delete
, c
, key
, 0);
529 mode_tree_build(mtd
);
532 mode_tree_each_tagged(mtd
, window_buffer_do_paste
, c
, key
, 0);
537 item
= mode_tree_get_current(mtd
);
538 window_buffer_do_paste(data
, item
, c
, key
);
544 if (finished
|| paste_get_top(NULL
) == NULL
)
545 window_pane_reset_mode(wp
);
548 wp
->flags
|= PANE_REDRAW
;