set -g default-terminal "tmux-256color" set -g mouse on set -g mode-keys vi set -g extended-keys always if -F "#{>=:#{version},3.3}" { set -g prompt-history-limit 999999 } set -g history-file "$XDG_DATA_HOME/tmux/tmux_history" run-shell 'mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/tmux"' # Better clipboard on mousedrag (https://unix.stackexchange.com/a/349020) # Disable xterm escape sequences for setting clipboard set -s set-clipboard off set -g copy-command 'xclip -selection clipboard' # https://github.com/neovim/neovim/wiki/FAQ#cursor-shape-doesnt-change-in-tmux set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q' # Set C-a as new prefix unbind C-b set -g prefix C-a bind C-a send-prefix # Copy selection into primary selection (without aborting) bind -T copy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-no-clear "xclip -selection primary" } # Select word or line on double or triple click respectively (w/o aborting). # (These are the defaults w/o the `run -d 0.3; send -X copy-pipe-and-cancel`) # use 'SecondClick' event instead of 'DoubleClick' for a quicker response. The # third click would just extend the selection further so the eager execution # does not hurt. unbind -T copy-mode-vi DoubleClick1Pane bind -T copy-mode-vi SecondClick1Pane { select-pane -t= send -X select-word send -X pipe-no-clear "xclip -selection primary" } bind -T copy-mode-vi TripleClick1Pane { select-pane -t= send -X select-line send -X pipe-no-clear "xclip -selection primary" } # use 'SecondClick' event instead of 'DoubleClick' (see above). unbind -n DoubleClick1Pane bind -n SecondClick1Pane { select-pane -t= if -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send -M } { copy-mode send -X select-word send -X pipe-no-clear "xclip -selection primary" } } bind -n TripleClick1Pane { select-pane -t= if -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send -M } { copy-mode send -X select-line send -X pipe-no-clear "xclip -selection primary" } } # Paste primary selection with middle mouse button bind -n MouseDown2Pane { select-pane -t= if -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send -M } { run 'tmux set-buffer -b XA_PRIMARY "$(xclip -selection p -o)"' paste-buffer -p -b XA_PRIMARY } } # When in copy-mode do the same but leave copy-mode first bind -T copy-mode-vi MouseDown2Pane { select-pane -t= send -X cancel # TODO: Forward click to 'root' key-table to prevent code duplication run 'tmux set-buffer -b XA_PRIMARY "$(xclip -selection p -o)"' paste-buffer -p -b XA_PRIMARY } # Scroll up 5 lines when entering copy-mode through the mouse wheel, send 5x`up` # when the current pane is in alternate mode and discard the `-e` flag passed to # copy-mode from the default binding (See below) bind -n WheelUpPane { if -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send -M } { if -F "#{alternate_on}" { send -N5 up } { copy-mode send -N5 C-y } } } # Scroll without selecting the pane bind -T copy-mode-vi WheelUpPane send -N5 C-y # Exit copy-mode when at the bottom. This practically mimics `copy-mode -e` but # only scrolling with the wheel (`-e` annoys me when scrolling down with `C-d`). bind -T copy-mode-vi WheelDownPane { if -F "#{==:0,#{scroll_position}}" { send -X cancel } { send -N5 C-e } } # Make mouse wheel scroll down properly in alternate mode bind -n WheelDownPane { send -M if -F "#{||:#{pane_in_mode},#{mouse_any_flag}}" { send -M } { if -F "#{alternate_on}" { send -N5 down } { # we're already at the bottom of the history, so nothing # needs to be done here } } } # Split panes with > and < unbind % bind < splitw -v -c "#{pane_current_path}" unbind '"' bind > splitw -h -c "#{pane_current_path}" # tmux stopped supporting format string for -t, thus run-shell is bind -N "Create a new window behind the current one with the same CWD" \ c run-shell \ "tmux neww -at #{active_window_index} -c \"#{pane_current_path}\"" bind -N "Break off the current pane to a new window behind the current one" \ ! run-shell "tmux breakp -at #{active_window_index}" bind -N "Source the config file" \ R \ source-file "$XDG_CONFIG_HOME/tmux/tmux.conf" \; \ display "Sourced $XDG_CONFIG_HOME/tmux/tmux.conf" # Fullscreen pane (toggle) bind F resizep -Z # Automatically renumber windows when closing one set -g renumber-windows on # Number windows and panes with 1-based indices set -g base-index 1 set -g pane-base-index 1 # Enter copy-mode and scroll one page up directly with PageUp without the need # of the prefix. (Exists already per default with prefix) bind -n PgUp copy-mode -u # Repeatable window-navigation bindings bind -r n next bind -r C-n next bind -r p prev bind -r C-p prev # Change windows with Alt-Page{Up,Down} analog to Ctrl-Page{Up,Down} in vim bind -n M-PgUp prev bind -n M-PgDn next # Jump back to last focused window bind C-o last-window # Swap window with previous/next one bind -r N swap-window -d -t :+1 bind -r P swap-window -d -t :-1 # Reorder windows by dragging them in the status line. # NOTE: Omitting the source from 3.4 on (after 8636848e6348) will do nothing, as # the source is then implied to be the window that the mouse is currently # on (i.e. also the target) # TODO: comment on tmux issue #656 if -F "#{>=:#{version},3.4}" { # TODO: why does this not work <3.4? bind -n MouseDrag1Status swap-window -d -s"" -t= } { bind -n MouseDrag1Status swap-window -d -t= # TODO: why does this not work >=3.4? # bind -n MouseDrag1Status swap-window -d -s":" -t= } # Move a window to the first or very last position # TODO: do nothing on drags that don't start on a window name # NOTE: mouse_{x,y} were not set in status line before 3.3 if -F "#{>=:#{version},3.3}" { bind -n MouseDragEnd1StatusDefault { if -F "#{e|<|f|0:#{mouse_x},#{e|/|f:#{window_width},2}}" { move-window -bt :^ } { move-window -at :$ } } } # Repeatable pane swapping bindings bind -r \{ swap-pane -U bind -r \} swap-pane -D # Select last window if target is current bind 1 select-window -T -t 1 bind 2 select-window -T -t 2 bind 3 select-window -T -t 3 bind 4 select-window -T -t 4 bind 5 select-window -T -t 5 bind 6 select-window -T -t 6 bind 7 select-window -T -t 7 bind 8 select-window -T -t 8 bind 9 select-window -T -t 9 # Since base-index == 1, 0 should go to window 10 bind 0 select-window -T -t 10 # Select windows with Alt-[0-9], select last window if target is current bind -n M-1 select-window -T -t 1 bind -n M-2 select-window -T -t 2 bind -n M-3 select-window -T -t 3 bind -n M-4 select-window -T -t 4 bind -n M-5 select-window -T -t 5 bind -n M-6 select-window -T -t 6 bind -n M-7 select-window -T -t 7 bind -n M-8 select-window -T -t 8 bind -n M-9 select-window -T -t 9 bind -n M-0 select-window -T -t 10 # Vim-bindings # Enter copy-mode with Escape # bind Escape copy-mode # Start selection (i.e. visual mode) with `v`, and rectangle selection (i.e. # visual block mode) with `C-v` # TODO: mimic vims behaviour for visual mode cycling and toggling unbind -T copy-mode-vi v bind -T copy-mode-vi v send -X begin-selection unbind -T copy-mode-vi C-v bind -T copy-mode-vi C-v { if -F "#{selection_active}" {} { send -X begin-selection } send -X rectangle-toggle } # Yank into system clipboard with vim-like bindings bind -T copy-mode-vi y run-shell '${XDG_CONFIG_HOME:-$HOME/.config}/tmux/yank.sh' bind -T copy-mode-vi Y { if -F "#{selection_present}" { send -X copy-pipe-line } { send -X copy-pipe-end-of-line } } # Clear selection or cancel copy-mode when nothing is selected bind -T copy-mode-vi Escape { if -F "#{selection_present}" { send -X clear-selection } { send -X cancel } } # Use insert-mode-like bindings to cancel copy-mode bind -T copy-mode-vi A send -X cancel bind -T copy-mode-vi I send -X cancel bind -T copy-mode-vi a send -X cancel # Rudimentary text object simulation bind -T copy-mode-vi i run-shell '${XDG_CONFIG_HOME:-$HOME/.config}/tmux/textobjs.sh' # Simulate vim's C-e & C-y (Keep cursor position while scrolling). As tmux # cannot scroll past the first and last history line, both bindings stop # scrolling when the respective line is visible. This mimics the behaviour of # vim's C-y at the top of the file. bind -T copy-mode-vi C-e { # check that there is still history beneath to scroll to. if -F "#{e|>:#{scroll_position},0}" { send-keys -X scroll-down # check that the cursor is not at the top of the pane if -F "#{e|>:#{copy_cursor_y},#{scroll_region_upper}}" { send-keys -X cursor-up } } } bind -T copy-mode-vi C-y { # check that there is still history above to scroll to. if -F "#{e|<:#{scroll_position},#{history_size}}" { send-keys -X scroll-up # check that the cursor is not at the bottom of the pane if -F "#{e|<:#{copy_cursor_y},#{scroll_region_lower}}" { send-keys -X cursor-down } } } # Scroll so that the current line becomes the middle one (vim's zz) if -F "#{<:#{version},3.4}" { # Simulate when scroll-middle does not exist yet # TODO: do the scroll_position and cursor_y calculations only once here bind -T copy-mode-vi z { set -F @middle_line "#{e|/:#{e|-:#{pane_height},1},2}" set -F @scroll_to_middle "#{e|-:#{copy_cursor_y},#{@middle_line}}" if -F "#{e|<:#{@scroll_to_middle},0}" { run "tmux send -N #{e|*:-1,#{@scroll_to_middle}} C-y" } { if -F "#{e|>:#{@scroll_to_middle},0}" { run "tmux send -N #{@scroll_to_middle} C-e" } } } } { # should be the default, but here for verboseness bind -T copy-mode-vi z send -X scroll-middle } # Navigate panes with -[hjkl] # NOTE: C-[hjkl] (w/o prefix) moves through vim splits and tmux panes # See vim-tmux-navigator unbind l bind h selectp -L bind j selectp -D bind k selectp -U bind l selectp -R unbind -T prefix Left unbind -T prefix Up unbind -T prefix Down unbind -T prefix Right # Resize mode similar to my i3 config bind -T resize h resizep -L \; switchc -T resize bind -T resize j resizep -D \; switchc -T resize bind -T resize k resizep -U \; switchc -T resize bind -T resize l resizep -R \; switchc -T resize bind -T resize 1 selectl even-horizontal \; switchc -T resize bind -T resize 2 selectl even-vertical \; switchc -T resize bind -T resize 3 selectl main-horizontal \; switchc -T resize bind -T resize 4 selectl main-vertical \; switchc -T resize bind -T resize 5 selectl tiled \; switchc -T resize bind r switchc -T resize # Make it possible to send keys that are bound in the root key table. (C-h for # example is sometimes needed as backspace sequence) # TODO: Make repeatable without C-v bind -r C-v command-prompt -k -p (selfinsert) { send "%%" } # Synchronize the panes in the current window with `S` bind S set -w synchronize-panes # Require confirmation before killing a pane set -g remain-on-exit on if -F "#{>=:#{version},3.3}" { bind -n C-d if -F "#{pane_dead}" { kill-pane } { send } bind -n Enter if -F "#{pane_dead}" { respawn-pane } { send } } { # omitting the key argument was introduced in 3.3 bind -n C-d if -F "#{pane_dead}" { kill-pane } { send C-d } bind -n Enter if -F "#{pane_dead}" { respawn-pane } { send Enter } } # Change installation location of plugins setenv -g TMUX_PLUGIN_MANAGER_PATH "$XDG_CONFIG_HOME/tmux/plugins/" ## Plugins set -g @plugin 'tmux-plugins/tmux-continuum' set -g @plugin 'tmux-plugins/tmux-prefix-highlight' set -g @plugin 'tmux-plugins/tmux-resurrect' set -g @plugin 'tmux-plugins/tmux-sensible' set -g @plugin 'tmux-plugins/tpm' set -g @plugin 'vim-tmux-navigator' # Capture pane content set -g @resurrect-capture-pane-contents 'on' # Change keybindings set -g @resurrect-save 'C-s' set -g @resurrect-restore 'C-r' # Save session every 5 min set -g @continuum-save-interval '5' # Last saved environment is automatically restored when tmux is started. set -g @continuum-restore 'on' # Restore {,neo}vim sessions with the help of vim-obsession # https://github.com/tpope/vim-obsession set -g @resurrect-strategy-vim 'session' set -g @resurrect-strategy-nvim 'session' # Save sessions before detaching unbind d bind d { run -b "$XDG_CONFIG_HOME/tmux/plugins/tmux-resurrect/scripts/save.sh" detach } ## Theming set -g @accent_color '#fce94f' # brightyellow set -g @ui_color '#757773' # brightblack set -g @sync_color '#ef2929' # brightred set -g @text_fg '#5e6466' # black set -g status-style bg=default,fg='#d3d7cf' # white set -g message-command-style 'bg=default,fg=#{@accent_color}' set -g message-style 'bg=#{@accent_color},fg=#{@text_fg}' set -g mode-style 'bg=#{@accent_color},fg=#{@text_fg}' # Visually display when a pane is in copy-mode or 'synchronize-panes' is on. # Use the pane-borders for tmux >= 3.2 and the "prefix_highlight" plugin # otherwise. # # NOTE: `<` does string comparison with `strcmp()`. This means that the # condition will break from a double digit major version (e.g. `10.0`) on. # TODO: Use #{e|<|f:, but cut off trailing non-numbers and convert the result # (i.e. 1.00 or 0.00) back into an int. if -F "#{<:#{version},3.2}" { set -g pane-border-style 'bg=default,fg=#{@ui_color}' set -g pane-active-border-style 'bg=default,fg=#{@accent_color}' set -g @prefix_highlight_show_copy_mode 'on' set -g @prefix_highlight_show_sync_mode 'on' set -g @prefix_highlight_copy_mode_attr 'fg=#{@text_fg},bg=#{@accent_color}' set -g @prefix_highlight_sync_mode_attr 'fg=#{@text_fg},bg=#{@sync_color}' } { # From 3.2 on, we can use formats in styles, so that all indicators can be # done by the borders. set -g pane-border-style "#{?synchronize-panes,fg=#{@sync_color},fg=#{@ui_color}}" set -g pane-active-border-style "#{?pane_in_mode,bg=#{@accent_color}#,fg=#{@ui_color},#{?synchronize-panes,fg=#{@sync_color},fg=#{@accent_color}}}" } set -g @prefix_highlight_fg '#{@accent_color}' set -g @prefix_highlight_bg 'default' set -g status-right "#{prefix_highlight}" set -g status-left "#{prefix_highlight}" set -g status-justify centre # Display window_index and window_name as status and highlight it when the # active pane is zoomed. set -g window-status-current-format \ "#{?window_zoomed_flag,#[fg=#{@text_fg} bg=#{@accent_color} underscore],#[fg=#{@accent_color} bold]}#I #W" set -g window-status-format \ "#[fg=#{@ui_color}]#{?window_zoomed_flag,#[underscore],}#I #W" # Initialize TMUX plugin manager. Keep this line at the very bottom. run -b "$XDG_CONFIG_HOME/tmux/plugins/tpm/tpm"