2023-06-02 14:54:14 +00:00
|
|
|
|
#+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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
* Tmux
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
** Setting sane configurations
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
** Windows and panes configuration
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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
|
2023-12-10 14:09:07 +00:00
|
|
|
|
to be labelled with it rather than one. So, let’s pull a Lua on Tmux
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
** Plugins!
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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 |
|
2023-06-15 12:19:10 +00:00
|
|
|
|
| tmux-resurrect | Persist tmux sessions across system restarts |
|
2023-06-02 14:54:14 +00:00
|
|
|
|
| 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'
|
2023-06-15 12:19:10 +00:00
|
|
|
|
set -g @plugin 'tmux-plugins/tmux-resurrect'
|
2023-06-02 14:54:14 +00:00
|
|
|
|
set -g @plugin 'nordtheme/tmux'
|
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
|
|
Let’s run TPM right after that.
|
|
|
|
|
#+begin_src tmux
|
|
|
|
|
run '~/.config/tmux/tpm/tpm'
|
|
|
|
|
#+end_src
|
|
|
|
|
|
2023-06-15 12:19:10 +00:00
|
|
|
|
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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
** Keybindings
|
2023-12-10 14:09:07 +00:00
|
|
|
|
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.
|
2023-06-02 14:54:14 +00:00
|
|
|
|
#+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 | |
|
2023-06-15 12:19:10 +00:00
|
|
|
|
| =Tab= | | windows |
|
|
|
|
|
| =w= | | pane |
|
2023-06-02 14:54:14 +00:00
|
|
|
|
| =y= | | copy-mode |
|
|
|
|
|
|
2023-06-15 12:19:10 +00:00
|
|
|
|
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.
|
|
|
|
|
|
2023-06-02 14:54:14 +00:00
|
|
|
|
#+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))
|
2024-03-21 05:20:35 +00:00
|
|
|
|
(goto-prefix (if (or (null goto-prefix)
|
|
|
|
|
(string= "" goto-prefix))
|
|
|
|
|
nil
|
|
|
|
|
(concat "switch-client -T " goto-prefix))))
|
2023-06-02 14:54:14 +00:00
|
|
|
|
(if (and command goto-prefix)
|
|
|
|
|
(concat command "\\; " goto-prefix)
|
|
|
|
|
(or command goto-prefix)))))
|
|
|
|
|
keybinds
|
|
|
|
|
"\n")
|
|
|
|
|
#+end_src
|
|
|
|
|
|
2024-03-21 05:20:35 +00:00
|
|
|
|
#+RESULTS[3e58a076aacba87d1cca87db849460e4c2414e94]: gen-keybinds
|
2023-06-02 14:54:14 +00:00
|
|
|
|
: bind-key -T prefix « select-window -p
|
|
|
|
|
: bind-key -T prefix » select-window -n
|
2023-06-15 12:19:10 +00:00
|
|
|
|
: bind-key -T prefix Tab switch-client -T windows
|
|
|
|
|
: bind-key -T prefix w switch-client -T pane
|
2023-06-02 14:54:14 +00:00
|
|
|
|
: bind-key -T prefix y switch-client -T copy-mode
|
|
|
|
|
|
|
|
|
|
#+begin_src tmux
|
|
|
|
|
<<gen-keybinds()>>
|
|
|
|
|
#+end_src
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
*** Panes
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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}" | |
|
2023-06-15 12:19:10 +00:00
|
|
|
|
| =c= | select-pane -L | |
|
|
|
|
|
| =t= | select-pane -D | |
|
|
|
|
|
| =s= | select-pane -U | |
|
|
|
|
|
| =r= | select-pane -R | |
|
2023-07-07 20:29:36 +00:00
|
|
|
|
| =f= | resize-pane -Z | |
|
2023-06-02 14:54:14 +00:00
|
|
|
|
| =.= | | 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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
*** Windows
|
2023-06-15 12:19:10 +00:00
|
|
|
|
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
|
|
|
|
|
|
2023-12-10 14:09:07 +00:00
|
|
|
|
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).
|
2023-06-15 12:19:10 +00:00
|
|
|
|
#+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
|
|
|
|
|
|
2023-09-18 16:45:14 +00:00
|
|
|
|
*** Copy in vi mode
|
2023-06-02 14:54:14 +00:00
|
|
|
|
Tmux has a nice mode for vim keybindings users: =copy-mode-vi= which
|
2023-12-10 14:09:07 +00:00
|
|
|
|
allows moving the cursor around in the pane, select some stuff, and
|
2023-06-02 14:54:14 +00:00
|
|
|
|
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
|