tmux/regress/conf/01840240e807e837dbf76d85b4b...

1497 lines
55 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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! <expr> <m-h> <sid>m_hl('h')
# noremap! <expr> <m-l> <sid>m_hl('l')
# nno <expr> <m-h> <sid>m_hl('h')
# nno <expr> <m-l> <sid>m_hl('l')
# xno <expr> <m-h> <sid>m_hl('h')
# xno <expr> <m-l> <sid>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