Files
dotfiles/.config/tmux/textobjs.sh
Julian Prein ec417a439c tmux:textobjs: Merge all copy-mode commands
`copy_exec` was stupid since it brought unnecessary shell overhead even
though it was meant to improve performance (one could argue that it is
not even necessary here).

Continue the idea of reducing calls to `tmux` by "globally" chaining
together all copy-mode commands instead of doing it only "locally" with
`copy_exec`. This has the added benefit that also calls having arguments
can be included and merged.
2024-09-04 16:38:52 +02:00

157 lines
4.1 KiB
Bash
Executable File

#!/bin/sh
# Support vim's text objects in tmux's copy-mode.
get_var() {
tmux display-message -p "#{$1}"
}
command_prompt() {
tmux command-prompt -k -I "$2" -p "$1" 'display-message -p "%%"'
}
mode="$(get_var pane_mode)"
if [ "$mode" != copy-mode ]; then
>&2 printf "%s: Not in copy mode\n" "$0"
exit 1
fi
# cancel copy-mode when not in 'visual' mode (i.e. selection is present)
selection_present="$(get_var selection_present)"
if [ "$selection_present" -eq 0 ]; then
tmux send -X cancel
exit 0
fi
# get motion
motion="$(command_prompt "(operator-pending)")"
# ignore any args
set --
case "$motion" in
w)
copy_x="$(get_var copy_cursor_x)"
copy_line="$(get_var copy_cursor_line)"
# Do nothing if the cursor sits on a space
# TODO: Do the same for other non-word characters
# NOTE: Using grep here, as cut does not support UTF-8
char_curr="$(printf %s "$copy_line" | grep -Po "^.{$copy_x}\\K.")"
[ "$char_curr" != " " ] || return 0
copy_line_post="$(printf %s "$copy_line" | grep -Po "^.{$copy_x}\\K.*")"
copy_line_pre="$(printf %s "$copy_line" | grep -Po "^..{$copy_x}")"
copy_word="$(get_var copy_cursor_word)"
# send "boe"
if [ "${copy_line_post#"$copy_word"}" = "$copy_line_post" ];
then
# not on beginning of word
set -- "$@" send -X previous-word \;
set -- "$@" send -X other-end \;
fi
if [ "${copy_line_pre%"$copy_word"}" = "$copy_line_pre" ];
then
# not on end of word
set -- "$@" send -X next-word-end \;
fi
;;
W)
copy_x="$(get_var copy_cursor_x)"
copy_line="$(get_var copy_cursor_line)"
# Do nothing if the cursor sits on the space
char_curr="$(printf %s "$copy_line" | grep -Po "^.{$copy_x}\\K.")"
[ "$char_curr" != " " ] || return 0
char_pre="$(printf %s "$copy_line" | grep -Po "^.{$((copy_x - 1))}\\K.")"
char_post="$(printf %s "$copy_line" | grep -Po "^..{$copy_x}\\K.")"
# default to a space if the cursor is at the beginning or end of
# the line
: "${char_pre:= }"
: "${char_post:= }"
# send "BoE"
if [ "$char_pre" != " " ]; then
# not on beginning of WORD
set -- "$@" send -X previous-space \;
set -- "$@" send -X other-end \;
fi
if [ "$char_post" != " " ]; then
# not on end of WORD
set -- "$@" send -X next-space-end \;
fi
;;
p)
# send "{j0o}k$"
set -- "$@" send -X previous-paragraph \;
scroll_pos="$(get_var scroll_position)"
hist_size="$(get_var history_size)"
cursor_y="$(get_var copy_cursor_y)"
# don't move down if we're at the very first paragraph
if [ "$scroll_pos" -lt "$hist_size" ] || [ "$cursor_y" -gt 0 ]
then
set -- "$@" send -X cursor-down \;
fi
set -- "$@" send -X start-of-line \;
set -- "$@" send -X other-end \;
set -- "$@" send -X next-paragraph \;
scroll_pos="$(get_var scroll_position)"
cursor_y="$(get_var copy_cursor_y)"
pane_height="$(get_var pane_height)"
: "$((pane_height -= 1))"
# don't move up if we're at the very last paragraph
if [ "$scroll_pos" -gt 0 ] || [ "$cursor_y" -lt "$pane_height" ]
then
set -- "$@" send -X cursor-up \;
fi
set -- "$@" send -X end-of-line \;
;;
# TODO: All following break when the cursor sits on the start or end
\")
set -- "$@" send -X jump-to-backward '"' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward '"' \;
;;
\')
set -- "$@" send -X jump-to-backward "'" \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward "'" \;
;;
\`)
set -- "$@" send -X jump-to-backward '`' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward '`' \;
;;
'[|]')
set -- "$@" send -X jump-to-backward '[' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward ']' \;
;;
'b|(|)')
set -- "$@" send -X jump-to-backward '(' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward ')' \;
;;
'<|>')
set -- "$@" send -X jump-to-backward '<' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward '>' \;
;;
'B|{|}')
# TODO: make this work over multiple lines
set -- "$@" send -X jump-to-backward '{' \;
set -- "$@" send -X other-end \;
set -- "$@" send -X jump-to-forward '}' \;
;;
esac
[ $# -eq 0 ] || tmux "$@"