# Options {{{1 # Server {{{2 # Don't pile up more than 10 buffers (down from 50 by default).{{{ # # Rationale: Keeping a tidy stack, with relevant information, could help us # integrate tmux buffers in our workflow more often. # # However, maybe we could keep a big stack of buffers, and filter them by # pressing `f` in the window opened by `choose-buffer`. # Alternatively, we could also try to use fzf to fuzzy search through their # contents... #}}} set -s buffer-limit 10 # What does this option control?{{{ # # It sets the time in milliseconds for which tmux waits after an escape is input # to determine if it is part of a function or meta key sequences. # The default is 500 millisec onds. #}}} # Why do you reset it?{{{ # # The default value introduces lag when we use Vim and escape from insert to # normal mode. We want to reduce the timeout. #}}} # Why don't you set it to 0 ?{{{ # # > Some people set it to zero but I consider that risky if you're connecting # > over a wide-area network or there is anything else that might insert small # > delays between the delivery of chars in such a sequence. # # Source: https://github.com/tmux/tmux/issues/353#issuecomment-294570322 # # Basically, we should still let a few ms to be sure all the keys in a control # sequence will have enough time to reach tmux. #}}} set -s escape-time 10 # If the terminal supports focus events, they will be requested by the tmux # client and passed through to the tmux server, then to the programs it runs. # Necessary to be able to listen to `FocusGained` and `FocusLost`. # Also necessary for `pane-focus-[in|out]` hooks. set -s focus-events on # history of tmux commands (pfx :) set -s history-file "$HOME/.tmux/command_history" # What does this do?{{{ # # It makes tmux sometimes send an OSC 52 sequence – which sets the terminal # clipboard content – if there is an `Ms` entry in the terminfo description of # the outer terminal. #}}} # What are the possible values of this option?{{{ # # - `on` # - `external` # - `off` # # --- # # There are 3 ways to create a tmux buffer: # # 1. invoke the `set-buffer` or `load-buffer` tmux commands # 2. copy text in copy mode (`send -X copy-selection`, `copy-pipe`, ...) # 3. send an OSC 52 sequence from an application inside tmux (e.g. `$ printf ...`) # # `1.` always creates a tmux buffer; never sets the X clipboard. # `2.` always creates a tmux buffer; sets the X clipboard via OSC 52 iff `set-clipboard` is not `off`. # `3.` creates a tmux buffer *and* sets the X clipboard via OSC 52 iff `set-clipboard` is `on`. # # IOW, `external` makes tmux *automatically* set the X clipboard when you yank # sth in copy mode via OSC 52, while `on` does the same, but also allows an # application to *manually* set a tmux buffer/the X clipboard via a OSC 52. # # Source: https://unix.stackexchange.com/a/560464/289772 #}}} # What is the possible drawback of the value `on`?{{{ # # https://github.com/tmux/tmux/wiki/Clipboard#security-concerns #}}} # How to disable OSC 52 for some terminals, but not all?{{{ # # # or use 'external' # set -s set-clipboard on # set -as terminal-overrides 'yourTERMname:Ms@' # ^ # man terminfo /Similar Terminals/;/canceled #}}} set -s set-clipboard external # Why do you move your 'terminal-overrides' settings in another file?{{{ # # It makes it easier to source the settings only when the tmux server is started; # not when we manually re-source `tmux.conf`. #}}} # Why do you need them to be sourced only once?{{{ # # We *append* strings to the value of the 'terminal-overrides' option. # I don't want to append the same strings again and again every time I re-source # `tmux.conf`. #}}} # Why don't you simply reset the option before appending a value?{{{ # # That would make us lose the default value of the option: # # terminal-overrides[0] "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007:Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007:Ss=\\E[%p1%d q:Se=\\E[2 q" # terminal-overrides[1] screen*:XT # # --- # # Besides, one of the value we append to 'terminal-overrides' depends on the value of `$TERM`. # # if '[ "$TERM" != "st-256color" ]' 'set -as terminal-overrides ",*:Cr=\\E]112\\007"' # ^----------------------^ # # And the value of `$TERM` will be correct only the first time we source `tmux.conf`. #}}} # What's the value of `$TERM` here?{{{ # # The first time our `tmux.conf` is sourced, it matches the `$TERM` of the # outer terminal; the next times, it's the value of 'default-terminal' (i.e. # 'tmux-256color'). #}}} # What's the meaning of this `if` guard?{{{ # # The condition `[ "$TERM" != "tmux-256color" ]` is true only the first time our # `tmux.conf` is sourced. # So, this part of the guard means: “if this is the first time the file is sourced”. # This is equivalent to `has('vim_starting')` in Vim. # # The condition `[ -n "$DISPLAY" ]` is true only in a GUI environment. # So, this part of the guard means: “don't source the file if we are in a console”. # Indeed, I doubt a linux console is able to understand any sequence we might # want to use. #}}} # Could we use `%if` instead of `if`?{{{ # # You could try this: # # %if "#{!=:$TERM,#{default-terminal}}" # source-file "$HOME/.config/tmux/terminal-overrides.conf" # %endif # # But it doesn't seem to work; the guard would not prevent the file from being re-sourced. # I think that's because, in this case, `$TERM` refers to the value in the # environment of the tmux server process; and for the latter, `TERM` always # matches the outer terminal. #}}} if '[ "$TERM" != "#{default-terminal}" -a -n "$DISPLAY" ]' { source "$HOME/.config/tmux/terminal-overrides.conf" } # Leave `default-terminal` at the end.{{{ # # In my limited testing, moving it above would not cause an issue, but better be # safe than sorry. # In particular, I want to be sure that the value of `$TERM` is not # 'tmux-256color' the first time our `tmux.conf` is sourced; otherwise # `terminal-overrides.conf` would never be sourced. #}}} # Why `-s` instead of `-g`? {{{ # # Since tmux 2.1, `default-terminal` is a server option, not a session option. # # > As a side effect this changes default-terminal to be a server rather than a # > session option. # # https://github.com/tmux/tmux/commit/7382ba82c5b366be84ca55c7842426bcf3d1f521 # Confirmed by the fact that `default-terminal` is described in the section of # the server options in the man page. # Also confirmed by the fact that it's listed in the output of: # # tmux show -s # # However, according to nicm: # # > You do not have to use -s or -w for set-option except for user options. # > tmux can work it out from the option name. # > For show-option you do need it. # # So, we could omit `-s`, but I prefer to be explicit. #}}} # Why not let tmux use the default value `screen` (for `$TERM`)?{{{ # # By default, most terminals set `$TERM` to `xterm` because the `xterm` entry is # present and set in the terminfo db of most machines. # tmux sets it to `screen`, again, because it's a popular entry (more than the # `tmux` one). # The `xterm`/`screen` value implies that the terminal will declare supporting # only 8 colors; confirmed by `$ tput colors`. # # Because of this, the theme of some programs might be off (including Vim and # the terminal itself). We want the terminal to declare it supports 256 colors, # which anyway is usually true. #}}} # Do we need `$TERM` to contain `tmux`?{{{ # # Yes. To support italics: # # The `screen-256color` entry in the terminfo db doesn't have a `sitm` field. # IOW, the db reports that screen is unable to support italics, which is true. # So, if we set `$TERM` to `screen-256color`, when an application will want to # make some text appear italicized, it will think it's not possible. # But it *is* possible, because we use tmux, not screen. And tmux *does* # support the italics style. # The solution is to set `$TERM` to `tmux-256color` so that when an application # queries the terminfo db, it finds the field `sitm` with the right value # `\E[3m`. # # See also: # https://github.com/tmux/tmux/wiki/FAQ#i-dont-see-italics-or-italics-and-reverse-are-the-wrong-way-round # https://github.com/tmux/tmux/issues/175#issuecomment-152719805 #}}} # `256color`?{{{ # # For a Vim color scheme to be correctly applied, no. # Because it seems that our current theme automatically sets the number of # colors to 256: # # :runtime colors/seoul256.vim # :echo &t_Co # # But, for the color schemes of other programs, maybe. #}}} set -s default-terminal tmux-256color # Session {{{2 # Don't ring the bell in the current window. set -g bell-action other # Why?{{{ # # If a new window is created without any command to execute, tmux reads the # session option `default-command` to find one. # By default, its value is an empty string which instructs tmux to create a # *login* shell using the value of the default-shell option. # The default value of the latter is `$SHELL` (atm: `/usr/local/bin/zsh`). # # When we create a new window, we want a *non*-login shell, because a login zsh # shell sources `~/.zprofile`, which we use to execute code specific to a # virtual *console* (set the background to white in the console). # This code is not suited to a virtual *terminal*. # # More generally, we don't want a *non*-login shell to source login files # (`.profile`, `.zprofile`, `.zlogin`). # # So, we give the value zsh to `default-command` to prevent tmux from starting a # login shell. #}}} set -g default-command "$SHELL" # Don't detach the client when the current session is killed. set -g detach-on-destroy off # display status line messages and other on-screen indicators for 1s # (or until a key is pressed) set -g display-time 1000 # display the indicators shown by the display-panes command for 5s set -g display-panes-time 5000 # increase scrollback buffer (2000 → 50000) # # `history-limit` has nothing to do with the history of executed tmux commands. # It controls the amount of lines you can scroll back when you enter copy mode. set -g history-limit 50000 # Index options # When we create a new window, tmux looks for an unused index, starting from 0.{{{ # # I prefer 1, because: # # - I usually count from 1, not 0 # - this lets us run `:movew -t :0` to move the current window in first position # # Note that you can't run `:movew -t :1` to move the window in first position, # because 1 is already taken by the first window. # `:movew` expects an index which is “free” (i.e. not used by any existing window). # # Also, note that when running `:movew -t :0`, tmux renumbers all windows # automatically from whatever value is assigned to the 'base-index' option. #}}} set -g base-index 1 # │ # └ must be applied globally to all sessions # # same thing for the panes set -gw pane-base-index 1 # ││ # │└ window option # └ must be applied globally to all windows # make tmux capture the mouse and allow mouse events to be bound as key bindings set -g mouse on # use `M-space` as a prefix set -g prefix M-space # renumber windows, when:{{{ # # - we destroy one of them # - we move the first window (index 1) at the end (index 99), by running `movew -t :99` # # to prevent holes in the sequence of indexes #}}} set -g renumber-windows on # time for repeating of a hotkey bound using the -r flag without having to type the prefix again; default: 500 set -g repeat-time 1000 # Some consoles don't like attempts to set the window title. # This might cause tmux to freeze the terminal when you attach to a session. # https://github.com/tmux/tmux/wiki/FAQ#tmux-freezes-my-terminal-when-i-attach-to-a-session-i-have-to-kill--9-the-shell-it-was-started-from-to-recover set -g set-titles off # update the status line every 5 seconds (instead of 15s by default) set -g status-interval 5 # emacs key bindings in tmux command prompt (prefix + :) are better than vi keys, # even for vim users. set -g status-keys emacs # color of status line set -g -F status-style "bg=#{?#{==:$DISPLAY,},blue,colour138}" # Center the position of the window list component of the status line set -g status-justify centre # cache the number of cpu cores in `~/.ncore`, which a shell command in 'status-right' is going to need if '[ ! -s "${HOME}/.ncore" ]' \ { run "lscpu | awk '/^CPU\\(s\\):\\s*[0-9]/ { print $2 }' >\"${HOME}/.ncore\"" } # set the contents of the status line # What's `#[...]`?{{{ # # It lets you embed some styles. # If you want to apply the same style all over the left part or the right part # of the status line, you can also use `status-left-style` or `status-right-style`: # # set -g status-left '#[fg=colour15,bold] #S' # ⇔ # set -g status-left ' #S' # set -g status-left-style '#[fg=colour15,bold]' # # However, I prefer embedding the styles inside the value of `status-left` and # `status-right`, because: # # - it's more concise # - it's more powerful: you can set the style of an arbitrary *portion* of the status line # # --- # # Note that you can use this syntax only in the value of an option which sets # the *contents* of sth, not its style. # So, this is *not* a valid syntax: # # # ✘ # set -g status-left-style '#[fg=colour15,bold]' # # Here, you must get rid of `#[...]`: # # # ✔ # set -g status-left-style 'fg=colour15,bold' #}}} # What's `#{?...}`?{{{ # # A conditional: # # #{?test,val1,val2} # # For example: # # {?client_prefix,#[bold],} # # This will be evaluated into the style `bold` if the prefix has been pressed, # or nothing otherwise. #}}} # Why do you use `nobold`?{{{ # # We set the style `bold` for some part of the status line. # But a style applies to *all* the remaining text in the status line. # We need `nobold` to reset the style. #}}} # How can I include the time of the day or the hour in the status line?{{{ # # Use `date(1)` and `%` items: # # %a = day of week # %d = day of month # %b = month # %R = hour # # See `man date`. #}}} set -g status-left ' #[fg=colour7]#{?client_prefix,#[fg=colour0],}#S#[fg=colour7]' # Which alternative could I use to get the cpu load?{{{ # # $ uptime|awk '{split(substr($0, index($0, "load")), a, ":"); print a[2]}' # # https://github.com/tmux/tmux/wiki/FAQ#what-is-the-best-way-to-display-the-load-average-why-no-l #}}} # Why don't you write the code in a script and invoke it with `#(my-script.sh)`?{{{ # # A script needs another shell to be interpreted. # The latter adds overhead, which would almost double the time the code needs to # be run. # # Check this: # # $ cat <<'EOF' >/tmp/sh.sh # awk 'BEGIN { getline load <"/proc/loadavg"; getline ncore <(ENVIRON["HOME"]"/.ncore"); printf("%d", 100 * load / ncore) }' # EOF # $ chmod +x /tmp/sh.sh # # $ time zsh -c 'repeat 100 /tmp/sh.sh' # ... 0,420 total˜ # $ time zsh -c 'repeat 100 awk '\''BEGIN { getline load <"/proc/loadavg"; getline ncore <(ENVIRON["HOME"]"/.ncore"); printf("%d", 100 * load / ncore) }'\''' # ... 0,193 total˜ #}}} # Why do you double the percent sign in `printf("%%d", ...)`?{{{ # # The code is going to be expanded inside the value of 'status-right'. # And the latter is always passed to `strftime(3)` for which the percent sign # has a special meaning. # As a result, if you don't double the percent sign – to make it literal – `%d` # will be replaced with the current day of the month (01, 02, ..., 31). # # From `man tmux /status-right`: # # > As with status-left, string will be passed to strftime(3) and character # > pairs are replaced. #}}} cpu='awk '\''BEGIN { \ getline load <"/proc/loadavg"; \ getline ncore <(ENVIRON["HOME"]"/.ncore"); \ printf("%%d", 100 * load / ncore) }'\''' mem='free | awk '\''/Mem/ { total = $2; used = $3 }; \ END { printf("%%d", 100 * used / total) }'\''' # TODO: Maybe we could get rid of `free(1)`, by inspecting `/proc/meminfo`. # However, I can find the second field ("total") of `free(1)` in this file # (first line: "MemTotal"), but not the third one ("used"). # # Update: From `man free`: # # used Used memory (calculated as total - free - buffers - cache) # # You would have to read 4 files to compute the `used` field. # Make some tests (with `time(1)`) to see whether the resulting `awk(1)` command # is slower than what we currently run. # TODO: Color the numbers in red if they exceed some threshold. # Try to capture the output of the 2 shell commands in tmux variables, so that # we can test them in a conditional. set -g status-right '#[fg=colour235]M #[fg=colour15,bold]' set -ga status-right "#($mem) " set -ga status-right '#[fg=colour235,nobold]C #[fg=colour15,bold]' set -ga status-right "#($cpu) " setenv -gu cpu setenv -gu mem # Why do you want `COLORTERM` to be automatically updated?{{{ # # It can be useful to detect a terminal which lies about its identity. # E.g., xfce4-terminal advertises itself as `xterm-256color`, but the value of # its `COLORTERM` variable is 'xfce4-terminal'. # # So, the more reliable `COLORTERM` is, the better we can detect that we're in # xfce4-terminal, and react appropriately. # This can be useful, for example, to prevent vim-term from sending `CSI 2 SPC q` # when we're leaving Vim from xfce4-terminal on Ubuntu 16.04. # The latter doesn't understand this sequence, and once sent to the terminal, # tmux will regularly reprint the sequence wherever our cursor is. #}}} # Why could I be tempted to run the same command for `LESSOPEN`, `LESSCLOSE`, `LS_COLORS`?{{{ # # setenv -gu LESSOPEN # setenv -gu LESSCLOSE # setenv -gu LS_COLORS # # These environment variables are set in `~/.zshenv`, but only on the condition # they've not been set yet. # The purpose of the condition is to make the shell quicker to start. # Indeed, setting these variables adds around 8ms to the shell's startup time. # However, if they are set in the tmux global environment, then they'll never be # reset when we start a new shell, because the condition will never be # satisfied. # # This means that we can't change the value of these variables by simply editing # `~/.zshenv`, which can be an issue. #}}} # Why don't you do it?{{{ # # Because it would add around 8ms to the startup time of every shell we ask tmux # to open. #}}} #  Isn't this an issue?{{{ # # No. # If you want to modify one of these variables, and if you want the change to be # applied immediately without restarting the tmux server, do it in the context # of the tmux global environment: # # $ tmux setenv -g LESSOPEN new_value #}}} if '[ "$TERM" != "#{default-terminal}" ]' { set -ga update-environment COLORTERM } # TODO: We have several similar `if` conditions. Maybe we should write only one, # and put inside all the commands we want to run. # This would make tmux start a little faster. # display a message when activity is detected in a window # Why?{{{ # # We haven't customized the status line to include an indicator when some # activity is detected in a window, because by default we don't monitor activity # ('monitor-activity' is off). # Indeed, generally, most windows will produce some output and have some activity. # Seeing a lot of indicators in the status line, all the time, is meaningless. # # But we do have a key binding to temporarily toggle 'monitor-activity' in the # current window; so, we need a way to be notified when some activity is later # detected in it. #}}} set -g visual-activity on # Window {{{2 # Use vi key bindings in copy mode. set -gw mode-keys vi # colors of *borders* of focused and non-focused panes set -gw pane-active-border-style 'fg=colour138,bg=#cacaca' set -gw pane-border-style 'fg=colour138,bg=#cacaca' # How to insert the index of a window?{{{ # # Use the alias `#I`. #}}} # How to insert its flags, like `Z` for a zoomed window?{{{ # # Use the alias `#F`. #}}} # Set what to display for the current window (then for the other ones), and how, # in the status line window list. # See: https://github.com/tmux/tmux/issues/74#issuecomment-129130023 set -gw window-status-current-format '#[fg=colour232,bg=colour253]#W#F#{?window_zoomed_flag, #[fg=blue]#P/#{window_panes},}' set -gw window-status-format '#[fg=colour232#,bg=colour248]#W#F#[bg=default]' # Pane {{{2 # colors of focused and non-focused panes # Because we use `-gw`, the colors will affect any pane.{{{ # # But, at runtime, you could use `-p` to set the color of a given pane when it's # (un)focused differently. #}}} set -gw window-active-style 'bg=#dbd6d1' set -gw window-style 'bg=#cacaca' # }}}1 # Key Bindings {{{1 # root {{{2 # `F1`, ..., `F10` are used in `htop(1)`. # `F11` and `F12` are used in WeeChat to scroll in the nicklist bar. bind -T root S-F8 run -b 'tmux_log' # Why do you rebind `command-prompt` to `pfx + ;`? It's already bound to `pfx + :`...{{{ # # We often press the prefix key by accident, then press `:` to open Vim's # command-line. As a result, we open tmux command-line; it's distracting. #}}} bind ';' command-prompt unbind : # Do *not* bind `command-prompt` to `M-:`; we press it by accident too frequently. # focus next/previous window # I'm frequently pressing these key bindings in Vim's insert mode. It's distracting!{{{ # # Idea1: Use `pfx w` to focus an arbitrary window, and supercharge `pfx h/l` to # focus the next/previous pane or window. # # bind -r h if -F '#{pane_at_left}' 'prev' 'selectp -L' # bind -r l if -F '#{pane_at_right}' 'next' 'selectp -R' # # Idea2: Use `M-h/l` twice when the current pane is running Vim. # # set -g @foo 0 # bind -T root M-l if -F '#{m:*vim,#{pane_current_command}}' { if -F '#{@foo}' { set -g @foo 0 ; next } { set -g @foo 1 } } { next } # bind -T root M-h if -F '#{m:*vim,#{pane_current_command}}' { if -F '#{@foo}' { set -g @foo 0 ; prev } { set -g @foo 1 } } { prev } # # Idea3: Make tmux send `M-h` or `M-l` when Vim is running in the current pane, # and let Vim decide what to do, based on whether we're in insert mode or normal # mode. # # noremap! m_hl('h') # noremap! m_hl('l') # nno m_hl('h') # nno m_hl('l') # xno m_hl('h') # xno m_hl('l') # # fu s:m_hl(seq) abort # if mode() =~# '^[ic]$' # return '' # endif # call system('tmux '..(a:seq is# 'l' ? 'next' : 'prev')) # return '' # endfu # bind -T root M-l if -F '#{m:*vim,#{pane_current_command}}' 'send M-l' 'next' # bind -T root M-h if -F '#{m:*vim,#{pane_current_command}}' 'send M-h' 'prev' # # Problem: When we're running Vim without config (`-Nu NONE`), we can't focus another tmux window. # This is because those custom mappings are not installed then. #}}} # Why do you inspect these window flags?{{{ # # To prevent the commands from wrapping around the edges. # They do that by default, and that bothers me at the moment. # # For example, I often want to focus the next window, and press `¹` by accident # instead of `²`. It still works, because we only have 2 windows, but that # prevents me from learning to press the correct keys. # # Once you're confident that those keys are well-chosen, and they've passed into # your muscle memory, you could just install: # # bind -T root ¹ prev # bind -T root ² next #}}} bind -T root ¹ if -F '#{window_start_flag}' {} { prev } bind -T root ² if -F '#{window_end_flag}' {} { next } # TODO: Sometimes, we press these keys by accident. Find better ones?{{{ # # In particular, when we press `[ox` (where `x` is some character) to toggle # some Vim setting, we need to press `AltGr` to produce `[`; but sometimes, we # don't release the key before pressing `o`. # As a result, we press `AltGr + o` which produce `²`; tmux intercepts the # keypress, and tries to visit the previous window, while in reality, we wanted # to toggle some Vim setting. #}}} # `M-s` to enter copy mode # Do *not* bind `M-s` to anything while in copy mode!{{{ # # This would make you lose an interesting feature of the `copy-mode` command # while you're already in copy mode, reading the output of some command such as # `list-keys`. # # The default behavior makes tmux show you the contents of the original window: # # # you're reading a file # :list-keys # :copy-mode # # the window shows again the file you were reading # # press `q`, and you get back the output of `:list-keys` # # I don't know where this is documented. # And I don't know why the `copy-mode` command is invoked when we press `M-s` # while in copy mode. # We only have one key binding using `M-s` as its lhs, and it's in the root # table, not in the copy-mode table. # # Note that this feature is not specific to our `M-s` key binding. # I can reproduce with no config (and `C-b [` instead of `M-s`). #}}} bind -T root M-s copy-mode bind -T root M-z resize-pane -Z bind -T root M-Z lastp \; resize-pane -Z # Do *not* exit copy mode when when we reach the bottom of the scrollback buffer.{{{ # # We remove the `-e` flag which is passed to `copy-mode` by default, so that if # we enter copy mode by scrolling the mouse wheel upward, and we press a key # which reaches the bottom of the scrollback buffer, we don't quit copy mode. # # If you leave `-e` in the default key binding, here's what could happen: # you scroll the wheel upward to enter copy mode; at one point, you keep # pressing `C-d` to scroll toward the bottom; once you reach the bottom, you'll # quit copy mode, and `C-d` will close the shell if the command-line is empty. #}}} # Where did you find the code?{{{ # # $ tmux -Lx -f/dev/null start \; lsk | grep 'root.*WheelUpPane' #}}} bind -T root WheelUpPane \ if -F -t= '#{mouse_any_flag}' \ { send -M } \ { if -Ft= '#{pane_in_mode}' 'send -M' 'copy-mode -t=' } # But *do* exit copy mode if we scroll downward with the mouse wheel and reach the bottom of the buffer. bind -T copy-mode-vi WheelDownPane \ selectp \; \ send -X -N 5 scroll-down \; \ if -F '#{scroll_position}' '' 'send -X cancel' # copy-mode-vi {{{2 bind -T copy-mode-vi C-Space send -X set-mark # actually, it should be named "exchange-point-and-mark"... bind -T copy-mode-vi C-x send -X jump-to-mark # jump Back to the Beginning of the previous shell command{{{ # # Look for the previous shell prompt, to get to the beginning of the last # command output. After pressing the key binding, you can visit all the other # prompts by pressing `n` or `N`. # # Inspiration: https://www.youtube.com/watch?v=uglorjY0Ntg #}}} bind -T copy-mode-vi ! send -X start-of-line \; send -X search-backward '٪' # Make tmux use the prefix 'buf_' instead of 'buffer' when naming the buffer # storing the copied selection. bind -T copy-mode-vi Enter send -X copy-selection-and-cancel 'buf_' bind -T copy-mode-vi g switchc -T g-prefix bind -T g-prefix g send -X history-top # Open a visually selected text (filepath or url) by pressing `gf` or `gx`. # TODO: The key binding will break if the file name contains double quotes.{{{ # # Find a way to escape special characters. # # I tried `$ tmux display -p '#{q:#(tmux pasteb)}'`, but it doesn't work. # You probably need `q:`, but a modifier needs to be followed by the name of a # replacement variable and `#(tmux pasteb)` is not one. # # Update: # I don't think you should use `q:`. # Maybe you should try to find a shell utility which quotes special characters # in a given text. # Does such a tool exist? # If it does, maybe you could try: `#(magic_tool $(tmux pasteb))`. # # Update: # `#{}` can be used for a user option (`@foo`). # You could temporarily set a user option with the the filepath/url, and quote # it with `q:`. # # Make some tests on that: # # http://example.org/foo/bar"baz.html # http://example.org/foo/bar'baz.html # https://www.reddit.com/r/Fantasy/ # # bind -T copy-mode-vi x send -X copy-selection-and-cancel \; \ # run 'tmux set @copied_url "$(tmux showb)"' \; \ # run 'xdg-open "#{q:@copied_url}"' # # For some reason, we need 2 `run-shell`, otherwise, it seems that the key # binding doesn't update the url, when we try a new one. # # For some reason, when we try to open this: # # http://example.org/a'b.html # # tmux opens this instead: # # http://example.org/a/'b.html # ^ # ✘ # # For some reason, when we try to open this: # # http://example.org/a"b.html # # tmux doesn't escape the double quote. # # Text ended before matching quote was found for ".˜ # (The text was '/usr/bin/firefox "http://example.org/foo/bar"baz.html"')˜ # # $ tmux set @foo "a'b" \; display -p '#{q:@foo}' # a\'b˜ # # $ tmux set @foo "a'b" \; run 'echo #{q:@foo}' # a'b˜ # # Why doesn't `run-shell` expand the `#{q:}` format? # # Update: you need to quote the format: # # $ tmux set @foo "a'b" \; run 'echo "#{q:@foo}"' # a\'b˜ # # Update: # I don't think it's possible. # Try to open the urls via `urlscan(1)`. # The latter fails for the first urls. # If a specialized tool fails, I doubt we can do better. #}}} # FIXME: `gf` fails to open a file path starting with `~/`. bind -T g-prefix f send -X pipe "xargs -I {} tmux run 'xdg-open \"{}\"'" bind -T g-prefix x send -X pipe "xargs -I {} tmux run 'xdg-open \"{}\"'" bind -T copy-mode-vi v if -F '#{selection_present}' { send -X clear-selection } { send -X begin-selection } bind -T copy-mode-vi V if -F '#{selection_present}' { send -X clear-selection } { send -X select-line } bind -T copy-mode-vi C-v if -F '#{selection_present}' \ { if -F '#{rectangle_toggle}' \ { send -X rectangle-toggle ; send -X clear-selection } \ { send -X rectangle-toggle } \ } { send -X begin-selection ; send -X rectangle-toggle } # set -s copy-command 'xsel -i' # if there's a selection, make `y` yank it # without selection, make `yy` yank the current line and `yiw` the current word bind -T copy-mode-vi y if -F '#{selection_present}' \ { send -X copy-pipe-and-cancel 'xsel -i -b' 'buf_' } \ { switchc -T operator-pending-and-cancel } # y**y** bind -T operator-pending-and-cancel y send -X copy-line 'buf_' # y**iw** bind -T operator-pending-and-cancel i switchc -T text-object-and-cancel # Do *not* use `select-word`: https://github.com/tmux/tmux/issues/2126 bind -T text-object-and-cancel w \ { send -X cursor-right send -X previous-word send -X begin-selection send -X next-word-end send -X copy-selection-and-cancel 'buf_' } bind -T copy-mode-vi . run "zsh -c \"tmux source =(sed -n '/^# #{@dot_command}/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" # **"A**yiw **"A**yy v_**"A**y bind -T copy-mode-vi '"' switchc -T specify-register bind -T specify-register A switchc -T operate-on-register # Why the `if 'tmux showb'`?{{{ # # `append-selection` doesn't accept the optional prefix buffer name argument. # If there's no buffer, `append-selection` will create a buffer with the prefix # name `buffer`; we want the prefix `buf_`. #}}} # "A**y**iw "A**y**y v_"A**y** bind -T operate-on-register y if -F '#{selection_present}' \ { if 'tmux showb' { send -X append-selection } { send -X copy-selection 'buf_' }} \ { switchc -T operator-pending } # "Ay**i**w bind -T operator-pending i switchc -T text-object # "Ay**y** bind -T operator-pending y { run "zsh -c \"tmux source =(sed -n '/^# yy/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" } # v**i**w bind -T copy-mode-vi i switchc -T text-object # Why pass a second command to the first `if`?{{{ # # To support `"Ayiw`. #}}} # vi**w** "Ayi**w** bind -T text-object w if -F '#{selection_present}' \ { send -X stop-selection send -X cursor-right send -X previous-word send -X begin-selection send -X next-word-end } \ { run "zsh -c \"tmux source =(sed -n '/^# yiw/,/^$/p' $HOME/.config/tmux/repeat.conf)\"" } bind -T copy-mode-vi Y send -X copy-end-of-line 'buf_' # Why don't you pass `-b` to run?{{{ # # There's no need to. # `pipe-and-cancel` doesn't block. # The shell command passed as an argument is forked. #}}} bind -T copy-mode-vi S send -X pipe-and-cancel \ "xargs -I {} tmux run 'xdg-open \"https://www.startpage.com/do/dsearch?query={}\"'" # Why? {{{ # # `search-backward-incremental` is better than `search-forward`, because it # highlights all the matches as you type (like in Vim when 'hlsearch' and # 'incsearch' are both set); you need to pass `-i` to `command-prompt` for it to work. # # Also, these key bindings make the prompt less noisy (`/` is shorter than `search down`). # # Inspired from the default emacs key bindings in copy mode. # }}} bind -T copy-mode-vi / command-prompt -ip '/' { send -X search-forward-incremental '%%' } bind -T copy-mode-vi ? command-prompt -ip '?' { send -X search-backward-incremental '%%' } bind -T copy-mode-vi % send -X next-matching-bracket bind -T copy-mode-vi _ send -X start-of-line # move current window position forward/backward # Why not using `h` and `l`, or `j` and `k`?{{{ # # `M-C-[jk]` could be more useful for something else (WeeChat?), and doesn't # match a horizontal motion. # `M-C-[hl]` conflicts with our window manager (move virtual desktop). # `M-C-[HL]` is hard to press. #}}} bind -r > if -F '#{window_end_flag}' { movew -t :0 } { swapw -t :+ ; selectw -n } bind -r < if -F '#{window_start_flag}' { movew -t :99 } { swapw -t :- ; selectw -p } # If you don't like `:99`, you could write this instead:{{{ # # bind -r < if -F '#{window_start_flag}' \ # { run 'tmux movew -t:$((#{W:#{?window_end_flag,#I,}}+1))' } \ # { swapw -t :- ; selectw -p } #}}} # prefix {{{2 # NOTE: `C-\` is free. # cycle through predefined layouts # Do *not* pass `-r` to `bind`!{{{ # # We use Space in Vim as a prefix key. # If you use `-r` here, when you're in Vim there's a risk that when you press # Space as a prefix key, it's consumed by tmux to run `nextl` instead. #}}} bind Space nextl # focus last pane, without breaking the zoomed state of the window bind M-Space lastp -Z # display short description for the next keypress (inspired from a default key binding) bind M-h command-prompt -k -p key { lsk -1N '%%' } # ├┘ ├─┘{{{ # │ └ -N instead show keys and attached notes in the root and prefix key tables; # │ with -1 only the first matching key and note is shown # └ -k is like -1 but the key press is translated to a key name #}}} # What is the side effect of `-1`?{{{ # # Not only does it limit the output of the command to the first matching key and # note, but it also redirects where it's displayed. # Without `-1`, it's displayed on the terminal in copy-mode. # With `-1`, it's displayed as a message on the tmux status line. #}}} # display short description for the next 2 keypresses bind M-l command-prompt -1p 'key1,key2' \ { run "tmux lsk | awk '/-T prefix\s+%1\s+/ { print \$NF }' | xargs -I {} tmux lsk -1N -P 'M-Space %1 ' -T {} '%2'" } # repeat last shell command in last active pane # Warning: This overrides a default key binding:{{{ # # bind-key -T prefix . command-prompt -T target "move-window -t '%%'" #}}} bind . if -F -t '{last}' '#{m:*sh,#{pane_current_command}}' { send -t '{last}' Up Enter } # copy clipboard selection into tmux buffer # Why `run`?{{{ # # To make the shell evaluate the command substitution. #}}} # `--`?{{{ # # The evaluation of the substitution command could start with a hyphen. # And if that happens, tmux could parse the text as an option passed to # `set-buffer` (i.e. `-a`, `-b`, or `-n`). #}}} # `-o`?{{{ # # To make `xsel(1x)` output something. #}}} bind b switchc -T buffer bind -T buffer -N 'copy clipboard into tmux buffer' > run 'tmux setb -- "$(xsel -ob)"' \; display 'clipboard copied into tmux buffer' bind -T buffer -N 'copy tmux buffer into clipboard' < choose-buffer -F '#{buffer_sample}' \ { run 'tmux showb -b "%%" | xsel -ib' ; display 'tmux buffer copied into clipboard' } # We use `*` instead of `q` because it's more consistent with `#`. # They both show information. Besides, if I hit `pfx q` by accident (which # happens often), I won't be distracted by the panes numbers. bind * displayp bind ! show-messages # make these default key bindings repeatable # Do *not* pass `-r` to `bind`!{{{ # # Suppose you're in the 'study' session, Vim is running, and the Vim window is # horizontally split in 2 viewports. # You press `pfx + )` to switch to the 'fun' session, then you press `)` to go # back to the 'study' session. # Finally you press C-j to focus the Vim split below; it won't work because of this: # # bind -r C-j resizep -D 5 #}}} bind ( switchc -p bind ) switchc -n # Problem: tmux-fingers doesn't let us search outside the current screen. # Solution: Install a key binding which lets us search through the scrollback buffer, in a Vim buffer. # What does `-J` do for `capture-pane`?{{{ # # It joins wrapped lines. # # Suppose that we have a long line in a file, which doesn't fit on a single line # of the terminal, but on two. # If you run `$ cat file`, this too-long line will be displayed on two # consecutive lines of the terminal. # Without `-J`, tmux would copy – in one of its buffers – the two consecutive # lines on two different lines. # # But that's not what we want. # We want the buffer to join back these two lines, as they were originally in the file. #}}} # `-S -`? {{{ # # `-S` specifies the starting line number, from where to copy. # The special value `-` refers to the start of the history. # Without this, `capture-pane` would capture only from the first visible line in # the pane; we want the *whole* scrollback buffer. #}}} # Why don't you use `split-window` instead of `popup`?{{{ # # With `split-window`, you would also need to run `resize-pane -Z`. # But what if there's already a zoomed pane in the current window? # After quitting Vim, the latter would be unzoomed. # So, we use `popup` to preserve a possible zoomed pane in the current window. #}}} bind -T root M-c if -F \ '#{||:#{m:*vim,#{pane_current_command}},#{==:#{pane_current_command},man}}' \ '' \ { capture-pane -b scrollback -J -S - popup -E -xC -yC -w75% -h75% -d '#{pane_current_path}' \ "tmux showb -b scrollback | vim --not-a-term +'call tmux#formatCapture#main()' - ; \ tmux deleteb -b scrollback" } # split window vertically / horizontally # When the window is zoomed, we often forget that it's already split, and wrongly press `pfx _`.{{{ # # Make `pfx _` smarter; i.e. if the window is already split and zoomed, don't # split it again, instead focus the previous pane. #}}} bind _ if -F '#{window_zoomed_flag}' 'lastp' 'split-window -v -c "#{pane_current_path}"' bind | if -F '#{window_zoomed_flag}' 'lastp' 'split-window -h -c "#{pane_current_path}"' bind - if -F '#{window_zoomed_flag}' 'lastp' 'split-window -fv -c "#{pane_current_path}"' bind '\' if -F '#{window_zoomed_flag}' 'lastp' 'split-window -fh -c "#{pane_current_path}"' # ├───────────────────────┘ # └ keep current working directory bind -N 'bring arbitrary pane in current window' [ command-prompt -p 'join pane from:' { join-pane -s '%%' } bind -N 'send current pane in arbitrary window' ] command-prompt -p 'send pane to:' { join-pane -t '%%' } bind T breakp # Why a space before every shell command (` cmus`, ` weechat`, ...)?{{{ # # It's useful to prevent zsh from saving the command in the history when we # cancel the search with `C-c` or `C-d` (`setopt HIST_IGNORE_SPACE`). #}}} # What's the `-n` option passed to `neww`?{{{ # # It sets the name of the window. #}}} # What about the `-c` option?{{{ # # It sets the cwd of the shell. #}}} bind M-1 rename -t 0 fun \; \ renamew -t 1 music \; \ send ' cmus' 'Enter' '2' 'Enter' 'Enter' \; \ neww -n irc -c $HOME \; \ send ' weechat' 'Enter' \; \ new -s study \; \ send ' nv' 'Enter' # you need the display command to force tmux to clear the log (`set message-limit 0` is not enough) bind C set -F @message-limit-save '#{message-limit}' \; \ set message-limit 0 \; display 'message log cleared' \; \ set -F message-limit '#{@message-limit-save}' \; set -u @message-limit-save # Note that `clear-history` doesn't clear *all* the history.{{{ # # The last lines of the scrollback buffer which fits in one screen are preserved. # So, if you enter copy mode, you'll still be able to scroll back *some* lines, # but not more than a few dozens. #}}} # We can't use `C-l` for the lhs, because we already use it in another key binding:{{{ # # bind -r C-l resizep -R 5 #}}} bind C-c send C-l \; clear-history # │ │ # │ └ clear tmux scrollback buffer # └ clear terminal screen # Do *not* use `q` in the prefix table; pressed by accident too frequently. bind Q switchc -T Q-prefix bind -T Q-prefix q confirm 'kill-pane' bind -T Q-prefix Q confirm 'kill-window' # Why `TERM="#{client_termname}"` in the 'sourced files' entry?{{{ # # When `$ tmux -v -Ldm` is started, it inherits the TERM of the current tmux # server, which is set by 'default-terminal'. # As a result, the condition `[ if '[ "$TERM" != #{default-terminal} ]'` is # true, and several files which we expect to be sourced, won't be sourced. # # We want to see all files which would be sourced if we were to start tmux from # a regular shell; so we need to reset TERM. #}}} # Why don't you pass `-v` to `show-options`?{{{ # # If you do, it would considerably simplify our commands; we wouldn't need `sed(1)` at all. # Unfortunately, it would also fuck up the output if one of the alias is defined # on several lines. # # By avoiding `-v`, we make sure that each alias is output on a single line. #}}} bind i display-menu -x 0 -y 0 \ 'server information' i info \ 'key bindings' K lsk \ 'aliases' a { run 'tmux show -s command-alias | column -t -s= | sed "s/^command-alias\[[0-9]*]\s*//; s/^\"\|\"$//g"' } \ 'sourced files' f { run 'cd "$(mktemp -d /tmp/.tmux.XXXXXXXXXX)" \ ; TERM="#{client_termname}" tmux -v -Ldm start \ ; grep loading tmux-server*.log | grep -v grep | sed "s/.*loading \(.*\)/\1/"' } \ '' \ 'options' o { display-menu -y 0 \ 'server options' C-s { show -s } \ 'global session options' s { show -g } \ 'local session options' S { show } \ '' \ 'global window options' w { show -gw } \ 'local window options' W { show -w } \ 'local pane options' p { show -p } \ } \ '' \ 'global hooks' h { show-hooks -g } \ 'local hooks' H { show-hooks } \ '' \ 'global environment' e { showenv -g } \ 'local environment' E { showenv } \ '' \ 'outer terminfo description' t { run 'infocmp -1x "#{client_termname}" | sort' } \ 'inner terminfo description' T { run 'infocmp -1x "#{default-terminal}" | sort' } \ '' \ 'default settings' d { display-menu -y 0 \ 'key bindings' K { run 'tmux -Ldm -f/dev/null start \; lsk' } \ 'aliases' a { run 'tmux -Ldm -f/dev/null start \; show -sv command-alias | column -t -s= | sed "s/^command-alias\\[[0-9]]\\s*//; s/^\"\|\"$//g"' } \ '' \ 'server options' C-s { run 'tmux -Ldm -f/dev/null start \; show -s' } \ 'window options' w { run 'tmux -Ldm -f/dev/null start \; show -gw' } \ 'session options' s { run 'tmux -Ldm -f/dev/null start \; show -g' } \ } # By default, `detach-session` is bound to `d`. # I find that too easy to press, so we move it to `@`. # Why `@`? # I didn't find anything better, and it seems hard to press by accident... bind @ detach # resize pane bind -r C-h resizep -L 5 bind -r C-j resizep -D 5 bind -r C-k resizep -U 5 bind -r C-l resizep -R 5 # focus neighboring panes # Do *not* make them repeatable. It leads to a confusing user experience.{{{ # # Example: Press `pfx k` to focus the pane above which is running Vim, then press `j`. # Expected: Vim scrolls downward. # Actual Result: tmux focuses back the pane below. # # If you have many panes, and you need to focus one, try `display-panes` # (currently bound to `pfx *`), then press the index of the desired pane. #}}} bind h selectp -L bind l selectp -R bind j selectp -D bind k selectp -U # move pane to the far right/left/bottom/top bind H split-window -fhb \; swap-pane -t ! \; kill-pane -t ! bind L split-window -fh \; swap-pane -t ! \; kill-pane -t ! bind J split-window -fv \; swap-pane -t ! \; kill-pane -t ! bind K split-window -fvb \; swap-pane -t ! \; kill-pane -t ! # Toggle mouse. # Temporarily preventing tmux from handling the mouse can be useful in some # terminals to copy text in the clipboard. # Why `M` for the lhs?{{{ # # It provides a good mnemonic for “mouse”. # I don't use `C-m` nor `M-m` because, atm, they're used to display some default menus. # # However, note that `pfx M` is used by default to clear the marked pane (`select-pane -M`). # It's not a big deal to lose it, because we can get the same result by focusing # the marked pane and then pressing `pfx m` (`select-pane -m`). # The latter marks the pane if it's not already, or clears the mark otherwise. #}}} bind M set -g mouse \; display 'mouse: #{?#{mouse},ON,OFF}' # toggle 'monitor-activity' in current window bind C-a set -w monitor-activity \; display 'monitor-activity: #{?#{monitor-activity},ON,OFF}' # kill all panes except the current one (similar to `:only` or `C-w o` in Vim) bind o kill-pane -a # paste last tmux buffer # Do *not* choose a key too easy to type.{{{ # # It's dangerous. # In Vim, the contents of the buffer will be typed, which will have unexpected # results, unless you're in insert mode. #}}} bind C-p paste-buffer -p # choose and paste arbitrary tmux buffer # What's `-Z`?{{{ # # It makes tmux zoom the pane so that it takes the whole window. #}}} # `-F`?{{{ # # It specifies the format with which each buffer should be displayed. # In it, you can use these replacement variables: # # ┌────────────────┬─────────────────────────────┐ # │ buffer_created │ creation date of the buffer │ # ├────────────────┼─────────────────────────────┤ # │ buffer_name │ name of the buffer │ # ├────────────────┼─────────────────────────────┤ # │ buffer_sample │ starting text of the buffer │ # ├────────────────┼─────────────────────────────┤ # │ buffer_size │ size of the buffer │ # └────────────────┴─────────────────────────────┘ # # Note that even with an empty format, tmux will still display the name of a # buffer followed by a colon. # So, `buffer_name` is not very useful (unless you want to print the name of a # buffer twice). #}}} # the `-p` argument passed to `paste-buffer`?{{{ # # It prevents the shell from automatically running a pasted text which contains # a newline. # # See `man tmux /^\s*paste-buffer` #}}} bind p choose-buffer -Z -F '#{buffer_sample}' "paste-buffer -p -b '%%'" # similar to `C-w r` and `C-w R` in Vim bind -r r rotate-window -D \; selectp -t :.+ bind -r R rotate-window -U \; selectp -t :.- # reload tmux config bind C-r source "$HOME/.config/tmux/tmux.conf" \; display 'Configuration reloaded' # You need to install the `urlscan(1)` utility for this key binding to work. # Why do you include `deleteb` inside the shell cmd run by `split-window`?{{{ # # If you move it outside: # # bind u capture-pane \; split-window -l 10 'urlscan =(tmux showb)' \; deleteb # # `urlscan(1)` can't find any link. # # This is because: # # 1. deleteb is run before `$ tmux showb` # 2. thus `$ tmux showb` outputs nothing # 3. urlscan finds no url # # You can get the same effect by running: # # $ tmux split-window 'urlscan =(echo "")' #}}} # We name the tmux buffer so that we can remove it reliably at the end.{{{ # # Indeed, you might copy some text while the urlscan pane is opened (not # necessarily in the latter; in any session, window, pane), creating a new # buffer at the top of the stack. # If you just run `$ tmux deleteb`, it would remove that buffer instead of the # buffer created by `capture-pane`. #}}} # Why `head -c -1`?{{{ # # If there is no url in the tmux buffer, we want tmux to automatically close the # pane. That's why we use `ifne(1)` later; it runs the second `urlscan(1)` on # the condition that the output of the previous one is empty. The problem is # that even when the first `urlscan(1)` fails to find any url, it still outputs # a single newline. We need to remove it, so that `ifne(1)` works as expected. #}}} bind u capture-pane -b urlscan \; \ split-window -l 10 " tmux showb -b urlscan | \ urlscan --no-browser | \ head -c -1 | \ ifne urlscan --compact \ --dedupe \ --nohelp \ --regex \"(http|ftp)s?://[^ '\\\">)}\\\\]]+\" \ ; tmux deleteb -b urlscan " # focus next pane bind -r C-w selectp -t :.+ # similar to `C-w x` in Vim bind x swap-pane -U bind X swap-pane -D # }}}1 # Hooks {{{1 # Don't use this hook: `set-hook -g after-split-window 'selectl even-vertical'`{{{ # # You wouldn't be able to split vertically anymore. # Splitting vertically would result in an horizontal split no matter what. # # The hook is given as an example in `man tmux`; its purpose is to resize # equally all the panes whenever you split a pane horizontally. #}}} set-hook -g pane-focus-out '' # Plugins {{{1 # Why the guard?{{{ # # To prevent the plugins from re-installing their key bindings every time we # resource `tmux.conf`. # Indeed, we only unbind the key bindings from the copy-mode table once. # # Besides, it's probably a bad idea to resource plugins. #}}} if '[ "$TERM" != "#{default-terminal}" ]' { source "$HOME/.config/tmux/plugins/run" } # Rebind {{{1 # Purpose:{{{ # # The tmux-yank plugin installs this key binding: # # bind-key -T copy-mode-vi Y send-keys -X copy-pipe-and-cancel "tmux paste-buffer" # # It copies the selection, quit copy mode, then paste the buffer. # # However, it doesn't support the bracketed paste mode. # So we redefine the key binding, and pass `-p` to `paste-buffer` to surround # the text with the sequences `Esc [ 200 ~` and `Esc [ 201 ~`. # This way, if we select a text containing a newline, then press `p`, it's not # automatically run by the shell. # # From `man tmux /^\s*paste-buffer`: # # > If -p is specified, paste bracket control codes are inserted around the # > buffer if the application has requested bracketed paste mode. # # Note that this requires that the shell supports the bracketed paste mode. # I.e. if you're using zsh, you need zsh version 5.1 or greater, and if you're # using bash, you need bash 4.4 or greater. # # --- # # The `-p` option was added to tmux in the commit `f4fdddc`. # According to the changelog, this was somewhere between tmux 1.6 and 1.7. # # --- # # Note that the original key binding used `copy-pipe-and-cancel` which – while # working – doesn't make sense; you can't pipe anything to `$ tmux paste-buffer`, # since it doesn't read its input. #}}} bind -T copy-mode-vi p send -X copy-selection-and-cancel \; paste-buffer -p \; deleteb # ^^ # Unbind {{{1 # How to find the default key bindings installed with no config?{{{ # # $ tmux -Lx -f/dev/null new # C-b ? # VG$ # Enter # $ vim # i # C-b ] # # Make sure to release `Ctrl` before pressing `]`. #}}} # How to unbind `#`, `~`, `'`, `"`?{{{ # # Quote the key (with single or double quotes). # # From `man tmux /^KEY BINDINGS`: # # > Note that to bind the ‘"’ or ‘'’ keys, quotation marks are necessary. #}}} # How to unbind `;`?{{{ # # Escape it. # # From `man tmux /^COMMANDS`: # # > A literal semicolon may be included by escaping it with a backslash (for # > example, when specifying a command sequence to bind-key). #}}} # TODO: # Remove all default key bindings which you're not interested in. # Some of them could be hit by accident. # Keep only the ones you really use. # Besides, it will give us a smaller table of key bindings, which will be easier # to read when we have an issue with one of our key bindings. # Have a look at `~/Desktop/tmux.md`. # prefix {{{2 # send-prefix unbind C-b # rotate-window unbind C-o # show-messages unbind '~' # split-window unbind '"' # choose-buffer -Z unbind = # detach-client unbind d # next-window unbind n # swap-pane -[UD] unbind '{' unbind '}' # select-pane -[UDLR] unbind Up unbind Down unbind Left unbind Right # tmux clear-history (tmux-logging) unbind M-c # rotate-window -D unbind M-o # resize-pane -U 5 unbind M-up # resize-pane -D 5 unbind M-down # resize-pane -L 5 unbind M-left # resize-pane -R 5 unbind M-right # resize-pane -[UDLR] unbind C-Up unbind C-Down unbind C-Left unbind C-Right # run ~/.config/tmux/plugins/tmux-logging/scripts/screen_capture.sh (tmux-logging) unbind M-p # run ~/.config/tmux/plugins/tmux-logging/scripts/save_complete_history.sh (tmux-logging) unbind M-P # resize-pane -Z unbind z # command-prompt -i -p / { send-keys -X search-forward-incremental "%%" } unbind / # copy-mode-vi {{{2 # By default, it's bound to `send-keys -X copy-pipe-and-cancel`.{{{ # # I don't like that, because I often select some text with the mouse by accident # (or when I'm bored); when that happens, obviously, tmux creates a buffer. # # This pollutes our list of buffers, and makes the interesting ones harder to # find. Besides, if when I want to copy some text, I will certainly not do it # with the mouse (not accurate enough). #}}} unbind -T copy-mode-vi MouseDragEnd1Pane # send-keys -X begin-selection unbind -T copy-mode-vi Space # send-keys -X copy-pipe-and-cancel unbind -T copy-mode-vi C-j # send -X copy-pipe-and-cancel 'xsel -i --clipboard; tmux paste-buffer' (tmux-yank) unbind -T copy-mode-vi M-y # (tmux-yank) unbind -T copy-mode-vi Y # copy-mode {{{2 # We don't need the key bindings from the copy-mode table; we use the copy-mode-*vi* table. # Why the guard?{{{ # # Once the table is empty, it's removed. # So, if you later try to unbind a key binding from it, an error will be raised: # # Table copy-mode doesn't exist # # Which can be repeated for every key binding you try to remove: # # Table copy-mode doesn't exist # Table copy-mode doesn't exist # ... # # Run `show-messages` to see them. # # This is annoying when you reload `tmux.conf`. #}}} if '[ "$TERM" != "#{default-terminal}" ]' { source "$HOME/.config/tmux/unbind-copy-mode.conf" } # }}}1