From ec637ad1c886221dc4996ad47d27192637ea8507 Mon Sep 17 00:00:00 2001 From: Julian Prein Date: Thu, 4 Jul 2024 14:30:56 +0200 Subject: [PATCH] tmux: Support repeatable yank motions via yank.sh Implementing logic inside tmux's config starts to become tedious with growing complexity. Introduce a shell script that handles the logic of the (now also repeatable) vim-like bindings. --- .config/tmux/tmux.conf | 19 ++------------- .config/tmux/yank.sh | 54 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 17 deletions(-) create mode 100755 .config/tmux/yank.sh diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf index 2f17d6a..1f2402c 100644 --- a/.config/tmux/tmux.conf +++ b/.config/tmux/tmux.conf @@ -132,23 +132,8 @@ bind -T copy-mode-vi C-v { send -X rectangle-toggle } -# Yank into system clipboard -# TODO: This breaks on repeatable movements (e.g. `y2e`) -bind -T copy-mode-vi y \ - if -F "#{selection_present}" { - send -X copy-pipe - } { - command-prompt -k -p (operator-pending) { - # do line-wise yank on yy - if -F "#{==:%1,y}" { - send -X select-line - } { - send -X begin-selection - send "%1" - } - send -X copy-pipe - } - } +# Yank into system clipboard with vim-like bindings +bind -T copy-mode-vi y run-shell '${XDG_CONFIG_HOME:-$HOME/.config}/tmux/yank.sh' bind -T copy-mode-vi Y \ if -F "#{selection_present}" { send -X copy-pipe-line } { send -X copy-pipe-end-of-line } diff --git a/.config/tmux/yank.sh b/.config/tmux/yank.sh new file mode 100755 index 0000000..6e200ee --- /dev/null +++ b/.config/tmux/yank.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +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 + +# Just yank if we have a selection already +selection_present="$(get_var selection_present)" +if [ "$selection_present" -eq 1 ]; then + tmux send -X copy-pipe + exit 0 +fi + +# get motion +motion="$(command_prompt "(operator-pending)")" + +# accumulate repeat number +# NOTE: -N unfortunately does not work since the first non-numeric key press is +# lost then (TODO: is it really?) +while printf "%s" "$motion" | grep -q '^[0-9]$'; do + repeat="$repeat$motion" + motion="$(command_prompt "(operator-pending-repeat)" "$repeat")" +done +# default to 1 if no repeat was specified +: "${repeat:=1}" + +# abort on Escape +[ "$motion" != "Escape" ] || exit 0 + +# choose between character or line-wise selection +case "$motion" in + y) + repeat="$((repeat - 1))" + motion="j" + tmux send -X select-line;; + j|k) + tmux send -X select-line;; + *) + tmux send -X begin-selection;; +esac + +# move and yank +[ "$repeat" -eq 0 ] || tmux send -N "$repeat" "$motion" +tmux send -X copy-pipe