#+title: Tmux #+setupfile: headers #+options: unique-id:t #+html_head: #+html_head: #+html_head: #+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 * Introduction :PROPERTIES: :CUSTOM_ID: Introduction-elnbk9c1huj0 :END: 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 :PROPERTIES: :CUSTOM_ID: Settingsaneconfigurations-7mrbk9c1huj0 :END: 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 :PROPERTIES: :CUSTOM_ID: Windowsandpanesconfiguration-nnelqdd1huj0 :END: 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 labeled 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! :PROPERTIES: :CUSTOM_ID: Plugins-0550rdd1huj0 :END: 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 '<>' #+end_src * Keybindings :PROPERTIES: :CUSTOM_ID: Keybindings-tx80rdd1huj0 :END: First of all, 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 (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: 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 <> #+end_src ** Panes :PROPERTIES: :CUSTOM_ID: KeybindingsPanes-7z99hld1huj0 :END: 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 | | | =.= | | pane-resize | #+begin_src tmux <> #+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 <> #+end_src ** Windows :PROPERTIES: :CUSTOM_ID: KeybindingsWindows-zg3fjes0luj0 :END: 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 <> #+end_src In order to access more easily the different windows, I want to be able to type = TAB =. 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 :PROPERTIES: :CUSTOM_ID: KeybindingsCopyinvimode-2mjfpie1huj0 :END: Tmux has a nice mode for vim keybindings users: =copy-mode-vi= which allows to move 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 <> #+end_src