mirror of
https://github.com/tmux/tmux.git
synced 2026-09-14 09:11:59 +00:00
- Added top-level commands:
- select-output
- copy-output
- copy-pipe-output
- pipe-output
- open-output
- open-selection
- Added -a support to operate on the entire pane/copy-mode buffer, including panes without OSC 133 markers.
- Output extraction now supports unfinished commands with C but no D, ending at the current pane cursor.
- Top-level commands use the most recent OSC 133 output when outside copy mode.
- select-output enters copy mode and selects the output; other top-level operations use a temporary copy-mode snapshot.
- Added copy-mode bindings to open the selection when present, otherwise the current command output:
- emacs: e
- vi: M-e
- Added copy-mode bidings to select the output of command cursor is on:
- emacs copy mode: C-o → select-output
- vi copy mode: M-o → select-output
- Updated command registration, build sources, and tmux.1 documentation.
- Added/extended regress/output-commands.sh.
- Verified with make, regress/collapse-output.sh, and regress/output-commands.sh.
This commit is contained in:
@@ -98,6 +98,7 @@ dist_tmux_SOURCES = \
|
||||
cmd-command-prompt.c \
|
||||
cmd-confirm-before.c \
|
||||
cmd-copy-mode.c \
|
||||
cmd-output.c \
|
||||
cmd-detach-client.c \
|
||||
cmd-display-menu.c \
|
||||
cmd-display-message.c \
|
||||
|
||||
113
cmd-output.c
Normal file
113
cmd-output.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static enum cmd_retval cmd_output_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
const struct cmd_entry cmd_select_output_entry = {
|
||||
.name = "select-output",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "at:", 0, 0, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_copy_output_entry = {
|
||||
.name = "copy-output",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "aCPt:", 0, 1, NULL },
|
||||
.usage = "[-aCP] " CMD_TARGET_PANE_USAGE " [prefix]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_copy_pipe_output_entry = {
|
||||
.name = "copy-pipe-output",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "aCPt:", 0, 2, NULL },
|
||||
.usage = "[-aCP] " CMD_TARGET_PANE_USAGE " [command] [prefix]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_pipe_output_entry = {
|
||||
.name = "pipe-output",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "at:", 0, 1, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_open_output_entry = {
|
||||
.name = "open-output",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "at:", 0, 1, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_open_selection_entry = {
|
||||
.name = "open-selection",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "t:", 0, 1, NULL },
|
||||
.usage = CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_output_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_output_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct client *c = cmdq_get_client(item);
|
||||
|
||||
window_copy_output(target->wp, c, target->s, target->wl, item,
|
||||
cmd_get_entry(self)->name, args);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
12
cmd.c
12
cmd.c
@@ -39,6 +39,8 @@ extern const struct cmd_entry cmd_clear_prompt_history_entry;
|
||||
extern const struct cmd_entry cmd_clock_mode_entry;
|
||||
extern const struct cmd_entry cmd_command_prompt_entry;
|
||||
extern const struct cmd_entry cmd_confirm_before_entry;
|
||||
extern const struct cmd_entry cmd_copy_output_entry;
|
||||
extern const struct cmd_entry cmd_copy_pipe_output_entry;
|
||||
extern const struct cmd_entry cmd_copy_mode_entry;
|
||||
extern const struct cmd_entry cmd_customize_mode_entry;
|
||||
extern const struct cmd_entry cmd_delete_buffer_entry;
|
||||
@@ -76,8 +78,11 @@ extern const struct cmd_entry cmd_new_session_entry;
|
||||
extern const struct cmd_entry cmd_new_window_entry;
|
||||
extern const struct cmd_entry cmd_next_layout_entry;
|
||||
extern const struct cmd_entry cmd_next_window_entry;
|
||||
extern const struct cmd_entry cmd_open_output_entry;
|
||||
extern const struct cmd_entry cmd_open_selection_entry;
|
||||
extern const struct cmd_entry cmd_paste_buffer_entry;
|
||||
extern const struct cmd_entry cmd_pipe_pane_entry;
|
||||
extern const struct cmd_entry cmd_pipe_output_entry;
|
||||
extern const struct cmd_entry cmd_previous_layout_entry;
|
||||
extern const struct cmd_entry cmd_previous_window_entry;
|
||||
extern const struct cmd_entry cmd_refresh_client_entry;
|
||||
@@ -91,6 +96,7 @@ extern const struct cmd_entry cmd_rotate_window_entry;
|
||||
extern const struct cmd_entry cmd_run_shell_entry;
|
||||
extern const struct cmd_entry cmd_save_buffer_entry;
|
||||
extern const struct cmd_entry cmd_select_layout_entry;
|
||||
extern const struct cmd_entry cmd_select_output_entry;
|
||||
extern const struct cmd_entry cmd_select_pane_entry;
|
||||
extern const struct cmd_entry cmd_select_window_entry;
|
||||
extern const struct cmd_entry cmd_send_keys_entry;
|
||||
@@ -134,6 +140,8 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_command_prompt_entry,
|
||||
&cmd_confirm_before_entry,
|
||||
&cmd_copy_mode_entry,
|
||||
&cmd_copy_output_entry,
|
||||
&cmd_copy_pipe_output_entry,
|
||||
&cmd_customize_mode_entry,
|
||||
&cmd_delete_buffer_entry,
|
||||
&cmd_detach_client_entry,
|
||||
@@ -170,8 +178,11 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_new_window_entry,
|
||||
&cmd_next_layout_entry,
|
||||
&cmd_next_window_entry,
|
||||
&cmd_open_output_entry,
|
||||
&cmd_open_selection_entry,
|
||||
&cmd_paste_buffer_entry,
|
||||
&cmd_pipe_pane_entry,
|
||||
&cmd_pipe_output_entry,
|
||||
&cmd_previous_layout_entry,
|
||||
&cmd_previous_window_entry,
|
||||
&cmd_refresh_client_entry,
|
||||
@@ -185,6 +196,7 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_run_shell_entry,
|
||||
&cmd_save_buffer_entry,
|
||||
&cmd_select_layout_entry,
|
||||
&cmd_select_output_entry,
|
||||
&cmd_select_pane_entry,
|
||||
&cmd_select_window_entry,
|
||||
&cmd_send_keys_entry,
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
" ''" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_line_numbers,Line Numbers Off,Line Numbers On},}' 'L' {send -X line-numbers-toggle}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_fold_view,Fold View Off,Fold View On},}' 'O' {send -X fold-view-toggle}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},Edit,}' 'e' {if -F '#{selection_present}' {send -X open-selection} {send -X open-output}}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?refresh_active,Refresh Off,Refresh On},}' 'r' {send -X refresh-toggle}" \
|
||||
" ''" \
|
||||
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
|
||||
@@ -576,6 +577,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
||||
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
||||
"bind -Tcopy-mode C-n { send -X cursor-down }",
|
||||
"bind -Tcopy-mode C-o { send -X select-output }",
|
||||
"bind -Tcopy-mode C-p { send -X cursor-up }",
|
||||
"bind -Tcopy-mode C-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
|
||||
@@ -596,6 +598,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode R { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode X { send -X set-mark }",
|
||||
"bind -Tcopy-mode e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode n { send -X search-again }",
|
||||
@@ -693,6 +696,8 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi K { send -X scroll-up }",
|
||||
"bind -Tcopy-mode-vi L { send -X bottom-line }",
|
||||
"bind -Tcopy-mode-vi M { send -X middle-line }",
|
||||
"bind -Tcopy-mode-vi M-e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode-vi M-o { send -X select-output }",
|
||||
"bind -Tcopy-mode-vi N { send -X search-reverse }",
|
||||
"bind -Tcopy-mode-vi O { send -X fold-view-toggle }",
|
||||
"bind -Tcopy-mode-vi P { send -X toggle-position }",
|
||||
|
||||
106
regress/output-commands.sh
Normal file
106
regress/output-commands.sh
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/bin/sh
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -L${TEST_SOCKET:-testO$$} -f/dev/null"
|
||||
OUT=/tmp/tmux-output-commands-$$
|
||||
EXPECTED=$(printf 'one\ntwo')
|
||||
|
||||
cleanup()
|
||||
{
|
||||
$TMUX kill-server 2>/dev/null
|
||||
rm -f "$OUT"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\ntwo\\n\\033]133;D;0\\007separator\\n\\033]133;A\\007p\\$ \\033]133;B\\007broken\\n\\033]133;C\\007unfinished\"; exec sleep 100'" || exit 1
|
||||
sleep 1
|
||||
|
||||
$TMUX copy-output || exit 1
|
||||
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||
|
||||
$TMUX pipe-output "wc -c >$OUT" || exit 1
|
||||
sleep 1
|
||||
[ "$(cat "$OUT")" = 10 ] || exit 1
|
||||
|
||||
$TMUX copy-pipe-output "wc -c >$OUT" output || exit 1
|
||||
sleep 1
|
||||
[ "$(cat "$OUT")" = 10 ] || exit 1
|
||||
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||
|
||||
$TMUX select-output || exit 1
|
||||
[ "$($TMUX display-message -p '#{pane_in_mode}')" = 1 ] || exit 1
|
||||
$TMUX send-keys -X copy-selection || exit 1
|
||||
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||
$TMUX send-keys -X cancel || exit 1
|
||||
|
||||
$TMUX copy-mode -U || exit 1
|
||||
$TMUX send-keys -X search-backward one || exit 1
|
||||
$TMUX send-keys -X copy-output || exit 1
|
||||
copied=$($TMUX show-buffer)
|
||||
[ "$copied" = "$EXPECTED" ] || exit 1
|
||||
|
||||
$TMUX send-keys -X pipe-output "wc -l >$OUT" || exit 1
|
||||
sleep 1
|
||||
[ "$(cat "$OUT")" = 2 ] || exit 1
|
||||
|
||||
$TMUX send-keys -X copy-pipe-output "wc -l >$OUT" output || exit 1
|
||||
sleep 1
|
||||
[ "$(cat "$OUT")" = 2 ] || exit 1
|
||||
copied=$($TMUX show-buffer)
|
||||
[ "$copied" = "$EXPECTED" ] || exit 1
|
||||
$TMUX send-keys -X cancel || exit 1
|
||||
|
||||
$TMUX copy-mode -c || exit 1
|
||||
$TMUX send-keys -X search-backward echo || exit 1
|
||||
$TMUX send-keys -X select-output || exit 1
|
||||
$TMUX send-keys -X copy-selection || exit 1
|
||||
selected=$($TMUX show-buffer)
|
||||
[ "$selected" = "$EXPECTED" ] || exit 1
|
||||
$TMUX send-keys -X cancel || exit 1
|
||||
|
||||
$TMUX set-buffer -b keep unchanged || exit 1
|
||||
$TMUX copy-mode -U || exit 1
|
||||
$TMUX send-keys -X search-backward unfinished || exit 1
|
||||
$TMUX send-keys -X copy-output || exit 1
|
||||
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
|
||||
$TMUX send-keys -X cancel || exit 1
|
||||
|
||||
$TMUX new-window -d -n plain "printf 'alpha\\nbeta\\n'; exec sleep 100" || exit 1
|
||||
sleep 1
|
||||
$TMUX copy-output -a -t :plain || exit 1
|
||||
all=$($TMUX show-buffer)
|
||||
case "$all" in
|
||||
*alpha*beta*) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
|
||||
$TMUX set-buffer -b keep unchanged || exit 1
|
||||
$TMUX copy-output -t :plain || exit 1
|
||||
[ "$($TMUX show-buffer -b keep)" = unchanged ] || exit 1
|
||||
|
||||
$TMUX select-output -t :plain || exit 1
|
||||
[ "$($TMUX display-message -p -t :plain.0 '#{pane_in_mode}')" = 0 ] || exit 1
|
||||
$TMUX select-output -a -t :plain || exit 1
|
||||
[ "$($TMUX display-message -p -t :plain.0 '#{pane_in_mode}')" = 1 ] || exit 1
|
||||
$TMUX send-keys -t :plain.0 -X copy-selection || exit 1
|
||||
all=$($TMUX show-buffer)
|
||||
case "$all" in
|
||||
*alpha*beta*) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||
|
||||
$TMUX copy-mode -t :plain || exit 1
|
||||
$TMUX send-keys -t :plain.0 -X copy-output -a || exit 1
|
||||
all=$($TMUX show-buffer)
|
||||
case "$all" in
|
||||
*alpha*beta*) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
$TMUX send-keys -t :plain.0 -X cancel || exit 1
|
||||
|
||||
exit 0
|
||||
6
spawn.c
6
spawn.c
@@ -701,7 +701,7 @@ spawn_editor_finish(struct window_pane *wp)
|
||||
|
||||
struct spawn_editor_state *
|
||||
spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
spawn_finish_edit_cb cb, void *arg)
|
||||
const char *editor, spawn_finish_edit_cb cb, void *arg)
|
||||
{
|
||||
struct spawn_editor_state *es;
|
||||
struct spawn_context sc = { 0 };
|
||||
@@ -715,13 +715,13 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
FILE *f;
|
||||
char *cmd, *cause = NULL;
|
||||
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
||||
const char *editor;
|
||||
int fd;
|
||||
|
||||
if (w->modal != NULL)
|
||||
return (NULL);
|
||||
|
||||
editor = options_get_string(global_options, "editor");
|
||||
if (editor == NULL)
|
||||
editor = options_get_string(global_options, "editor");
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
return (NULL);
|
||||
|
||||
132
tmux.1
132
tmux.1
@@ -2004,6 +2004,25 @@ Same as
|
||||
.Ic copy\-pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic copy\-pipe\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar command
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output, pipe it to
|
||||
.Ar command ,
|
||||
and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-selection
|
||||
.Op Fl CP
|
||||
.Op Ar prefix
|
||||
@@ -2243,6 +2262,32 @@ but treat all non-whitespace characters as part of a word.
|
||||
.Xc
|
||||
Switch at which end of the selection the cursor sits.
|
||||
.It Xo
|
||||
.Ic open\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Open the current OSC 133 command output in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo
|
||||
.Ic open\-selection
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Open the current selection in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
In emacs mode,
|
||||
.Ql e ,
|
||||
and in vi mode,
|
||||
.Ql M\-e ,
|
||||
opens the selection if one is present, otherwise the command output.
|
||||
.It Xo
|
||||
.Ic page\-down
|
||||
(vi: C\-f)
|
||||
(emacs: PageDown)
|
||||
@@ -2283,6 +2328,14 @@ Same as
|
||||
.Ic pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic pipe\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the current OSC 133 command output to
|
||||
.Ar command
|
||||
and clear the selection.
|
||||
.It Xo
|
||||
.Ic previous\-matching\-bracket
|
||||
(emacs: M\-C\-b)
|
||||
.Xc
|
||||
@@ -2477,6 +2530,16 @@ backward becomes forward).
|
||||
.Xc
|
||||
Select the current line.
|
||||
.It Xo
|
||||
.Ic select\-output
|
||||
.Op Fl a
|
||||
.Xc
|
||||
Select the current OSC 133 command output.
|
||||
If it is collapsed, it is expanded first.
|
||||
The default emacs binding is
|
||||
.Ql C\-o
|
||||
and the default vi binding is
|
||||
.Ql M\-o .
|
||||
.It Xo
|
||||
.Ic select\-word
|
||||
.Xc
|
||||
Select the current word.
|
||||
@@ -2515,6 +2578,12 @@ Toggle the visibility of the position indicator in the top right.
|
||||
Move to the top line.
|
||||
.El
|
||||
.Pp
|
||||
The output commands use OSC 133 output text from the C marker to the D marker.
|
||||
If there is no D marker, the output ends at the end of the copy-mode buffer.
|
||||
With
|
||||
.Fl a ,
|
||||
they use the entire copy-mode buffer instead.
|
||||
.Pp
|
||||
The search commands come in several varieties:
|
||||
.Ql search\-forward
|
||||
and
|
||||
@@ -2560,6 +2629,15 @@ Finding the beginning of command output requires the shell to emit an escape
|
||||
sequence (\e033]133;C\e033\e\e) to tell tmux where the output begins.
|
||||
If the shell does not send these escape sequences, these commands do nothing.
|
||||
.Pp
|
||||
The output commands operate on text from an OSC 133
|
||||
.Ql C
|
||||
marker up to its matching
|
||||
.Ql D
|
||||
marker, or the current end of the buffer if there is no
|
||||
.Ql D
|
||||
marker.
|
||||
They do nothing if the cursor is not associated with an output range.
|
||||
.Pp
|
||||
Copy commands may take an optional buffer prefix argument which is used
|
||||
to generate the buffer name (the default is
|
||||
.Ql buffer
|
||||
@@ -2675,6 +2753,60 @@ bind PageDown copy\-mode \-ed
|
||||
kills the pane when the mode is exited.
|
||||
.El
|
||||
.Pp
|
||||
The following commands operate on OSC 133 command output. In copy mode, they
|
||||
use the output on the cursor line; otherwise, they use the most recent output
|
||||
in the target pane. If an output has a C marker but no D marker, it ends at the
|
||||
end of the pane buffer. If there is no OSC 133 output, they do nothing unless
|
||||
.Fl a
|
||||
is given, which uses the entire buffer.
|
||||
.Bl -tag -width Ds
|
||||
.It Xo Ic select\-output
|
||||
.Op Fl a
|
||||
.Op Fl t Ar target\-pane
|
||||
.Xc
|
||||
Enter copy mode if necessary and select the output.
|
||||
.It Xo Ic copy\-output
|
||||
.Op Fl aCP
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the output to a paste buffer.
|
||||
.It Xo Ic copy\-pipe\-output
|
||||
.Op Fl aCP
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar command
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the output to a paste buffer and pipe it to
|
||||
.Ar command .
|
||||
.It Xo Ic pipe\-output
|
||||
.Op Fl a
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the output to
|
||||
.Ar command .
|
||||
.It Xo Ic open\-output
|
||||
.Op Fl a
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Open the output in an editor. If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo Ic open\-selection
|
||||
.Op Fl t Ar target\-pane
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Open the current copy-mode selection in an editor. If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.El
|
||||
.Pp
|
||||
A number of preset arrangements of panes are available, these are called
|
||||
layouts.
|
||||
These may be selected with the
|
||||
|
||||
5
tmux.h
5
tmux.h
@@ -4012,6 +4012,9 @@ int window_copy_get_current_offset(struct window_pane *, u_int *,
|
||||
u_int *);
|
||||
char *window_copy_get_hyperlink(struct window_pane *, u_int, u_int);
|
||||
void window_copy_set_line_numbers(struct window_pane *, int);
|
||||
void window_copy_output(struct window_pane *, struct client *,
|
||||
struct session *, struct winlink *, struct cmdq_item *,
|
||||
const char *, struct args *);
|
||||
|
||||
/* window-customize.c */
|
||||
extern const struct window_mode window_customize_mode;
|
||||
@@ -4223,7 +4226,7 @@ struct winlink *spawn_window(struct spawn_context *, char **);
|
||||
struct window_pane *spawn_pane(struct spawn_context *, char **);
|
||||
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
|
||||
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
|
||||
spawn_finish_edit_cb, void *);
|
||||
const char *, spawn_finish_edit_cb, void *);
|
||||
void spawn_cancel_editor(struct spawn_editor_state *);
|
||||
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
|
||||
void spawn_editor_finish(struct window_pane *);
|
||||
|
||||
@@ -606,7 +606,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
|
||||
ed->name = xstrdup(paste_buffer_name(pb));
|
||||
ed->pb = pb;
|
||||
|
||||
ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_buffer_edit_close_cb, ed);
|
||||
if (ed->editor == NULL)
|
||||
window_buffer_finish_edit(ed);
|
||||
else {
|
||||
|
||||
537
window-copy.c
537
window-copy.c
@@ -86,8 +86,15 @@ static int window_copy_set_fold(struct window_mode_entry *, int, int,
|
||||
u_int, int);
|
||||
static int window_copy_find_fold(struct window_mode_entry *, u_int,
|
||||
u_int *, u_int *);
|
||||
static int window_copy_find_output_range(struct window_mode_entry *,
|
||||
u_int *, u_int *, u_int *, u_int *);
|
||||
static int window_copy_find_previous_output_range(struct screen *, u_int *,
|
||||
u_int *, u_int *, u_int *);
|
||||
static void window_copy_output_end(struct screen *, u_int *, u_int *);
|
||||
static void window_copy_cursor_source(struct window_mode_entry *, u_int *,
|
||||
u_int *);
|
||||
static int window_copy_source_position(struct window_mode_entry *, u_int,
|
||||
u_int, u_int *, u_int *);
|
||||
static int window_copy_restore_cursor(struct window_mode_entry *, u_int,
|
||||
u_int);
|
||||
static int window_copy_restore_command_end(struct window_mode_entry *,
|
||||
@@ -138,8 +145,14 @@ static int window_copy_update_selection(struct window_mode_entry *, int,
|
||||
int);
|
||||
static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
|
||||
static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
|
||||
static void *window_copy_get_output(struct window_mode_entry *, size_t *,
|
||||
int);
|
||||
static void *window_copy_get_grid_range(struct grid *, u_int, u_int, u_int,
|
||||
u_int, size_t *);
|
||||
static void window_copy_copy_buffer(struct window_mode_entry *,
|
||||
const char *, void *, size_t, int, int);
|
||||
static void window_copy_pipe_buffer(struct session *, const char *, void *,
|
||||
size_t);
|
||||
static void window_copy_pipe(struct window_mode_entry *,
|
||||
struct session *, const char *);
|
||||
static void window_copy_copy_pipe(struct window_mode_entry *,
|
||||
@@ -151,6 +164,8 @@ static void window_copy_append_selection(struct window_mode_entry *);
|
||||
static void window_copy_clear_selection(struct window_mode_entry *);
|
||||
static void window_copy_copy_line(struct window_mode_entry *, char **,
|
||||
size_t *, u_int, u_int, u_int);
|
||||
static void window_copy_copy_grid_line(struct grid *, char **, size_t *,
|
||||
u_int, u_int, u_int);
|
||||
static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
|
||||
const char *);
|
||||
static u_int window_copy_find_length(struct window_mode_entry *, u_int);
|
||||
@@ -335,6 +350,7 @@ struct window_copy_mode_data {
|
||||
int hide_position; /* hide position marker */
|
||||
int line_numbers; /* 0 off, 1 from option, 2 absolute */
|
||||
int fold_view;
|
||||
int output_previous;
|
||||
u_int output_status_width;
|
||||
|
||||
enum {
|
||||
@@ -2119,6 +2135,94 @@ window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid_reader gr;
|
||||
void *buf;
|
||||
size_t len;
|
||||
u_int sx, sy, ex, ey, bsx, bsy, bex, bey, total;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
|
||||
if (all) {
|
||||
sx = sy = 0;
|
||||
total = data->source->grid->hsize + data->source->grid->sy;
|
||||
ey = total - 1;
|
||||
ex = grid_get_line(data->source->grid, ey)->cellused;
|
||||
} else if (data->output_previous) {
|
||||
if (!window_copy_find_previous_output_range(data->source, &sx, &sy,
|
||||
&ex, &ey))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
} else if (!window_copy_find_output_range(wme, &sx, &sy, &ex, &ey))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
free(buf);
|
||||
|
||||
if (all && data->fold_view) {
|
||||
window_copy_set_all_folds(data, 0);
|
||||
window_copy_rebuild_backing(wme);
|
||||
} else if (data->fold_view &&
|
||||
data->source_flags[sy] & WINDOW_COPY_FOLD_COLLAPSED)
|
||||
window_copy_set_fold(wme, 0, 0, sy, 0);
|
||||
if (!window_copy_source_position(wme, sx, sy, &bsx, &bsy) ||
|
||||
!window_copy_source_position(wme, ex, ey, &bex, &bey))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
|
||||
window_copy_clear_selection(wme);
|
||||
data->lineflag = LINE_SEL_NONE;
|
||||
data->rectflag = 0;
|
||||
data->selflag = SEL_CHAR;
|
||||
window_copy_scroll_to(wme, bsx, bsy, 1);
|
||||
window_copy_start_selection(wme);
|
||||
if (options_get_number(wme->wp->window->options, "mode-keys") ==
|
||||
MODEKEY_VI) {
|
||||
grid_reader_start(&gr, data->backing->grid, bex, bey);
|
||||
grid_reader_cursor_left(&gr, 1);
|
||||
grid_reader_get_cursor(&gr, &bex, &bey);
|
||||
}
|
||||
window_copy_scroll_to(wme, bex, bey, 1);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_output_no_clear(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg0 != NULL)
|
||||
prefix = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL)
|
||||
window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
|
||||
set_clip);
|
||||
}
|
||||
free(prefix);
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
window_copy_cmd_copy_output_no_clear(cs);
|
||||
window_copy_clear_selection(cs->wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -3274,6 +3378,133 @@ window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_pipe_output_no_clear(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL, *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
const char *arg1 = args_string(cs->wargs, 1);
|
||||
size_t len;
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg1 != NULL)
|
||||
prefix = format_single(NULL, arg1, c, s, wl, wp);
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
|
||||
set_clip);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
free(prefix);
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
window_copy_cmd_copy_pipe_output_no_clear(cs);
|
||||
window_copy_clear_selection(cs->wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_pipe_output_no_clear(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
window_copy_cmd_pipe_output_no_clear(cs);
|
||||
window_copy_clear_selection(cs->wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_open_done(char *buf, __unused size_t len, __unused void *arg)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_selection(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
void *buf;
|
||||
char *editor = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL ||
|
||||
(data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
if (arg0 != NULL && *arg0 != '\0')
|
||||
editor = format_single(NULL, arg0, cs->c, cs->s, cs->wl, wme->wp);
|
||||
buf = window_copy_get_selection(wme, &len);
|
||||
if (buf != NULL) {
|
||||
spawn_editor(cs->c, buf, len, editor, window_copy_open_done, NULL);
|
||||
free(buf);
|
||||
}
|
||||
free(editor);
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
void *buf;
|
||||
char *editor = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
if (arg0 != NULL && *arg0 != '\0')
|
||||
editor = format_single(NULL, arg0, cs->c, cs->s, cs->wl, wme->wp);
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL) {
|
||||
spawn_editor(cs->c, buf, len, editor, window_copy_open_done, NULL);
|
||||
free(buf);
|
||||
}
|
||||
free(editor);
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -3902,6 +4133,109 @@ window_copy_find_fold(struct window_mode_entry *wme, u_int target,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Find the output range for the command at the cursor. */
|
||||
static int
|
||||
window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
|
||||
u_int *sy, u_int *ex, u_int *ey)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd;
|
||||
struct grid_line *gl;
|
||||
u_int cursor_x, cursor_y, prompt_x = 0;
|
||||
u_int prompt_y = UINT_MAX, y, total;
|
||||
int found_start = 0, found_end = 0;
|
||||
|
||||
if (data->source == NULL)
|
||||
return (0);
|
||||
window_copy_cursor_source(wme, &cursor_x, &cursor_y);
|
||||
|
||||
gd = data->source->grid;
|
||||
total = gd->hsize + gd->sy;
|
||||
for (y = 0; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
if (~gl->flags & GRID_LINE_START_PROMPT)
|
||||
continue;
|
||||
if (y > cursor_y ||
|
||||
(y == cursor_y && gl->osc133_data.prompt_col > cursor_x))
|
||||
break;
|
||||
prompt_y = y;
|
||||
prompt_x = gl->osc133_data.prompt_col;
|
||||
}
|
||||
if (prompt_y == UINT_MAX)
|
||||
return (0);
|
||||
|
||||
for (y = prompt_y; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
if (y != prompt_y && gl->flags & GRID_LINE_START_PROMPT)
|
||||
break;
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT &&
|
||||
(y != prompt_y || gl->osc133_data.out_start_col >= prompt_x)) {
|
||||
*sx = gl->osc133_data.out_start_col;
|
||||
*sy = y;
|
||||
found_start = 1;
|
||||
}
|
||||
if (found_start && gl->flags & GRID_LINE_END_OUTPUT &&
|
||||
(y != prompt_y || gl->osc133_data.out_end_col >= prompt_x)) {
|
||||
*ex = gl->osc133_data.out_end_col;
|
||||
*ey = y;
|
||||
found_end = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_start)
|
||||
return (0);
|
||||
if (!found_end) {
|
||||
if (y != total)
|
||||
return (0);
|
||||
window_copy_output_end(data->source, ex, ey);
|
||||
}
|
||||
if (cursor_y > *ey || (cursor_y == *ey &&
|
||||
(found_end ? cursor_x >= *ex : cursor_x > *ex)))
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Find the most recent complete output, or one still running at buffer end. */
|
||||
static int
|
||||
window_copy_find_previous_output_range(struct screen *s, u_int *sx, u_int *sy,
|
||||
u_int *ex, u_int *ey)
|
||||
{
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_line *gl;
|
||||
u_int y, total;
|
||||
int found = 0, pending = 0;
|
||||
|
||||
total = gd->hsize + gd->sy;
|
||||
for (y = 0; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT) {
|
||||
*sx = gl->osc133_data.out_start_col;
|
||||
*sy = y;
|
||||
pending = 1;
|
||||
}
|
||||
if (pending && gl->flags & GRID_LINE_END_OUTPUT) {
|
||||
*ex = gl->osc133_data.out_end_col;
|
||||
*ey = y;
|
||||
found = 1;
|
||||
pending = 0;
|
||||
}
|
||||
if (gl->flags & GRID_LINE_START_PROMPT)
|
||||
pending = 0;
|
||||
}
|
||||
if (pending) {
|
||||
window_copy_output_end(s, ex, ey);
|
||||
return (1);
|
||||
}
|
||||
return (found);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_output_end(struct screen *s, u_int *x, u_int *y)
|
||||
{
|
||||
*x = s->cx;
|
||||
*y = screen_hsize(s) + s->cy;
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_cursor_source(struct window_mode_entry *wme, u_int *source_x,
|
||||
u_int *source_y)
|
||||
@@ -3919,6 +4253,31 @@ window_copy_cursor_source(struct window_mode_entry *wme, u_int *source_x,
|
||||
*source_y = line->source_line;
|
||||
}
|
||||
|
||||
static int
|
||||
window_copy_source_position(struct window_mode_entry *wme, u_int source_x,
|
||||
u_int source_y, u_int *backing_x, u_int *backing_y)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct window_copy_line *line;
|
||||
u_int y, total;
|
||||
|
||||
if (!data->fold_view) {
|
||||
*backing_x = source_x;
|
||||
*backing_y = source_y;
|
||||
return (1);
|
||||
}
|
||||
total = screen_hsize(data->backing) + screen_size_y(data->backing);
|
||||
for (y = 0; y < total; y++) {
|
||||
line = window_copy_get_line_info(data, y);
|
||||
if (line == NULL || line->source_line != source_y)
|
||||
continue;
|
||||
*backing_x = source_x;
|
||||
*backing_y = y;
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
window_copy_restore_cursor(struct window_mode_entry *wme, u_int source_x,
|
||||
u_int source_y)
|
||||
@@ -4297,6 +4656,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_and_cancel
|
||||
},
|
||||
{ .command = "copy-pipe-output",
|
||||
.args = { "aCP", 0, 2, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_output
|
||||
},
|
||||
{ .command = "copy-output",
|
||||
.args = { "aCP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_output
|
||||
},
|
||||
{ .command = "copy-selection-no-clear",
|
||||
.args = { "CP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
@@ -4543,6 +4914,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
|
||||
.f = window_copy_cmd_other_end
|
||||
},
|
||||
{ .command = "open-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_output
|
||||
},
|
||||
{ .command = "open-selection",
|
||||
.args = { "", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_selection
|
||||
},
|
||||
{ .command = "page-down",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -4579,6 +4962,12 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_and_cancel
|
||||
},
|
||||
{ .command = "pipe-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_output
|
||||
},
|
||||
{ .command = "previous-matching-bracket",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -4762,6 +5151,12 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_select_line
|
||||
},
|
||||
{ .command = "select-output",
|
||||
.args = { "a", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_select_output
|
||||
},
|
||||
{ .command = "select-word",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
@@ -4900,6 +5295,69 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
window_copy_output(struct window_pane *wp, struct client *c,
|
||||
struct session *s, struct winlink *wl, struct cmdq_item *item,
|
||||
const char *command, struct args *args)
|
||||
{
|
||||
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
|
||||
struct window_copy_mode_data *data;
|
||||
struct window_copy_cmd_state cs;
|
||||
struct args *mode_args;
|
||||
enum window_copy_cmd_action action;
|
||||
int temporary = 0;
|
||||
|
||||
if (wme == NULL || wme->mode != &window_copy_mode) {
|
||||
if (strcmp(command, "open-selection") == 0)
|
||||
return;
|
||||
mode_args = args_create();
|
||||
if (window_pane_set_mode(wp, wp, &window_copy_mode, item, NULL,
|
||||
mode_args)) {
|
||||
args_free(mode_args);
|
||||
return;
|
||||
}
|
||||
args_free(mode_args);
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
temporary = 1;
|
||||
}
|
||||
|
||||
cs.wme = wme;
|
||||
cs.args = args;
|
||||
cs.wargs = args;
|
||||
cs.m = NULL;
|
||||
cs.c = c;
|
||||
cs.s = s;
|
||||
cs.wl = wl;
|
||||
|
||||
data = wme->data;
|
||||
data->output_previous = temporary;
|
||||
if (strcmp(command, "select-output") == 0)
|
||||
action = window_copy_cmd_select_output(&cs);
|
||||
else if (strcmp(command, "copy-output") == 0)
|
||||
action = window_copy_cmd_copy_output(&cs);
|
||||
else if (strcmp(command, "copy-pipe-output") == 0)
|
||||
action = window_copy_cmd_copy_pipe_output(&cs);
|
||||
else if (strcmp(command, "pipe-output") == 0)
|
||||
action = window_copy_cmd_pipe_output(&cs);
|
||||
else if (strcmp(command, "open-output") == 0)
|
||||
action = window_copy_cmd_open_output(&cs);
|
||||
else if (strcmp(command, "open-selection") == 0)
|
||||
action = window_copy_cmd_open_selection(&cs);
|
||||
else {
|
||||
data->output_previous = 0;
|
||||
if (temporary)
|
||||
window_pane_reset_mode(wp);
|
||||
return;
|
||||
}
|
||||
data->output_previous = 0;
|
||||
|
||||
if (temporary && (strcmp(command, "select-output") != 0 ||
|
||||
action != WINDOW_COPY_CMD_REDRAW))
|
||||
window_pane_reset_mode(wp);
|
||||
else if (action == WINDOW_COPY_CMD_REDRAW)
|
||||
window_copy_redraw_screen(wme);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
|
||||
int no_redraw)
|
||||
@@ -7098,6 +7556,58 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void *
|
||||
window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd;
|
||||
u_int sx, sy, ex, ey, total;
|
||||
|
||||
gd = data->source->grid;
|
||||
total = gd->hsize + gd->sy;
|
||||
if (all) {
|
||||
sx = sy = 0;
|
||||
ey = total - 1;
|
||||
ex = grid_get_line(gd, ey)->cellused;
|
||||
} else if (data->output_previous) {
|
||||
if (!window_copy_find_previous_output_range(data->source, &sx, &sy,
|
||||
&ex, &ey)) {
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
} else if (!window_copy_find_output_range(wme, &sx, &sy, &ex, &ey)) {
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (window_copy_get_grid_range(gd, sx, sy, ex, ey, len));
|
||||
}
|
||||
|
||||
static void *
|
||||
window_copy_get_grid_range(struct grid *gd, u_int sx, u_int sy, u_int ex,
|
||||
u_int ey, size_t *len)
|
||||
{
|
||||
char *buf;
|
||||
size_t off;
|
||||
u_int i;
|
||||
|
||||
buf = xmalloc(1);
|
||||
off = 0;
|
||||
for (i = sy; i <= ey; i++) {
|
||||
window_copy_copy_grid_line(gd, &buf, &off, i,
|
||||
i == sy ? sx : 0, i == ey ? ex : gd->sx);
|
||||
}
|
||||
if (off != 0 && buf[off - 1] == '\n')
|
||||
off--;
|
||||
if (off == 0) {
|
||||
free(buf);
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
*len = off;
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
|
||||
void *buf, size_t len, int set_paste, int set_clip)
|
||||
@@ -7132,18 +7642,28 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
|
||||
const char *cmd, size_t *len)
|
||||
{
|
||||
void *buf;
|
||||
struct job *job;
|
||||
|
||||
buf = window_copy_get_selection(wme, len);
|
||||
window_copy_pipe_buffer(s, cmd, buf, *len);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_pipe_buffer(struct session *s, const char *cmd, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct job *job;
|
||||
|
||||
if (buf == NULL)
|
||||
return;
|
||||
if (cmd == NULL || *cmd == '\0')
|
||||
cmd = options_get_string(global_options, "copy-command");
|
||||
if (cmd != NULL && *cmd != '\0') {
|
||||
job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
|
||||
NULL, JOB_NOWAIT, -1, -1);
|
||||
if (job != NULL)
|
||||
bufferevent_write(job_get_event(job), buf, *len);
|
||||
bufferevent_write(job_get_event(job), buf, len);
|
||||
}
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -7224,7 +7744,14 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
u_int sy, u_int sx, u_int ex)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
|
||||
window_copy_copy_grid_line(data->backing->grid, buf, off, sy, sx, ex);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_copy_grid_line(struct grid *gd, char **buf, size_t *off, u_int sy,
|
||||
u_int sx, u_int ex)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
struct grid_line *gl;
|
||||
struct utf8_data ud;
|
||||
@@ -7246,7 +7773,7 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
if (wrapped)
|
||||
xx = gl->cellsize;
|
||||
else
|
||||
xx = window_copy_find_length(wme, sy);
|
||||
xx = grid_line_length(gd, sy);
|
||||
if (ex > xx)
|
||||
ex = xx;
|
||||
if (sx > xx)
|
||||
|
||||
@@ -2104,8 +2104,8 @@ window_customize_start_edit(struct window_customize_modedata *data,
|
||||
buf = "\n";
|
||||
len = 1;
|
||||
}
|
||||
ed->editor = spawn_editor(c, buf, len, window_customize_edit_close_cb,
|
||||
ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_customize_edit_close_cb, ed);
|
||||
free(value);
|
||||
if (ed->editor == NULL)
|
||||
window_customize_finish_edit(ed);
|
||||
|
||||
Reference in New Issue
Block a user