#+title: Tmux
#+setupfile: headers
#+property: header-args:tmux  :mkdirp yes :lexical t :exports code
#+property: header-args:tmux+ :tangle ~/.config/tmux/tmux.conf
#+property: header-args:tmux+ :mkdirp yes :noweb yes

* Tmux
Tmux is one of the widest known terminal multiplexers along with
=screen=. I find it useful when I don’t want to bother with panes in my
[[https://sw.kovidgoyal.net/kitty/][kitty]] terminal.

Be aware this configuration is mostly keybinds.

** Setting sane configurations
By default, Tmux does not support all the colours your terminal may
support, so we need to tell it to set true colours..
#+begin_src tmux
set-option -sa terminal-overrides ",xterm*:Tc"
#+end_src

We can also tell it to enable mouse support. That way, we will be able
to click on Tmux elements and scroll in its panes!
#+begin_src tmux
set -g mouse on
#+end_src

Finally, I’ll set ~vi-mode~ for some sane movements.
#+begin_src tmux
set-window-option -g mode-keys vi
#+end_src

** Windows and panes configuration
Although I agree in computer science most things should begin with
zero, I find it quite weird to see my first window and my first pane
to be labelled with it rather than one. So, let’s pull a Lua on Tmux
and force it to begin with 1 instead of 0.
#+begin_src tmux
set -g base-index 1
set -g pane-base-index 1
set-window-option -g pane-base-index 1
set-option -g renumber-windows on
#+end_src

** Plugins!
Using [[https://github.com/tmux-plugins/tpm][TPM]], we can use plugins in Tmux! The way I installed it is very simple:
#+begin_src bash
mkdir -p ~/.config/tmux/
git clone https://github.com/tmux-plugins/tpm.git ~/.config/tmux/tpm
#+end_src

Here’s a list of the plugins that I use.
#+name: plugins
| Plugin                | Why                                                  |
|-----------------------+------------------------------------------------------|
| tpm                   | TPM itself                                           |
| tmux-sensible         | Better defaults I can’t be bothered to change myself |
| tmux-yank             | Better copy/pasting                                  |
| tmux-prefix-highlight | Tell me which prefix I’m using                       |
| tmux-resurrect        | Persist tmux sessions across system restarts         |
| nordtheme/tmux        | Nord theme for Tmux!                                 |

#+begin_src emacs-lisp :var plugins=plugins[,0] :exports code :wrap src tmux
(mapconcat (lambda (plugin)
             (format "set -g @plugin '%s'"
                     (if (string-match-p (quote "/") plugin)
                         plugin
                       (concat "tmux-plugins/" plugin))))
           plugins
           "\n")
#+end_src

#+RESULTS:
#+begin_src tmux
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'tmux-plugins/tmux-prefix-highlight'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'nordtheme/tmux'
#+end_src

Let’s run TPM right after that.
#+begin_src tmux
run '~/.config/tmux/tpm/tpm'
#+end_src

For restoring processes with =tmux-resurrect=, we must add additional
programs in the =@resurrect-processes= variable. These are:
#+name: resurrect-processes-list
- ="~ncmpcpp -q"=
- =btop=
- =ssh=

#+name: resurrect-processes
#+begin_src emacs-lisp :var processes=resurrect-processes-list :exports none :cache yes
(mapconcat (lambda (process) (string-replace "=" "" process))
           processes
           " ")
#+end_src

#+RESULTS[4f72312f2da98c81eff2b092779a59c993fa19e2]: resurrect-processes
: "~ncmpcpp -q" btop ssh

#+begin_src tmux :noweb yes
set -g @resurrect-processes '<<resurrect-processes()>>'
#+end_src

** Keybindings
Firstly, I don’t like prefixing all of my keybindings with ~C-b~, that’s
what I use in ~insert-mode~ in Emacs to make the cursor go back. So
instead, let’s set meta with space as my prefix.
#+begin_src tmux
unbind C-b
set -g prefix M-Space
bind M-Space send-prefix
#+end_src

Now, I will only add few keybindings on the root prefix that actually
do something immediately, I will mostly add keybindings that will lead
to other prefixes; I prefer by far to have keychords that are a bit
lengthy and mnemonic than some obscure “ ~C-M-&~ is for doing this and
that”.

#+name: std-prefix
| Keybinding | Command          | Prefix to go to |
|------------+------------------+-----------------|
| =«=          | select-window -p |                 |
| =»=          | select-window -n |                 |
| =Tab=        |                  | windows         |
| =w=          |                  | pane            |
| =y=          |                  | copy-mode       |

Note I am using the key =w= to access the pane prefix because of how
used to Emacs I am, and in Emacs terminology panes are windows while
tmux windows would be tabs.

#+name: gen-keybinds
#+begin_src emacs-lisp :var keybinds=std-prefix :var prefix="prefix" :exports none
(mapconcat (lambda (keybind)
             (format "bind-key -T %s %s %s"
                     prefix
                     (string-replace "=" "" (car keybind))
                     (let* ((command (nth 1 keybind))
                            (command (if (string= "" command) nil command))
                            (goto-prefix (nth 2 keybind))
                            (goto-prefix (if (or (null goto-prefix)
                                                 (string= "" goto-prefix))
                                             nil
                                           (concat "switch-client -T " goto-prefix))))
                       (if (and command goto-prefix)
                           (concat command "\\; " goto-prefix)
                         (or command goto-prefix)))))
           keybinds
           "\n")
#+end_src

#+RESULTS[3e58a076aacba87d1cca87db849460e4c2414e94]: gen-keybinds
: bind-key -T prefix « select-window -p
: bind-key -T prefix » select-window -n
: bind-key -T prefix Tab switch-client -T windows
: bind-key -T prefix w switch-client -T pane
: bind-key -T prefix y switch-client -T copy-mode

#+begin_src tmux
<<gen-keybinds()>>
#+end_src

*** Panes
As you will see not only here but also lower, I am not using the usual
=hjkl= to navigate around since I am using the [[https://bepo.fr][bépo]] layout. I use
instead the =ctsr= keys.

Below are the keybindings living in the =pane= prefix. Note that calls to
=split-window= have a =-c "#{pane_current_path}"= argument so new panes
open in the same directory as the directory I am currently in.
#+name: pane-prefix
| Keybinding | Command                                   | Prefix to go to |
|------------+-------------------------------------------+-----------------|
| =/=          | split-window -h -c "#{pane-current_path}" |                 |
| =-=          | split-window -v -c "#{pane-current_path}" |                 |
| =c=          | select-pane -L                            |                 |
| =t=          | select-pane -D                            |                 |
| =s=          | select-pane -U                            |                 |
| =r=          | select-pane -R                            |                 |
| =f=          | resize-pane -Z                            |                 |
| =.=          |                                           | pane-resize     |

#+begin_src tmux
<<gen-keybinds(keybinds=pane-prefix, prefix="pane")>>
#+end_src

When it comes to resizing the panes, the keybindings are in their own
prefix referenced above: =pane-resize=. All keybindings will lead to the
same prefix again, which enables the user to type for instance =M-Space
w . r r r r r= in order to call repetitively =resize-pane -R 5=. Hitting
any key that is not part of the current prefix will get us out of it.
#+name: pane-resize-prefix
| Keybinding | Command          | Prefix to go to |
|------------+------------------+-----------------|
| c          | resize-pane -L 5 | pane-resize     |
| t          | resize-pane -D 5 | pane-resize     |
| s          | resize-pane -U 5 | pane-resize     |
| r          | resize-pane -R 5 | pane-resize     |
| C          | resize-pane -L   | pane-resize     |
| T          | resize-pane -D   | pane-resize     |
| S          | resize-pane -U   | pane-resize     |
| R          | resize-pane -R   | pane-resize     |

#+begin_src tmux
<<gen-keybinds(keybinds=pane-resize-prefix, prefix="pane-resize")>>
#+end_src

*** Windows
Since windows are more akin to tabs in Emacs, and I am way more used
to it than Tmux, all keybinds are prefixed with a =Tab=, itself prefixed
with the main prefix.
#+name: windows-prefix
| Keybinding | Command         |
|------------+-----------------|
| c          | new-window      |
| n          | next-window     |
| p          | previous-window |

#+begin_src tmux
<<gen-keybinds(keybinds=windows-prefix, prefix="windows")>>
#+end_src

In order to access more easily the different windows, I want to be
able to type =<prefix> TAB <window number>=. However, I’m using the bépo
layout, numbers are available only when pressing shift. Otherwise, the
characters typed are ="«»()@+-/*= (from 1 to 0).
#+begin_src emacs-lisp :wrap src tmux :exports code
(let ((keybinds "")
      (keys '("\\\"" "«" "»" "(" ")" "@" "+" "-" "/" "*")))
  (dotimes (i (length keys) keybinds)
    (setq keybinds (string-trim
                    (concat keybinds
                            "\n"
                            (format "bind-key -T windows %s select-window -t :=%d"
                                    (nth i keys)
                                    (1+ i)))))))
#+end_src

#+RESULTS:
#+begin_src tmux
bind-key -T windows \" select-window -t :=1
bind-key -T windows « select-window -t :=2
bind-key -T windows » select-window -t :=3
bind-key -T windows ( select-window -t :=4
bind-key -T windows ) select-window -t :=5
bind-key -T windows @ select-window -t :=6
bind-key -T windows + select-window -t :=7
bind-key -T windows - select-window -t :=8
bind-key -T windows / select-window -t :=9
bind-key -T windows * select-window -t :=10
#+end_src

*** Copy in vi mode
Tmux has a nice mode for vim keybindings users: =copy-mode-vi= which
allows moving the cursor around in the pane, select some stuff, and
copy it. But first, I need to unbind some keys:
#+begin_src tmux
unbind -T copy-mode-vi H
unbind -T copy-mode-vi J
unbind -T copy-mode-vi K
unbind -T copy-mode-vi L
unbind -T copy-mode-vi h
unbind -T copy-mode-vi j
unbind -T copy-mode-vi k
unbind -T copy-mode-vi l
#+end_src

#+name: copy-mode-vi-prefix
| Keybinding | Command                                |
|------------+----------------------------------------|
| v          | send-keys -X begin-selection           |
| C-v        | send-keys -X rectangle-toggle          |
| y          | send-keys -X copy-selection-and-cancel |
| C          | send-keys -X top-line                  |
| J          | send-keys -X jump-to-backward          |
| S          | send-keys -X scroll-up                 |
| R          | send-keys -X bottom-line               |
| T          | send-keys -X scroll-down               |
| c          | send-keys -X cursor-left               |
| t          | send-keys -X cursor-down               |
| s          | send-keys -X cursor-up                 |
| r          | send-keys -X cursor-right              |

#+begin_src tmux
<<gen-keybinds(keybinds=copy-mode-vi-prefix, prefix="copy-mode-vi")>>
#+end_src
