Replace tmux copy/scrollback mode with Kakoune

# Make our own copy-mode with Kakoune!
bind -Tpane-menu N capture-pane -S- -E- -J -b copy-mode \; \
    new-window -n copy-mode -d '\
      file=$(mktemp) ; \
      tmux save-buffer -b copy-mode "$file" ; \
      tmux delete-buffer -b copy-mode ; \
      command kak -e "\
         edit $file ;\
         exec gj ;\
         set-option buffer readonly true ;\
         set-option window filetype tmux-copy ;\
         rename-buffer *tmux-copy* ;\
         " ; \
      rm -f "$file" ; \
      tmux swap-pane -s :copy-mode.0 -t : ; \
    ' \; \
    swap-pane -s :copy-mode.0 -t :

Of course, you’ll need to bind to your existing copy-mode key, not my weird pane-menu thing.

Here’s an updated version which captures ANSI escapes and tries to put the cursor close to where it was. It’s more complicated, and could use some more love, but I think it’s finally better than above:

# Make our own copy-mode with Kakoune!
bind -Tpane-menu N run-shell 'tmux set-buffer -b copy-pane-id "#D"' \; \
    new-window -n copy-mode -d '\
      pane_id="$(tmux show-buffer -b copy-pane-id)" ;\
      file=$(mktemp) ;\
      tmux capture-pane -t "${pane_id}" -S- -E- -J -e -p >"$file" ;\
      cursor_y=$(tmux display-message -t "${pane_id}" -p "#{cursor_y}") ;\
      cursor_x=$(tmux display-message -t "${pane_id}" -p "#{cursor_x}") ;\
      pane_height=$(tmux display-message -t "${pane_id}" -p "#{pane_height}") ;\
      line_count="$(wc -l "$file" |awk "{print \$1}")" ;\
      sel_line=$(( line_count - ( pane_height - cursor_y ) + 1 )) ;\
      printf "sel = %s\n" "$line_count" >>/tmp/debug.log ;\
      cursor="${sel_line}.${cursor_x},${sel_line}.${cursor_x}" ;\
      printf "cursor = %s\n" "$cursor" >>/tmp/debug.log ;\
      command kak -e "\
         edit $file ;\
         exec gj ;\
         try %{\
            ansi-render ;\
         } catch %{\
            exec -draft %{%s\x1B[\d;]+m<ret><a-d>} ;\
         } ;\
         write ;\
         set-option buffer readonly true ;\
         set-option window filetype tmux-copy ;\
         rename-buffer *tmux-copy* ;\
         select $cursor ;\
         " ; \
      rm -f "$file" ; \
      tmux swap-pane -s :copy-mode.0 -t : ; \
    ' \; \
    swap-pane -s :copy-mode.0 -t :
1 Like

Of course, you’ll need to bind to your existing copy-mode key, not my weird pane-menu thing.

When you say “my weird pane-menu” thing what do you mean exactly? I’d love to put this to use in my own tmux config, but I’m not very well versed with tmux’s config language.

@EpocSquadron I have bind -Tpane-menu N where you might just use bind c (for example) to make <leader>c copy mode.

In the first version that you proposed @eraserhd , why not dumping the pane to stdout and directly pipe it to kak? I understand that it does not handle ansi, but as a quick solution, what are the drawbacks compared to what you proposed? What is the point of having a temporary file?

bind-key [ send-keys 'tmux capture-pane -S - -p | kak'

Few modifications I made:

  • avoid run-shell
  • set user option @copy-pane-id instead of setting buffer
  • split-window -Z instead of new-window in order to avoid referring to pane index :copy-mode.0 because index 0 can be invalid in case of set-option -wg pane-base-index 1.
bind-key F10 set-option -F @copy-pane-id "#D" \; \
    split-window -Z '\
      pane_id="$(tmux show-option -v @copy-pane-id)" ;\
      file=$(mktemp) ;\
      tmux capture-pane -t "${pane_id}" -S- -E- -J -e -p >"$file" ;\
      cursor_y=$(tmux display-message -t "${pane_id}" -p "#{cursor_y}") ;\
      cursor_x=$(tmux display-message -t "${pane_id}" -p "#{cursor_x}") ;\
      pane_height=$(tmux display-message -t "${pane_id}" -p "#{pane_height}") ;\
      line_count="$(wc -l "$file" |awk "{print \$1}")" ;\
      sel_line=$(( line_count - ( pane_height - cursor_y ) + 1 )) ;\
      cursor="${sel_line}.${cursor_x},${sel_line}.${cursor_x}" ;\
      command kak -e "\
         edit $file ;\
         exec gj ;\
         try %{\
            ansi-render ;\
         } catch %{\
            exec -draft %{%s\x1B[\d;]+m<ret><a-d>} ;\
         } ;\
         write ;\
         set-option buffer readonly true ;\
         set-option window filetype tmux-copy ;\
         rename-buffer *tmux-copy* ;\
         select $cursor ;\
         " ;\
      unlink "$file" ;\
    '

tmux’s capture-pane has option -b buffer-name, so I think it’s possible to avoid creating temporary file by capturing pane directly into a buffer but I’m lazy to implement this.