Добавлены конфиги tmux и zsh

This commit is contained in:
eKa 2019-09-18 00:17:47 +05:00
commit 7d0a7691c6
882 changed files with 67305 additions and 0 deletions

0
README.md Normal file
View File

1
install.sh Executable file
View File

@ -0,0 +1 @@
#!/bin/bash

0
profile Normal file
View File

401
tmux/local.conf Normal file
View File

@ -0,0 +1,401 @@
# ==========================
# === General settings ===
# ==========================
set -g default-terminal "screen-256color"
set -g history-limit 20000
set -g buffer-limit 20
set -sg escape-time 0
set -g display-time 1500
set -g remain-on-exit off
set -g repeat-time 300
setw -g allow-rename off
setw -g automatic-rename off
setw -g aggressive-resize on
# Change prefix key to C-a, easier to type, same to "screen"
unbind C-b
set -g prefix C-a
# Set parent terminal title to reflect current window in tmux session
set -g set-titles on
set -g set-titles-string "#I:#W"
# Start index of window/pane with 1, because we're humans, not computers
set -g base-index 1
setw -g pane-base-index 1
# Enable mouse support
set -g mouse on
# ==========================
# === Key bindings ===
# ==========================
# Unbind default key bindings, we're going to override
unbind "\$" # rename-session
unbind , # rename-window
unbind % # split-window -h
unbind '"' # split-window
unbind } # swap-pane -D
unbind { # swap-pane -U
unbind [ # paste-buffer
unbind ]
unbind "'" # select-window
unbind n # next-window
unbind p # previous-window
unbind l # last-window
unbind M-n # next window with alert
unbind M-p # next window with alert
unbind o # focus thru panes
unbind & # kill-window
unbind "#" # list-buffer
unbind = # choose-buffer
unbind z # zoom-pane
unbind M-Up # resize 5 rows up
unbind M-Down # resize 5 rows down
unbind M-Right # resize 5 rows right
unbind M-Left # resize 5 rows left
# Edit configuration and reload
bind C-e new-window -n 'tmux.conf' "sh -c '\${EDITOR:-vim} ~/.tmux.conf && tmux source ~/.tmux.conf && tmux display \"Config reloaded\"'"
# Reload tmux configuration
bind C-r source-file ~/.tmux.conf \; display "Config reloaded"
# new window and retain cwd
bind c new-window -c "#{pane_current_path}"
# Prompt to rename window right after it's created
#set-hook -g after-new-window 'command-prompt -I "#{window_name}" "rename-window '%%'"'
# Rename session and window
bind r command-prompt -I "#{window_name}" "rename-window '%%'"
bind R command-prompt -I "#{session_name}" "rename-session '%%'"
# Split panes
bind | split-window -h -c "#{pane_current_path}"
bind _ split-window -v -c "#{pane_current_path}"
# Select pane and windows
bind -r C-[ previous-window
bind -r C-] next-window
bind -r [ select-pane -t :.-
bind -r ] select-pane -t :.+
bind -r Tab last-window # cycle thru MRU tabs
bind -r C-o swap-pane -D
# Zoom pane
bind + resize-pane -Z
# Link window
bind L command-prompt -p "Link window from (session:window): " "link-window -s %% -a"
# Swap panes back and forth with 1st pane
# When in main-(horizontal|vertical) layouts, the biggest/widest panel is always @1
bind \ if '[ #{pane_index} -eq 1 ]' \
'swap-pane -s "!"' \
'select-pane -t:.1 ; swap-pane -d -t 1 -s "!"'
# Kill pane/window/session shortcuts
bind x kill-pane
bind X kill-window
bind C-x confirm-before -p "kill other windows? (y/n)" "kill-window -a"
bind Q confirm-before -p "kill-session #S? (y/n)" kill-session
# Merge session with another one (e.g. move all windows)
# If you use adhoc 1-window sessions, and you want to preserve session upon exit
# but don't want to create a lot of small unnamed 1-window sessions around
# move all windows from current session to main named one (dev, work, etc)
bind C-u command-prompt -p "Session to merge with: " \
"run-shell 'yes | head -n #{session_windows} | xargs -I {} -n 1 tmux movew -t %%'"
# Detach from session
bind d detach
bind D if -F '#{session_many_attached}' \
'confirm-before -p "Detach other clients? (y/n)" "detach -a"' \
'display "Session has only 1 client attached"'
# Hide status bar on demand
bind C-s if -F '#{s/off//:status}' 'set status off' 'set status on'
# ==================================================
# === Window monitoring for activity and silence ===
# ==================================================
bind m setw monitor-activity \; display-message 'Monitor window activity [#{?monitor-activity,ON,OFF}]'
bind M if -F '#{monitor-silence}' \
'setw monitor-silence 0 ; display-message "Monitor window silence [OFF]"' \
'command-prompt -p "Monitor silence: interval (s)" "setw monitor-silence %%"'
# Activity bell and whistles
set -g visual-activity on
# TODO: Does not work as well, check on newer versions
# set -g visual-silence on
# BUG: bell-action other ignored · Issue #1027 · tmux/tmux · GitHub - https://github.com/tmux/tmux/issues/1027
# set -g visual-bell on
# setw -g bell-action other
# ================================================
# === Copy mode, scroll and clipboard ===
# ================================================
set -g @copy_use_osc52_fallback on
# Prefer vi style key table
setw -g mode-keys vi
bind p paste-buffer
bind C-p choose-buffer
# trigger copy mode by
bind -n M-Up copy-mode
# Scroll up/down by 1 line, half screen, whole screen
bind -T copy-mode-vi M-Up send-keys -X scroll-up
bind -T copy-mode-vi M-Down send-keys -X scroll-down
bind -T copy-mode-vi M-PageUp send-keys -X halfpage-up
bind -T copy-mode-vi M-PageDown send-keys -X halfpage-down
bind -T copy-mode-vi PageDown send-keys -X page-down
bind -T copy-mode-vi PageUp send-keys -X page-up
# When scrolling with mouse wheel, reduce number of scrolled rows per tick to "2" (default is 5)
bind -T copy-mode-vi WheelUpPane select-pane \; send-keys -X -N 2 scroll-up
bind -T copy-mode-vi WheelDownPane select-pane \; send-keys -X -N 2 scroll-down
# wrap default shell in reattach-to-user-namespace if available
# there is some hack with `exec & reattach`, credits to "https://github.com/gpakosz/.tmux"
# don't really understand how it works, but at least window are not renamed to "reattach-to-user-namespace"
if -b "command -v reattach-to-user-namespace > /dev/null 2>&1" \
"run 'tmux set -g default-command \"exec $(tmux show -gv default-shell) 2>/dev/null & reattach-to-user-namespace -l $(tmux show -gv default-shell)\"'"
yank="~/.dots/tmux/utils/yank.sh"
# Copy selected text
bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "$yank"
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "$yank"
bind -T copy-mode-vi Y send-keys -X copy-line \;\
run "tmux save-buffer - | $yank"
bind-key -T copy-mode-vi D send-keys -X copy-end-of-line \;\
run "tmux save-buffer - | $yank"
bind -T copy-mode-vi C-j send-keys -X copy-pipe-and-cancel "$yank"
bind-key -T copy-mode-vi A send-keys -X append-selection-and-cancel \;\
run "tmux save-buffer - | $yank"
# Copy selection on drag end event, but do not cancel copy mode and do not clear selection
# clear select on subsequence mouse click
bind -T copy-mode-vi MouseDragEnd1Pane \
send-keys -X copy-pipe "$yank"
bind -T copy-mode-vi MouseDown1Pane select-pane \;\
send-keys -X clear-selection
# iTerm2 works with clipboard out of the box, set-clipboard already set to "external"
# tmux show-options -g -s set-clipboard
# set-clipboard on|external
# =====================================
# === Theme ===
# =====================================
# Feel free to NOT use this variables at all (remove, rename)
# this are named colors, just for convenience
color_orange="colour166" # 208, 166
color_purple="colour134" # 135, 134
color_green="colour076" # 070
color_blue="colour39"
color_yellow="colour220"
color_red="colour160"
color_black="colour232"
color_white="white" # 015
# This is a theme CONTRACT, you are required to define variables below
# Change values, but not remove/rename variables itself
color_dark="$color_black"
color_light="$color_white"
color_session_text="$color_blue"
color_status_text="colour245"
color_main="$color_orange"
color_secondary="$color_purple"
color_level_ok="$color_green"
color_level_warn="$color_yellow"
color_level_stress="$color_red"
color_window_off_indicator="colour088"
color_window_off_status_bg="colour238"
color_window_off_status_current_bg="colour254"
# =====================================
# === Appearence and status bar ===
# ======================================
set -g mode-style "fg=default,bg=$color_main"
# command line style
set -g message-style "fg=$color_main,bg=$color_dark"
# status line style
set -g status-style "fg=$color_status_text,bg=$color_dark"
# window segments in status line
set -g window-status-separator ""
separator_powerline_left=""
separator_powerline_right=""
# setw -g window-status-style "fg=$color_status_text,bg=$color_dark"
setw -g window-status-format " #I:#W "
setw -g window-status-current-style "fg=$color_light,bold,bg=$color_main"
setw -g window-status-current-format "#[fg=$color_dark,bg=$color_main]$separator_powerline_right#[default] #I:#W# #[fg=$color_main,bg=$color_dark]$separator_powerline_right#[default]"
# when window has monitoring notification
setw -g window-status-activity-style "fg=$color_main"
# outline for active pane
setw -g pane-active-border-style "fg=$color_main"
# general status bar settings
set -g status on
set -g status-interval 5
set -g status-position top
set -g status-justify left
set -g status-right-length 100
# define widgets we're going to use in status bar
# note, that this is not the complete list, some of them are loaded from plugins
wg_session="#[fg=$color_session_text] #S #[default]"
wg_battery="#{battery_status_fg} #{battery_icon} #{battery_percentage}"
wg_date="#[fg=$color_secondary]%h %d %H:%M#[default]"
wg_user_host="#[fg=$color_secondary]#(whoami)#[default]@#H"
wg_is_zoomed="#[fg=$color_dark,bg=$color_secondary]#{?window_zoomed_flag,[Z],}#[default]"
# TODO: highlighted for nested local session as well
wg_is_keys_off="#[fg=$color_light,bg=$color_window_off_indicator]#([ $(tmux show-option -qv key-table) = 'off' ] && echo 'OFF')#[default]"
set -g status-left "$wg_session"
set -g status-right "#{prefix_highlight} $wg_is_keys_off $wg_is_zoomed #{sysstat_cpu} | #{sysstat_mem} | #{sysstat_loadavg} | $wg_user_host | $wg_date $wg_battery #{online_status}"
# online and offline icon for tmux-online-status
set -g @online_icon "#[fg=$color_level_ok]●#[default]"
set -g @offline_icon "#[fg=$color_level_stress]●#[default]"
# Configure view templates for tmux-plugin-sysstat "MEM" and "CPU" widget
set -g @sysstat_mem_view_tmpl 'MEM:#[fg=#{mem.color}]#{mem.pused}#[default] #{mem.used}'
# Configure colors for tmux-plugin-sysstat "MEM" and "CPU" widget
set -g @sysstat_cpu_color_low "$color_level_ok"
set -g @sysstat_cpu_color_medium "$color_level_warn"
set -g @sysstat_cpu_color_stress "$color_level_stress"
set -g @sysstat_mem_color_low "$color_level_ok"
set -g @sysstat_mem_color_medium "$color_level_warn"
set -g @sysstat_mem_color_stress "$color_level_stress"
set -g @sysstat_swap_color_low "$color_level_ok"
set -g @sysstat_swap_color_medium "$color_level_warn"
set -g @sysstat_swap_color_stress "$color_level_stress"
# Configure tmux-battery widget colors
set -g @batt_color_full_charge "#[fg=$color_level_ok]"
set -g @batt_color_high_charge "#[fg=$color_level_ok]"
set -g @batt_color_medium_charge "#[fg=$color_level_warn]"
set -g @batt_color_low_charge "#[fg=$color_level_stress]"
# Configure tmux-prefix-highlight colors
set -g @prefix_highlight_output_prefix '['
set -g @prefix_highlight_output_suffix ']'
set -g @prefix_highlight_fg "$color_dark"
set -g @prefix_highlight_bg "$color_secondary"
set -g @prefix_highlight_show_copy_mode 'on'
set -g @prefix_highlight_copy_mode_attr "fg=$color_dark,bg=$color_secondary"
# =====================================
# === Renew environment ===
# =====================================
set -g update-environment \
"DISPLAY\
SSH_ASKPASS\
SSH_AUTH_SOCK\
SSH_AGENT_PID\
SSH_CONNECTION\
SSH_TTY\
WINDOWID\
XAUTHORITY"
bind '$' run "~/.dots/tmux/utils/renew_env.sh"
# ============================
# === Plugins ===
# ============================
#set -g @plugin 'tmux-plugins/tpm'
#set -g @plugin 'tmux-plugins/tmux-battery'
#set -g @plugin 'tmux-plugins/tmux-prefix-highlight'
#set -g @plugin 'tmux-plugins/tmux-online-status'
#set -g @plugin 'tmux-plugins/tmux-sidebar'
#set -g @plugin 'tmux-plugins/tmux-copycat'
#set -g @plugin 'tmux-plugins/tmux-open'
#set -g @plugin 'samoshkin/tmux-plugin-sysstat'
#set -g @plugin 'tmux-plugins/tmux-resurrect'
#set -g @plugin 'tmux-plugins/tmux-continuum'
# Plugin properties
set -g @sidebar-tree 't'
set -g @sidebar-tree-focus 'T'
set -g @sidebar-tree-command 'tree -C'
set -g @continuum-restore 'on'
set -g @open-S 'https://www.google.com/search?q='
# ==============================================
# === Nesting local and remote sessions ===
# ==============================================
# Session is considered to be remote when we ssh into host
if-shell 'test -n "$SSH_CLIENT"' \
'source-file ~/.dots/tmux/remote.conf'
# We want to have single prefix key "C-a", usable both for local and remote session
# we don't want to "C-a" + "a" approach either
# Idea is to turn off all key bindings and prefix handling on local session,
# so that all keystrokes are passed to inner/remote session
# see: toggle on/off all keybindings · Issue #237 · tmux/tmux - https://github.com/tmux/tmux/issues/237
# Also, change some visual styles when window keys are off
bind -T root F12 \
set prefix None \;\
set key-table off \;\
set status-style "fg=$color_status_text,bg=$color_window_off_status_bg" \;\
set window-status-current-format "#[fg=$color_window_off_status_bg,bg=$color_window_off_status_current_bg]$separator_powerline_right#[default] #I:#W# #[fg=$color_window_off_status_current_bg,bg=$color_window_off_status_bg]$separator_powerline_right#[default]" \;\
set window-status-current-style "fg=$color_dark,bold,bg=$color_window_off_status_current_bg" \;\
if -F '#{pane_in_mode}' 'send-keys -X cancel' \;\
refresh-client -S \;\
bind -T off F12 \
set -u prefix \;\
set -u key-table \;\
set -u status-style \;\
set -u window-status-current-style \;\
set -u window-status-current-format \;\
refresh-client -S
# Run all plugins' scripts
#run '~/.tmux/plugins/tpm/tpm'
run '~/.dots/tmux/plugins/tmux-battery/battery.tmux'
run '~/.dots/tmux/plugins/tmux-online-status/online_status.tmux'
run '~/.dots/tmux/plugins/tmux-plugin-sysstat/sysstat.tmux'
run '~/.dots/tmux/plugins/tmux-prefix-highlight/prefix_highlight.tmux'
run '~/.dots/tmux/plugins/tmux-copycat/copycat.tmux'
run '~/.dots/tmux/plugins/tmux-open/open.tmux'
run '~/.dots/tmux/plugins/tmux-sidebar/sidebar.tmux'
#run '~/.dots/tmux/plugins/tmux-yank/yank.tmux'
run '~/.dots/tmux/plugins/tmux-ressurect/resurrect.tmux'
run '~/.dots/tmux/plugins/tmux-continuum/continuum.tmux'

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,262 @@
# Tmux battery status
Enables displaying battery percentage and status icon in tmux status-right.
## Installation
In order to read the battery status, this plugin depends on having one of the following applications installed:
- pmset (MacOS only)
- acpi
- upower
- termux-battery-status
- apm
In a normal situation, one of the above should be installed on your system by default and thus it should not be necessary to specifically install one of them. That being said, the `acpi` utility is currently recommended for use over `upower` where possible due to ongoing CPU usage issues.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
```tmux
set -g @plugin 'tmux-plugins/tmux-battery'
```
Hit `<prefix> + I` to fetch the plugin and source it.
If format strings are added to `status-right`, they should now be visible.
### Manual Installation
Clone the repo:
```shell
git clone https://github.com/tmux-plugins/tmux-battery ~/clone/path
```
Add this line to the bottom of `.tmux.conf`:
```tmux
run-shell ~/clone/path/battery.tmux
```
From the terminal, reload TMUX environment:
```shell
tmux source-file ~/.tmux.conf
```
If format strings are added to `status-right`, they should now be visible.
## Usage
Add any of the supported format strings (see below) to the `status-right` tmux option in `.tmux.conf`. Example:
```tmux
set -g status-right '#{battery_status_bg} Batt: #{battery_icon} #{battery_percentage} #{battery_remain} | %a %h-%d %H:%M '
```
### Supported Format Strings
- `#{battery_color_bg}` - will set the background color of the status bar based on the battery charge level if discharging and status otherwise
- `#{battery_color_fg}` - will set the foreground color of the status bar based on the battery charge level if discharging and status otherwise
- `#{battery_color_charge_bg}` - will set the background color of the status bar based solely on the battery charge level
- `#{battery_color_charge_fg}` - will set the foreground color of the status bar based solely on the battery charge level
- `#{battery_color_status_bg}` - will set the background color of the status bar based solely on the battery status
- `#{battery_color_status_fg}` - will set the foreground color of the status bar based solely on the battery status
- `#{battery_graph}` - will show battery percentage as a bar graph: ▁▂▄▆█
- `#{battery_icon}` - will display a battery status/charge icon
- `#{battery_icon_charge}` - will display a battery charge icon
- `#{battery_icon_status}` - will display a battery status icon
- `#{battery_percentage}` - will show battery percentage
- `#{battery_remain}` - will show remaining time of battery charge\*
\* These format strings can be further customized via options as described below.
#### Options
`#{battery_remain}`
- `@batt_remain_short`: 'true' / 'false' - This will shorten the time remaining (when charging or discharging) to `~H:MM`.
### Defaults
#### Options
- `@batt_remain_short`: 'false'
#### Icons/Colors
By default, the following colors and icons are used. (The exact colors displayed depends on your terminal / X11 config.)
Please be aware that the 'level of charge' as noted below (e.g. `[80%-95%)`) uses interval notation. If you are unfamiliar with it, <a href="https://en.wikipedia.org/wiki/Bracket_(mathematics)#Intervals">check it out here</a>.
Also, a note about the `@batt_color_...` options: `@batt_color_..._primary_...` options are what will be displayed in the main `bg` or `fg` format strings you choose - e.g. if you use `#{battery_color_bg}`, the `@batt_color_..._primary_...` colors you choose will be the background. Likewise, the corresponding `@batt_color_..._secondary_...` color will be the foreground.
Level of Charge Colors:
- primary tier 8 \[95%-100%] (`@batt_color_charge_primary_tier8`): '#00ff00'
- primary tier 7 \[80%-95%) (`@batt_color_charge_primary_tier7`): '#55ff00'
- primary tier 6 \[65%-80%) (`@batt_color_charge_primary_tier6`): '#aaff00'
- primary tier 5 \[50%-65%) (`@batt_color_charge_primary_tier5`): '#ffff00'
- primary tier 4 \[35%-50%) (`@batt_color_charge_primary_tier4`): '#ffc000'
- primary tier 3 \[20%-35%) (`@batt_color_charge_primary_tier3`): '#ff8000'
- primary tier 2 (5%-20%) (`@batt_color_charge_primary_tier2`): '#ff4000'
- primary tier 1 \[0%-5%] (`@batt_color_charge_primary_tier1`): '#ff0000'
- secondary tier 8 \[95%-100%] (`@batt_color_charge_secondary_tier8`): 'colour0'
- secondary tier 7 \[80%-95%) (`@batt_color_charge_secondary_tier7`): 'colour0'
- secondary tier 6 \[65%-80%) (`@batt_color_charge_secondary_tier6`): 'colour0'
- secondary tier 5 \[50%-65%) (`@batt_color_charge_secondary_tier5`): 'colour0'
- secondary tier 4 \[35%-50%) (`@batt_color_charge_secondary_tier4`): 'colour0'
- secondary tier 3 \[20%-35%) (`@batt_color_charge_secondary_tier3`): 'colour0'
- secondary tier 2 (5%-20%) (`@batt_color_charge_secondary_tier2`): 'colour0'
- secondary tier 1 \[0%-5%] (`@batt_color_charge_secondary_tier1`): 'colour0'
Status Colors:
- primary charged (`@batt_color_status_primary_charged`): 'colour33'
- primary charging (`@batt_color_status_primary_charging`): 'colour33'
- primary discharging (`@batt_color_status_primary_discharging`): 'colour14'
- primary attached (`@batt_color_status_primary_attached`): 'colour201'
- primary unknown (`@batt_color_status_primary_unknown`): 'colour7'
- secondary charged (`@batt_color_status_secondary_charged`): 'colour0'
- secondary charging (`@batt_color_status_secondary_charging`): 'colour0'
- secondary discharging (`@batt_color_status_secondary_discharging`): 'colour0'
- secondary attached (`@batt_color_status_secondary_attached`): 'colour0'
- secondary unknown (`@batt_color_status_secondary_unknown`): 'colour0'
Level of Charge Icons:
- tier 8 \[95%-100%] (`@batt_icon_charge_tier8`): '█'
- tier 7 \[80%-95%) (`@batt_icon_charge_tier7`): '▇'
- tier 6 \[65%-80%) (`@batt_icon_charge_tier6`): '▆'
- tier 5 \[50%-65%) (`@batt_icon_charge_tier5`): '▅'
- tier 4 \[35%-50%) (`@batt_icon_charge_tier4`): '▄'
- tier 3 \[20%-35%) (`@batt_icon_charge_tier3`): '▃'
- tier 2 (5%-20%) (`@batt_icon_charge_tier2`): '▂'
- tier 1 \[0%-5%] (`@batt_icon_charge_tier1`): '▁'
Status Icons:
- charged (`@batt_icon_status_charged`): '🔌'
- charged - OS X (`@batt_icon_status_charged`): '🔌'
- charging (`@batt_icon_status_charging`): '🔌'
- discharging (`@batt_icon_status_discharging`): '🔋'
- attached (`@batt_icon_status_attached`): '⚠️'
- unknown (`@batt_icon_status_unknown`): '?'
#### Changing the Defaults
All efforts have been made to make sane defaults, but if you wish to change any of them, add the option to `.tmux.conf`. For example:
```tmux
set -g @batt_icon_charge_tier8 '🌕'
set -g @batt_icon_charge_tier7 '🌖'
set -g @batt_icon_charge_tier6 '🌖'
set -g @batt_icon_charge_tier5 '🌗'
set -g @batt_icon_charge_tier4 '🌗'
set -g @batt_icon_charge_tier3 '🌘'
set -g @batt_icon_charge_tier2 '🌘'
set -g @batt_icon_charge_tier1 '🌑'
set -g @batt_icon_status_charged '🔋'
set -g @batt_icon_status_charging '⚡'
set -g @batt_icon_status_discharging '👎'
set -g @batt_color_status_primary_charged '#3daee9'
set -g @batt_color_status_primary_charging '#3daee9'
```
Don't forget to reload the tmux environment after you do this by either hitting `<prefix> + I` if tmux battery is installed via the tmux plugin manager, or by typing `tmux source-file ~/.tmux.conf` in the terminal if tmux battery is manually installed.
*Warning*: The battery icon change most likely will not be instant. When you un-plug the power cord, it will take some time (15 - 60 seconds) for the icon to change. This depends on the `status-interval` tmux option. Setting it to 15 seconds should be good enough.
## Examples
These are all examples of the default plugin color and icon schemes paired with the default tmux color scheme using the following `status-right` and `status-right-length` settings in `.tmux.conf`
```tmux
set -g status-right 'Colors: #{battery_color_bg}bg#[default] #{battery_color_fg}fg#[default] #{battery_color_charge_bg}charge_bg#[default] #{battery_color_charge_fg}charge_fg#[default] #{battery_color_status_bg}status_bg#[default] #{battery_color_status_fg}status_fg#[default] | Graph: #{battery_graph} | Icon: #{battery_icon} | Charge Icon: #{battery_icon_charge} | Status Icon: #{battery_icon_status} | Percent: #{battery_percentage} | Remain: #{battery_remain}'
set -g status-right-length '150'
```
Battery charging at tier 8 \[95%-100%]:<br>
![battery-charging-tier8](./screenshots/battery_charging_tier8.png)
Battery charging at tier 7 \[80%-95%):<br>
![battery-charging-tier7](./screenshots/battery_charging_tier7.png)
Battery charging at tier 6 \[65%-80%):<br>
![battery-charging-tier6](./screenshots/battery_charging_tier6.png)
Battery charging at tier 5 \[50%-65%):<br>
![battery-charging-tier5](./screenshots/battery_charging_tier5.png)
Battery charging at tier 4 \[35%-50%):<br>
![battery-charging-tier4](./screenshots/battery_charging_tier4.png)
Battery charging at tier 3 \[20%-35%):<br>
![battery-charging-tier3](./screenshots/battery_charging_tier3.png)
Battery charging at tier 2 (5%-20%):<br>
![battery-charging-tier2](./screenshots/battery_charging_tier2.png)
Battery charging at tier 1 \[0%-5%]:<br>
![battery-charging-tier1](./screenshots/battery_charging_tier1.png)
Battery discharging at tier 8 \[95%-100%]:<br>
![battery-discharging-tier8](./screenshots/battery_discharging_tier8.png)
Battery discharging at tier 7 \[80%-95%):<br>
![battery-discharging-tier7](./screenshots/battery_discharging_tier7.png)
Battery discharging at tier 6 \[65%-80%):<br>
![battery-discharging-tier6](./screenshots/battery_discharging_tier6.png)
Battery discharging at tier 5 \[50%-65%):<br>
![battery-discharging-tier5](./screenshots/battery_discharging_tier5.png)
Battery discharging at tier 4 \[35%-50%):<br>
![battery-discharging-tier4](./screenshots/battery_discharging_tier4.png)
Battery discharging at tier 3 \[20%-35%):<br>
![battery-discharging-tier3](./screenshots/battery_discharging_tier3.png)
Battery discharging at tier 2 (5%-20%):<br>
![battery-discharging-tier2](./screenshots/battery_discharging_tier2.png)
Battery discharging at tier 1 \[0%-5%]:<br>
![battery-discharging-tier1](./screenshots/battery_discharging_tier1.png)
Battery in 'attached' status:<br>
![battery-status-attached](./screenshots/battery_status_attached.png)
Battery in an unknown status:<br>
![battery-status-unknown](./screenshots/battery_status_unknown.png)
### Tmux Plugins
This plugin is part of the [tmux-plugins](https://github.com/tmux-plugins) organisation. Checkout plugins as [resurrect](https://github.com/tmux-plugins/tmux-resurrect), [logging](https://github.com/tmux-plugins/tmux-logging), [online status](https://github.com/tmux-plugins/tmux-online-status), and many more over at the [tmux-plugins](https://github.com/tmux-plugins) organisation page.
### Maintainer
- [Martin Beentjes](https://github.com/martinbeentjes)
### Contributors
- Adam Biggs
- Aleksandar Djurdjic
- Bruno Sutic
- Caleb
- Dan Cassidy
- Diego Ximenes
- Evan N-D
- Jan Ahrens
- Joey Geralnik
- HyunJong (Joseph) Lee
- Martin Beentjes
- Mike Foley
- Ryan Frantz
- Seth Wright
- Tom Levens
### License
[MIT](LICENSE.md)

View File

@ -0,0 +1,62 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/helpers.sh"
battery_interpolation=(
"\#{battery_color_bg}"
"\#{battery_color_fg}"
"\#{battery_color_charge_bg}"
"\#{battery_color_charge_fg}"
"\#{battery_color_status_bg}"
"\#{battery_color_status_fg}"
"\#{battery_graph}"
"\#{battery_icon}"
"\#{battery_icon_charge}"
"\#{battery_icon_status}"
"\#{battery_percentage}"
"\#{battery_remain}"
)
battery_commands=(
"#($CURRENT_DIR/scripts/battery_color.sh bg)"
"#($CURRENT_DIR/scripts/battery_color.sh fg)"
"#($CURRENT_DIR/scripts/battery_color_charge.sh bg)"
"#($CURRENT_DIR/scripts/battery_color_charge.sh fg)"
"#($CURRENT_DIR/scripts/battery_color_status.sh bg)"
"#($CURRENT_DIR/scripts/battery_color_status.sh fg)"
"#($CURRENT_DIR/scripts/battery_graph.sh)"
"#($CURRENT_DIR/scripts/battery_icon.sh)"
"#($CURRENT_DIR/scripts/battery_icon_charge.sh)"
"#($CURRENT_DIR/scripts/battery_icon_status.sh)"
"#($CURRENT_DIR/scripts/battery_percentage.sh)"
"#($CURRENT_DIR/scripts/battery_remain.sh)"
)
set_tmux_option() {
local option="$1"
local value="$2"
tmux set-option -gq "$option" "$value"
}
do_interpolation() {
local all_interpolated="$1"
for ((i=0; i<${#battery_commands[@]}; i++)); do
all_interpolated=${all_interpolated//${battery_interpolation[$i]}/${battery_commands[$i]}}
done
echo "$all_interpolated"
}
update_tmux_option() {
local option="$1"
local option_value="$(get_tmux_option "$option")"
local new_option_value="$(do_interpolation "$option_value")"
set_tmux_option "$option" "$new_option_value"
}
main() {
update_tmux_option "status-right"
update_tmux_option "status-left"
}
main

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
print_color() {
local plane="$1"
local status="$2"
if [ "$status" == "discharging" ]; then
$CURRENT_DIR/battery_color_charge.sh "$plane"
else
$CURRENT_DIR/battery_color_status.sh "$plane" "$status"
fi
}
main() {
local status="$(battery_status)"
local plane="$1"
print_color "$plane" "$status"
}
main $@

View File

@ -0,0 +1,98 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
# script global variables
color_charge_primary_tier8=''
color_charge_primary_tier7=''
color_charge_primary_tier6=''
color_charge_primary_tier5=''
color_charge_primary_tier4=''
color_charge_primary_tier3=''
color_charge_primary_tier2=''
color_charge_primary_tier1=''
color_charge_secondary_tier8=''
color_charge_secondary_tier7=''
color_charge_secondary_tier6=''
color_charge_secondary_tier5=''
color_charge_secondary_tier4=''
color_charge_secondary_tier3=''
color_charge_secondary_tier2=''
color_charge_secondary_tier1=''
# script default variables
color_charge_primary_tier8_default='#00ff00'
color_charge_primary_tier7_default='#55ff00'
color_charge_primary_tier6_default='#aaff00'
color_charge_primary_tier5_default='#ffff00'
color_charge_primary_tier4_default='#ffc000'
color_charge_primary_tier3_default='#ff8000'
color_charge_primary_tier2_default='#ff4000'
color_charge_primary_tier1_default='#ff0000'
color_charge_secondary_tier8_default='colour0'
color_charge_secondary_tier7_default='colour0'
color_charge_secondary_tier6_default='colour0'
color_charge_secondary_tier5_default='colour0'
color_charge_secondary_tier4_default='colour0'
color_charge_secondary_tier3_default='colour0'
color_charge_secondary_tier2_default='colour0'
color_charge_secondary_tier1_default='colour0'
# colors are set as script global variables
get_color_charge_settings() {
color_charge_primary_tier8=$(get_tmux_option "@batt_color_charge_primary_tier8" "$color_charge_primary_tier8_default")
color_charge_primary_tier7=$(get_tmux_option "@batt_color_charge_primary_tier7" "$color_charge_primary_tier7_default")
color_charge_primary_tier6=$(get_tmux_option "@batt_color_charge_primary_tier6" "$color_charge_primary_tier6_default")
color_charge_primary_tier5=$(get_tmux_option "@batt_color_charge_primary_tier5" "$color_charge_primary_tier5_default")
color_charge_primary_tier4=$(get_tmux_option "@batt_color_charge_primary_tier4" "$color_charge_primary_tier4_default")
color_charge_primary_tier3=$(get_tmux_option "@batt_color_charge_primary_tier3" "$color_charge_primary_tier3_default")
color_charge_primary_tier2=$(get_tmux_option "@batt_color_charge_primary_tier2" "$color_charge_primary_tier2_default")
color_charge_primary_tier1=$(get_tmux_option "@batt_color_charge_primary_tier1" "$color_charge_primary_tier1_default")
color_charge_secondary_tier8=$(get_tmux_option "@batt_color_charge_secondary_tier8" "$color_charge_secondary_tier8_default")
color_charge_secondary_tier7=$(get_tmux_option "@batt_color_charge_secondary_tier7" "$color_charge_secondary_tier7_default")
color_charge_secondary_tier6=$(get_tmux_option "@batt_color_charge_secondary_tier6" "$color_charge_secondary_tier6_default")
color_charge_secondary_tier5=$(get_tmux_option "@batt_color_charge_secondary_tier5" "$color_charge_secondary_tier5_default")
color_charge_secondary_tier4=$(get_tmux_option "@batt_color_charge_secondary_tier4" "$color_charge_secondary_tier4_default")
color_charge_secondary_tier3=$(get_tmux_option "@batt_color_charge_secondary_tier3" "$color_charge_secondary_tier3_default")
color_charge_secondary_tier2=$(get_tmux_option "@batt_color_charge_secondary_tier2" "$color_charge_secondary_tier2_default")
color_charge_secondary_tier1=$(get_tmux_option "@batt_color_charge_secondary_tier1" "$color_charge_secondary_tier1_default")
}
print_color_charge() {
local primary_plane="$1"
local secondary_plane=""
if [ "$primary_plane" == "bg" ]; then
secondary_plane="fg"
else
secondary_plane="bg"
fi
percentage=$($CURRENT_DIR/battery_percentage.sh | sed -e 's/%//')
if [ $percentage -ge 95 -o "$percentage" == "" ]; then
# if percentage is empty, assume it's a desktop
printf "#[$primary_plane=$color_charge_primary_tier8${color_charge_secondary_tier8:+",$secondary_plane=$color_charge_secondary_tier8"}]"
elif [ $percentage -ge 80 ]; then
printf "#[$primary_plane=$color_charge_primary_tier7${color_charge_secondary_tier7:+",$secondary_plane=$color_charge_secondary_tier7"}]"
elif [ $percentage -ge 65 ]; then
printf "#[$primary_plane=$color_charge_primary_tier6${color_charge_secondary_tier6:+",$secondary_plane=$color_charge_secondary_tier6"}]"
elif [ $percentage -ge 50 ]; then
printf "#[$primary_plane=$color_charge_primary_tier5${color_charge_secondary_tier5:+",$secondary_plane=$color_charge_secondary_tier5"}]"
elif [ $percentage -ge 35 ]; then
printf "#[$primary_plane=$color_charge_primary_tier4${color_charge_secondary_tier4:+",$secondary_plane=$color_charge_secondary_tier4"}]"
elif [ $percentage -ge 20 ]; then
printf "#[$primary_plane=$color_charge_primary_tier3${color_charge_secondary_tier3:+",$secondary_plane=$color_charge_secondary_tier3"}]"
elif [ $percentage -gt 5 ]; then
printf "#[$primary_plane=$color_charge_primary_tier2${color_charge_secondary_tier2:+",$secondary_plane=$color_charge_secondary_tier2"}]"
else
printf "#[$primary_plane=$color_charge_primary_tier1${color_charge_secondary_tier1:+",$secondary_plane=$color_charge_secondary_tier1"}]"
fi
}
main() {
local plane="$1"
get_color_charge_settings
print_color_charge "$plane"
}
main $@

View File

@ -0,0 +1,74 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
# script global variables
color_status_primary_charged=''
color_status_primary_charging=''
color_status_primary_discharging=''
color_status_primary_attached=''
color_status_primary_unknown=''
color_status_secondary_charged=''
color_status_secondary_charging=''
color_status_secondary_discharging=''
color_status_secondary_attached=''
color_status_secondary_unknown=''
# script default variables
color_status_primary_charged_default='colour33'
color_status_primary_charging_default='colour33'
color_status_primary_discharging_default='colour14'
color_status_primary_attached_default='colour201'
color_status_primary_unknown_default='colour7'
color_status_secondary_charged_default='colour0'
color_status_secondary_charging_default='colour0'
color_status_secondary_discharging_default='colour0'
color_status_secondary_attached_default='colour0'
color_status_secondary_unknown_default='colour0'
# colors are set as script global variables
get_color_status_settings() {
color_status_primary_charged=$(get_tmux_option "@batt_color_status_primary_charged" "$color_status_primary_charged_default")
color_status_primary_charging=$(get_tmux_option "@batt_color_status_primary_charging" "$color_status_primary_charging_default")
color_status_primary_discharging=$(get_tmux_option "@batt_color_status_primary_discharging" "$color_status_primary_discharging_default")
color_status_primary_attached=$(get_tmux_option "@batt_color_status_primary_attached" "$color_status_primary_attached_default")
color_status_primary_unknown=$(get_tmux_option "@batt_color_status_primary_unknown" "$color_status_primary_unknown_default")
color_status_secondary_charged=$(get_tmux_option "@batt_color_status_secondary_charged" "$color_status_secondary_charged_default")
color_status_secondary_charging=$(get_tmux_option "@batt_color_status_secondary_charging" "$color_status_secondary_charging_default")
color_status_secondary_discharging=$(get_tmux_option "@batt_color_status_secondary_discharging" "$color_status_secondary_discharging_default")
color_status_secondary_attached=$(get_tmux_option "@batt_color_status_secondary_attached" "$color_status_secondary_attached_default")
color_status_secondary_unknown=$(get_tmux_option "@batt_color_status_secondary_unknown" "$color_status_secondary_unknown_default")
}
print_color_status() {
local plane_primary="$1"
local plane_secondary=""
if [ "$plane_primary" == "bg" ]; then
plane_secondary="fg"
else
plane_secondary="bg"
fi
local status="$2"
if [[ $status =~ (charged) || $status =~ (full) ]]; then
printf "#[$plane_primary=$color_status_primary_charged${color_status_secondary_charged:+",$plane_secondary=$color_status_secondary_charged"}]"
elif [[ $status =~ (^charging) ]]; then
printf "#[$plane_primary=$color_status_primary_charging${color_status_secondary_charging:+",$plane_secondary=$color_status_secondary_charging"}]"
elif [[ $status =~ (^discharging) ]]; then
printf "#[$plane_primary=$color_status_primary_discharging${color_status_secondary_discharging:+",$plane_secondary=$color_status_secondary_discharging"}]"
elif [[ $status =~ (attached) ]]; then
printf "#[$plane_primary=$color_status_primary_attached${color_status_secondary_attached:+",$plane_secondary=$color_status_secondary_attached"}]"
else
printf "#[$plane_primary=$color_status_primary_unknown${color_status_secondary_unknown:+",$plane_secondary=$color_status_secondary_unknown"}]"
fi
}
main() {
local plane="$1"
local status=${2:-$(battery_status)}
get_color_status_settings
print_color_status "$plane" "$status"
}
main $@

View File

@ -0,0 +1,45 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
print_graph() {
local percentage=$1
if [ $percentage -gt 5 ]; then
printf "▁"
else
printf " "
fi
if [ $percentage -ge 25 ]; then
printf "▂"
else
printf " "
fi
if [ $percentage -ge 50 ]; then
printf "▄"
else
printf " "
fi
if [ $percentage -ge 75 ]; then
printf "▆"
else
printf " "
fi
if [ $percentage -ge 95 ]; then
printf "█"
else
printf " "
echo "▇"
fi
}
main() {
local percentage=$($CURRENT_DIR/battery_percentage.sh)
if [ "$(uname)" == "OpenBSD" ]; then
print_graph ${percentage}
else
print_graph ${percentage%?}
fi
}
main

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
print_icon() {
local status=$1
if [ "$status" == "discharging" ]; then
$CURRENT_DIR/battery_icon_charge.sh
else
$CURRENT_DIR/battery_icon_status.sh "$status"
fi
}
main() {
local status=$(battery_status)
print_icon "$status"
}
main

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
# script global variables
icon_charge_tier8=''
icon_charge_tier7=''
icon_charge_tier6=''
icon_charge_tier5=''
icon_charge_tier4=''
icon_charge_tier3=''
icon_charge_tier2=''
icon_charge_tier1=''
# script default variables
icon_charge_tier8_default='█'
icon_charge_tier7_default='▇'
icon_charge_tier6_default='▆'
icon_charge_tier5_default='▅'
icon_charge_tier4_default='▄'
icon_charge_tier3_default='▃'
icon_charge_tier2_default='▂'
icon_charge_tier1_default='▁'
# icons are set as script global variables
get_icon_charge_settings() {
icon_charge_tier8=$(get_tmux_option "@batt_icon_charge_tier8" "$icon_charge_tier8_default")
icon_charge_tier7=$(get_tmux_option "@batt_icon_charge_tier7" "$icon_charge_tier7_default")
icon_charge_tier6=$(get_tmux_option "@batt_icon_charge_tier6" "$icon_charge_tier6_default")
icon_charge_tier5=$(get_tmux_option "@batt_icon_charge_tier5" "$icon_charge_tier5_default")
icon_charge_tier4=$(get_tmux_option "@batt_icon_charge_tier4" "$icon_charge_tier4_default")
icon_charge_tier3=$(get_tmux_option "@batt_icon_charge_tier3" "$icon_charge_tier3_default")
icon_charge_tier2=$(get_tmux_option "@batt_icon_charge_tier2" "$icon_charge_tier2_default")
icon_charge_tier1=$(get_tmux_option "@batt_icon_charge_tier1" "$icon_charge_tier1_default")
}
print_icon_charge() {
percentage=$($CURRENT_DIR/battery_percentage.sh | sed -e 's/%//')
if [ $percentage -ge 95 -o "$percentage" == "" ]; then
# if percentage is empty, assume it's a desktop
printf "$icon_charge_tier8"
elif [ $percentage -ge 80 ]; then
printf "$icon_charge_tier7"
elif [ $percentage -ge 65 ]; then
printf "$icon_charge_tier6"
elif [ $percentage -ge 50 ]; then
printf "$icon_charge_tier5"
elif [ $percentage -ge 35 ]; then
printf "$icon_charge_tier4"
elif [ $percentage -ge 20 ]; then
printf "$icon_charge_tier3"
elif [ $percentage -gt 5 ]; then
printf "$icon_charge_tier2"
else
printf "$icon_charge_tier1"
fi
}
main() {
get_icon_charge_settings
print_icon_charge
}
main

View File

@ -0,0 +1,61 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
# script global variables
icon_status_charged=''
icon_status_charging=''
icon_status_discharging=''
icon_status_attached=''
icon_status_unknown=''
# script default variables
icon_status_charged_default='🔌'
icon_status_charged_default_osx='🔌'
icon_status_charging_default='🔌'
icon_status_discharging_default='🔋'
icon_status_attached_default='⚠️'
icon_status_unknown_default='?'
# determine which charged_default variable to use
get_icon_status_charged_default() {
if is_osx; then
printf "$icon_status_charged_default_osx"
else
printf "$icon_status_charged_default"
fi
}
# icons are set as script global variables
get_icon_status_settings() {
icon_status_charged=$(get_tmux_option "@batt_icon_status_charged" "$(get_icon_status_charged_default)")
icon_status_charging=$(get_tmux_option "@batt_icon_status_charging" "$icon_status_charging_default")
icon_status_discharging=$(get_tmux_option "@batt_icon_status_discharging" "$icon_status_discharging_default")
icon_status_attached=$(get_tmux_option "@batt_icon_status_attached" "$icon_status_attached_default")
icon_status_unknown=$(get_tmux_option "@batt_icon_status_unknown" "$icon_status_unknown_default")
}
print_icon_status() {
local status=$1
if [[ $status =~ (charged) || $status =~ (full) ]]; then
printf "$icon_status_charged"
elif [[ $status =~ (^charging) ]]; then
printf "$icon_status_charging"
elif [[ $status =~ (^discharging) ]]; then
printf "$icon_status_discharging"
elif [[ $status =~ (attached) ]]; then
printf "$icon_status_attached"
else
printf "$icon_status_unknown"
fi
}
main() {
get_icon_status_settings
local status=${1:-$(battery_status)}
print_icon_status "$status"
}
main

View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
print_battery_percentage() {
# percentage displayed in the 2nd field of the 2nd row
if command_exists "pmset"; then
pmset -g batt | grep -o "[0-9]\{1,3\}%"
elif command_exists "acpi"; then
acpi -b | grep -m 1 -Eo "[0-9]+%"
elif command_exists "upower"; then
# use DisplayDevice if available otherwise battery
local battery=$(upower -e | grep -E 'battery|DisplayDevice'| tail -n1)
if [ -z "$battery" ]; then
return
fi
local percentage=$(upower -i $battery | awk '/percentage:/ {print $2}')
if [ "$percentage" ]; then
echo ${percentage%.*%}
return
fi
local energy
local energy_full
energy=$(upower -i $battery | awk -v nrg="$energy" '/energy:/ {print nrg+$2}')
energy_full=$(upower -i $battery | awk -v nrgfull="$energy_full" '/energy-full:/ {print nrgfull+$2}')
if [ -n "$energy" ] && [ -n "$energy_full" ]; then
echo $energy $energy_full | awk '{printf("%d%%", ($1/$2)*100)}'
fi
elif command_exists "termux-battery-status"; then
termux-battery-status | jq -r '.percentage' | awk '{printf("%d%%", $1)}'
elif command_exists "apm"; then
apm -l
fi
}
main() {
print_battery_percentage
}
main

View File

@ -0,0 +1,128 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
short=false
get_remain_settings() {
short=$(get_tmux_option "@batt_remain_short" false)
}
battery_discharging() {
local status="$(battery_status)"
[[ $status =~ (discharging) ]]
}
battery_charged() {
local status="$(battery_status)"
[[ $status =~ (charged) ]]
}
convertmins() {
((h=${1}/60))
((m=${1}%60))
printf "%02d:%02d\n" $h $m $s
}
apm_battery_remaining_time() {
local remaining_time="$(convertmins $(apm -m))"
if battery_discharging; then
if $short; then
echo $remaining_time | awk '{printf "~%s", $1}'
else
echo $remaining_time | awk '{printf "- %s left", $1}'
fi
elif battery_charged; then
if $short; then
echo $remaining_time | awk '{printf "charged", $1}'
else
echo $remaining_time | awk '{printf "fully charged", $1}'
fi
else
echo "charging"
fi
}
pmset_battery_remaining_time() {
local status="$(pmset -g batt)"
if echo $status | grep 'no estimate' >/dev/null 2>&1; then
if $short; then
echo '~?:??'
else
echo '- Calculating estimate...'
fi
else
local remaining_time="$(echo $status | grep -o '[0-9]\{1,2\}:[0-9]\{1,2\}')"
if battery_discharging; then
if $short; then
echo $remaining_time | awk '{printf "~%s", $1}'
else
echo $remaining_time | awk '{printf "- %s left", $1}'
fi
elif battery_charged; then
if $short; then
echo $remaining_time | awk '{printf "charged", $1}'
else
echo $remaining_time | awk '{printf "fully charged", $1}'
fi
else
if $short; then
echo $remaining_time | awk '{printf "~%s", $1}'
else
echo $remaining_time | awk '{printf "- %s till full", $1}'
fi
fi
fi
}
upower_battery_remaining_time() {
battery=$(upower -e | grep -E 'battery|DisplayDevice'| tail -n1)
if battery_discharging; then
local remaining_time
remaining_time=$(upower -i "$battery" | grep -E '(remain|time to empty)')
if $short; then
echo "$remaining_time" | awk '{printf "%s %s", $(NF-1), $(NF)}'
else
echo "$remaining_time" | awk '{printf "%s %s left", $(NF-1), $(NF)}'
fi
elif battery_charged; then
if $short; then
echo ""
else
echo "charged"
fi
else
local remaining_time
remaining_time=$(upower -i "$battery" | grep -E 'time to full')
if $short; then
echo "$remaining_time" | awk '{printf "%s %s", $(NF-1), $(NF)}'
else
echo "$remaining_time" | awk '{printf "%s %s to full", $(NF-1), $(NF)}'
fi
fi
}
acpi_battery_remaining_time() {
acpi -b | grep -m 1 -Eo "[0-9]+:[0-9]+:[0-9]+"
}
print_battery_remain() {
if command_exists "pmset"; then
pmset_battery_remaining_time
elif command_exists "acpi"; then
acpi_battery_remaining_time
elif command_exists "upower"; then
upower_battery_remaining_time
elif command_exists "apm"; then
apm_battery_remaining_time
fi
}
main() {
get_remain_settings
print_battery_remain
}
main

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
color_full_charge_default="#[bg=green]"
color_high_charge_default="#[bg=yellow]"
color_medium_charge_default="#[bg=colour208]" # orange
color_low_charge_default="#[bg=red]"
color_charging_default="#[bg=green]"
color_full_charge=""
color_high_charge=""
color_medium_charge=""
color_low_charge=""
color_charging=""
get_charge_color_settings() {
color_full_charge=$(get_tmux_option "@batt_color_full_charge" "$color_full_charge_default")
color_high_charge=$(get_tmux_option "@batt_color_high_charge" "$color_high_charge_default")
color_medium_charge=$(get_tmux_option "@batt_color_medium_charge" "$color_medium_charge_default")
color_low_charge=$(get_tmux_option "@batt_color_low_charge" "$color_low_charge_default")
color_charging=$(get_tmux_option "@batt_color_charging" "$color_charging_default")
}
print_battery_status_bg() {
# Call `battery_percentage.sh`.
percentage=$($CURRENT_DIR/battery_percentage.sh | sed -e 's/%//')
status=$(battery_status | awk '{print $1;}')
if [ $status == 'charging' ]; then
printf $color_charging
elif [ $percentage -eq 100 ]; then
printf $color_full_charge
elif [ $percentage -le 99 -a $percentage -ge 51 ];then
printf $color_high_charge
elif [ $percentage -le 50 -a $percentage -ge 16 ];then
printf $color_medium_charge
elif [ "$percentage" == "" ];then
printf $color_full_charge_default # assume it's a desktop
else
printf $color_low_charge
fi
}
main() {
get_charge_color_settings
print_battery_status_bg
}
main

View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
color_full_charge_default="#[fg=green]"
color_high_charge_default="#[fg=yellow]"
color_medium_charge_default="#[fg=colour208]" # orange
color_low_charge_default="#[fg=red]"
color_charging_default="#[fg=green]"
color_full_charge=""
color_high_charge=""
color_medium_charge=""
color_low_charge=""
color_charging=""
get_charge_color_settings() {
color_full_charge=$(get_tmux_option "@batt_color_full_charge" "$color_full_charge_default")
color_high_charge=$(get_tmux_option "@batt_color_high_charge" "$color_high_charge_default")
color_medium_charge=$(get_tmux_option "@batt_color_medium_charge" "$color_medium_charge_default")
color_low_charge=$(get_tmux_option "@batt_color_low_charge" "$color_low_charge_default")
color_charging=$(get_tmux_option "@batt_color_charging" "$color_charging_default")
}
print_battery_status_fg() {
# Call `battery_percentage.sh`.
percentage=$($CURRENT_DIR/battery_percentage.sh | sed -e 's/%//')
status=$(battery_status | awk '{print $1;}')
if [ $status == 'charging' ]; then
printf $color_charging
elif [ $percentage -eq 100 ]; then
printf $color_full_charge
elif [ $percentage -le 99 -a $percentage -ge 51 ];then
printf $color_high_charge
elif [ $percentage -le 50 -a $percentage -ge 16 ];then
printf $color_medium_charge
elif [ "$percentage" == "" ];then
printf $color_full_charge_default # assume it's a desktop
else
printf $color_low_charge
fi
}
main() {
get_charge_color_settings
print_battery_status_fg
}
main

View File

@ -0,0 +1,50 @@
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value="$(tmux show-option -gqv "$option")"
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
is_osx() {
[ $(uname) == "Darwin" ]
}
is_chrome() {
chrome="/sys/class/chromeos/cros_ec"
if [ -d "$chrome" ]; then
return 0
else
return 1
fi
}
command_exists() {
local command="$1"
type "$command" >/dev/null 2>&1
}
battery_status() {
if command_exists "pmset"; then
pmset -g batt | awk -F '; *' 'NR==2 { print $2 }'
elif command_exists "acpi"; then
acpi -b | awk '{gsub(/,/, ""); print tolower($3); exit}'
elif command_exists "upower"; then
local battery
battery=$(upower -e | grep -E 'battery|DisplayDevice'| tail -n1)
upower -i $battery | awk '/state/ {print $2}'
elif command_exists "termux-battery-status"; then
termux-battery-status | jq -r '.status' | awk '{printf("%s%", tolower($1))}'
elif command_exists "apm"; then
local battery
battery=$(apm -a)
if [ $battery -eq 0 ]; then
echo "discharging"
elif [ $battery -eq 1 ]; then
echo "charging"
fi
fi
}

View File

@ -0,0 +1,19 @@
Copyright (C) Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,98 @@
# tmux-continuum
Features:
- continuous saving of tmux environment
- automatic tmux start when computer/server is turned on
- automatic restore when tmux is started
Together these features enable uninterrupted tmux usage. No matter the computer
or server restarts, if the machine is on, tmux will be there how you left it off
the last time it was used.
Tested and working on Linux, OSX and Cygwin.
#### Continuous saving
Tmux environment will be saved at the interval of 15 minutes. All the saving
happens in the background without the impact to your workflow.
This action starts automatically when the plugin is installed.
#### Automatic tmux start
Tmux is automatically started after the computer/server is turned on.
See the [instructions](docs/automatic_start.md) how to enable this for your
system.
#### Automatic restore
Last saved environment is automatically restored when tmux is started.
Put `set -g @continuum-restore 'on'` in `.tmux.conf` to enable this.
Note: automatic restore happens **exclusively** on tmux server start. No other
action (e.g. sourcing `.tmux.conf`) triggers this.
#### Dependencies
`tmux 1.9` or higher, `bash`,
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) plugin.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Please make sure you have
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) installed.
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
Hit `prefix + I` to fetch the plugin and source it. The plugin will
automatically start "working" in the background, no action required.
### Manual Installation
Please make sure you have
[tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) installed.
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-continuum ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/continuum.tmux
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`
The plugin will automatically start "working" in the background, no action
required.
### Docs
- [frequently asked questions](docs/faq.md)
- [behavior when running multiple tmux servers](docs/multiple_tmux_servers.md) -
this doc is safe to skip, but you might want to read it if you're using tmux
with `-L` or `-S` flags
- [automatically start tmux after the computer is turned on](docs/automatic_start.md)
- [continuum status in tmux status line](docs/continuum_status.md)
### Other goodies
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
highlighted text to system clipboard
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
opening highlighted file or a url
### Reporting bugs and contributing
Both contributing and bug reports are welcome. Please check out
[contributing guidelines](CONTRIBUTING.md).
### License
[MIT](LICENSE.md)

View File

@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -x
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/helpers.sh"
source "$CURRENT_DIR/scripts/variables.sh"
source "$CURRENT_DIR/scripts/shared.sh"
save_command_interpolation="#($CURRENT_DIR/scripts/continuum_save.sh)"
supported_tmux_version_ok() {
"$CURRENT_DIR/scripts/check_tmux_version.sh" "$SUPPORTED_VERSION"
}
handle_tmux_automatic_start() {
"$CURRENT_DIR/scripts/handle_tmux_automatic_start.sh"
}
another_tmux_server_running() {
if just_started_tmux_server; then
another_tmux_server_running_on_startup
else
# script loaded after tmux server start can have multiple clients attached
[ "$(number_tmux_processes_except_current_server)" -gt "$(number_current_server_client_processes)" ]
fi
}
delay_saving_environment_on_first_plugin_load() {
if [ -z "$(get_tmux_option "$last_auto_save_option" "")" ]; then
# last save option not set, this is first time plugin load
set_last_save_timestamp
fi
}
add_resurrect_save_interpolation() {
local status_right_value="$(get_tmux_option "status-right" "")"
# check interpolation not already added
if ! [[ "$status_right_value" == *"$save_command_interpolation"* ]]; then
local new_value="${save_command_interpolation}${status_right_value}"
set_tmux_option "status-right" "$new_value"
fi
}
just_started_tmux_server() {
local tmux_start_time
tmux_start_time="$(tmux display-message -p -F '#{start_time}')"
[ "$tmux_start_time" == "" ] || [ "$tmux_start_time" -gt "$(($(date +%s)-10))" ]
}
start_auto_restore_in_background() {
"$CURRENT_DIR/scripts/continuum_restore.sh" &
}
update_tmux_option() {
local option="$1"
local option_value="$(get_tmux_option "$option")"
# replace interpolation string with a script to execute
local new_option_value="${option_value/$status_interpolation_string/$status_script}"
set_tmux_option "$option" "$new_option_value"
}
main() {
if supported_tmux_version_ok; then
handle_tmux_automatic_start
# Advanced edge case handling: start auto-saving only if this is the
# only tmux server. We don't want saved files from more environments to
# overwrite each other.
if ! another_tmux_server_running; then
# give user a chance to restore previously saved session
delay_saving_environment_on_first_plugin_load
add_resurrect_save_interpolation
fi
if just_started_tmux_server; then
start_auto_restore_in_background
fi
# Put "#{continuum_status}" interpolation in status-right or
# status-left tmux option to get current tmux continuum status.
update_tmux_option "status-right"
update_tmux_option "status-left"
fi
}
main

View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
VERSION="$1"
UNSUPPORTED_MSG="$2"
# this is used to get "clean" integer version number. Examples:
# `tmux 1.9` => `19`
# `1.9a` => `19`
get_digits_from_string() {
local string="$1"
local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
echo "$only_digits"
}
tmux_version_int() {
local tmux_version_string=$(tmux -V)
echo "$(get_digits_from_string "$tmux_version_string")"
}
exit_if_unsupported_version() {
local current_version="$1"
local supported_version="$2"
if [ "$current_version" -lt "$supported_version" ]; then
exit 1
fi
}
main() {
local supported_version_int="$(get_digits_from_string "$VERSION")"
local current_version_int="$(tmux_version_int)"
exit_if_unsupported_version "$current_version_int" "$supported_version_int"
}
main

View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
auto_restore_enabled() {
local auto_restore_value="$(get_tmux_option "$auto_restore_option" "$auto_restore_default")"
[ "$auto_restore_value" == "on" ] && [ ! -f "$auto_restore_halt_file" ]
}
fetch_and_run_tmux_resurrect_restore_script() {
# give tmux some time to start and source all the plugins
sleep 1
local resurrect_restore_script_path="$(get_tmux_option "$resurrect_restore_path_option" "")"
if [ -n "$resurrect_restore_script_path" ]; then
"$resurrect_restore_script_path"
fi
}
main() {
# Advanced edge case handling: auto restore only if this is the only tmux
# server. If another tmux server exists, it is assumed auto-restore is not wanted.
if auto_restore_enabled && ! another_tmux_server_running_on_startup; then
fetch_and_run_tmux_resurrect_restore_script
fi
}
main

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/shared.sh"
supported_tmux_version_ok() {
"$CURRENT_DIR/check_tmux_version.sh" "$SUPPORTED_VERSION"
}
get_interval() {
get_tmux_option "$auto_save_interval_option" "$auto_save_interval_default"
}
auto_save_not_disabled() {
[ "$(get_interval)" -gt 0 ]
}
enough_time_since_last_run_passed() {
local last_saved_timestamp="$(get_tmux_option "$last_auto_save_option" "0")"
local interval_minutes="$(get_interval)"
local interval_seconds="$((interval_minutes * 60))"
local next_run="$((last_saved_timestamp + $interval_seconds))"
[ "$(current_timestamp)" -ge "$next_run" ]
}
fetch_and_run_tmux_resurrect_save_script() {
local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "")"
if [ -n "$resurrect_save_script_path" ]; then
"$resurrect_save_script_path" "quiet" >/dev/null 2>&1 &
set_last_save_timestamp
fi
}
acquire_lock() {
# Sometimes tmux starts multiple saves in parallel. We want only one
# save to be running, otherwise we can get corrupted saved state.
local lockdir_prefix="/tmp/tmux-continuum-$(current_tmux_server_pid)-lock-"
# The following implements a lock that auto-expires after 100...200s.
local lock_generation=$((`date +%s` / 100))
local lockdir1="${lockdir_prefix}${lock_generation}"
local lockdir2="${lockdir_prefix}$(($lock_generation + 1))"
if mkdir "$lockdir1"; then
trap "rmdir "$lockdir1"" EXIT
if mkdir "$lockdir2"; then
trap "rmdir "$lockdir1" "$lockdir2"" EXIT
return 0
fi
fi
return 1 # Someone else has the lock.
}
main() {
if supported_tmux_version_ok && auto_save_not_disabled && enough_time_since_last_run_passed && acquire_lock; then
fetch_and_run_tmux_resurrect_save_script
fi
}
main

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
print_status() {
local save_int="$(get_tmux_option "$auto_save_interval_option")"
local status=""
local style_wrap
if [ $save_int -gt 0 ]; then
style_wrap="$(get_tmux_option "$status_on_style_wrap_option" "")"
status="$save_int"
else
style_wrap="$(get_tmux_option "$status_off_style_wrap_option" "")"
status="off"
fi
if [ -n "$style_wrap" ]; then
status="${style_wrap/$status_wrap_string/$status}"
fi
echo "$status"
}
print_status

View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/variables.sh"
is_tmux_automatic_start_enabled() {
local auto_start_value="$(get_tmux_option "$auto_start_option" "$auto_start_default")"
[ "$auto_start_value" == "on" ]
}
is_osx() {
[ $(uname) == "Darwin" ]
}
is_systemd() {
[ $(ps -o comm= -p1) == 'systemd' ]
}
main() {
if is_tmux_automatic_start_enabled; then
if is_osx; then
"$CURRENT_DIR/handle_tmux_automatic_start/osx_enable.sh"
elif is_systemd; then
"$CURRENT_DIR/handle_tmux_automatic_start/systemd_enable.sh"
fi
else
if is_osx; then
"$CURRENT_DIR/handle_tmux_automatic_start/osx_disable.sh"
elif is_systemd; then
"$CURRENT_DIR/handle_tmux_automatic_start/systemd_disable.sh"
fi
fi
}
main

View File

@ -0,0 +1 @@
docs/automatic_start.md

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../variables.sh"
main() {
rm "$osx_auto_start_file_path" > /dev/null 2>&1
}
main

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../helpers.sh"
source "$CURRENT_DIR/../variables.sh"
template() {
local tmux_start_script="$1"
local is_fullscreen="$2"
local fullscreen_tag=""
if [ "$is_fullscreen" == "true" ]; then
# newline and spacing so tag is aligned with other tags in template
fullscreen_tag=$'\n <string>fullscreen</string>'
fi
local content
read -r -d '' content <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>${osx_auto_start_file_name}</string>
<key>ProgramArguments</key>
<array>
<string>${tmux_start_script}</string>$fullscreen_tag
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF
echo "$content"
}
get_iterm_or_teminal_option_value() {
local options="$1"
if [[ "$options" =~ "iterm" ]]; then
echo "iterm"
else
# Terminal.app is the default console app
echo "terminal"
fi
}
get_fullscreen_option_value() {
local options="$1"
if [[ "$options" =~ "fullscreen" ]]; then
echo "true"
else
echo "false"
fi
}
main() {
local options="$(get_tmux_option "$auto_start_config_option" "$auto_start_config_default")"
local iterm_or_terminal_value="$(get_iterm_or_teminal_option_value "$options")"
local fullscreen_option_value="$(get_fullscreen_option_value "$options")"
local tmux_start_script_path="${CURRENT_DIR}/osx_${iterm_or_terminal_value}_start_tmux.sh"
local launchd_plist_file_content="$(template "$tmux_start_script_path" "$fullscreen_option_value")"
echo "$launchd_plist_file_content" > "$osx_auto_start_file_path"
}
main

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_iterm_and_run_tmux() {
osascript <<-EOF
tell application "iTerm"
activate
# open iterm window
try
set _session to current session of current terminal
on error
set _term to (make new terminal)
tell _term
launch session "Tmux"
set _session to current session
end tell
end try
# start tmux
tell _session
write text "tmux"
end tell
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "iTerm"
set winID to id of window 1
tell i term application "Finder"
set desktopSize to bounds of window of desktop
end tell
set bounds of window id winID to desktopSize
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "iTerm"
# wait for iTerm to start
delay 1
activate
# short wait for iTerm to gain focus
delay 0.1
# Command + Enter for fullscreen
tell i term application "System Events"
key code 36 using {command down}
end tell
end tell
EOF
}
main() {
start_iterm_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@ -0,0 +1,52 @@
#!/usr/bin/env bash
# for "true full screen" call the script with "fullscreen" as the first argument
TRUE_FULL_SCREEN="$1"
start_terminal_and_run_tmux() {
osascript <<-EOF
tell application "Terminal"
if not (exists window 1) then reopen
activate
set winID to id of window 1
do script "tmux" in window id winID
end tell
EOF
}
resize_window_to_full_screen() {
osascript <<-EOF
tell application "Terminal"
set winID to id of window 1
tell application "Finder"
set desktopSize to bounds of window of desktop
end tell
set bounds of window id winID to desktopSize
end tell
EOF
}
resize_to_true_full_screen() {
osascript <<-EOF
tell application "Terminal"
# waiting for Terminal.app to start
delay 1
activate
# short wait for Terminal to gain focus
delay 0.1
tell application "System Events"
keystroke "f" using {control down, command down}
end tell
end tell
EOF
}
main() {
start_terminal_and_run_tmux
if [ "$TRUE_FULL_SCREEN" == "fullscreen" ]; then
resize_to_true_full_screen
else
resize_window_to_full_screen
fi
}
main

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/../variables.sh"
main() {
systemctl --user disable ${systemd_service_name}
}
main

View File

@ -0,0 +1,56 @@
#!/usr/bin/env bash
CURRENT_DIR="$( dirname ${BASH_SOURCE[0]} )"
source "$CURRENT_DIR/../helpers.sh"
source "$CURRENT_DIR/../variables.sh"
template() {
local tmux_start_script="$1"
shift
local options="$@"
local content=""
read -r -d '' content <<-EOF
[Unit]
Description=tmux default session (detached)
Documentation=man:tmux(1)
[Service]
Type=forking
Environment=DISPLAY=:0
ExecStart=/usr/bin/tmux ${systemd_tmux_server_start_cmd}
ExecStop=${HOME}/.tmux/plugins/tmux-resurrect/scripts/save.sh
ExecStop=/usr/bin/tmux kill-server
KillMode=none
RestartSec=2
[Install]
WantedBy=default.target
EOF
echo "$content"
}
systemd_tmux_is_enabled() {
systemctl --user is_enabled $(basename "${systemd_unit_file_path}") >/dev/null 2>&1
}
enable_tmux_unit_on_boot() {
if ! systemd_tmux_is_enabled; then
systemctl --user enable ${systemd_service_name}
fi
}
main() {
local options="$(get_tmux_option "$auto_start_config_option" "${auto_start_config_default}")"
local systemd_tmux_server_start_cmd="$(get_tmux_option "${systemd_tmux_server_start_cmd_option}" "${systemd_tmux_server_start_cmd_default}" )"
local tmux_start_script_path="${CURRENT_DIR}/linux_start_tmux.sh"
local systemd_unit_file=$(template "${tmux_start_script_path}" "${options}")
mkdir -p "$(dirname ${systemd_unit_file_path})"
echo "$systemd_unit_file" > "${systemd_unit_file_path}"
enable_tmux_unit_on_boot
}
main

View File

@ -0,0 +1,48 @@
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
set_tmux_option() {
local option="$1"
local value="$2"
tmux set-option -gq "$option" "$value"
}
# multiple tmux server detection helpers
current_tmux_server_pid() {
echo "$TMUX" |
cut -f2 -d","
}
all_tmux_processes() {
# ignores `tmux source-file .tmux.conf` command used to reload tmux.conf
ps -Ao "command pid" |
\grep "^tmux" |
\grep -v "^tmux source"
}
number_tmux_processes_except_current_server() {
all_tmux_processes |
\grep -v " $(current_tmux_server_pid)$" |
wc -l |
sed "s/ //g"
}
number_current_server_client_processes() {
tmux list-clients |
wc -l |
sed "s/ //g"
}
another_tmux_server_running_on_startup() {
# there are 2 tmux processes (current tmux server + 1) on tmux startup
[ "$(number_tmux_processes_except_current_server)" -gt 1 ]
}

View File

@ -0,0 +1,7 @@
current_timestamp() {
echo "$(date +%s)"
}
set_last_save_timestamp() {
set_tmux_option "$last_auto_save_option" "$(current_timestamp)"
}

View File

@ -0,0 +1,40 @@
SUPPORTED_VERSION="1.9"
# these tmux options contain paths to tmux resurrect save and restore scripts
resurrect_save_path_option="@resurrect-save-script-path"
resurrect_restore_path_option="@resurrect-restore-script-path"
auto_save_interval_option="@continuum-save-interval"
auto_save_interval_default="15"
# time when the tmux environment was last saved (unix timestamp)
last_auto_save_option="@continuum-save-last-timestamp"
auto_restore_option="@continuum-restore"
auto_restore_default="off"
auto_restore_halt_file="${HOME}/tmux_no_auto_restore"
# tmux auto start options
auto_start_option="@continuum-boot"
auto_start_default="off"
# comma separated list of additional options for tmux auto start
auto_start_config_option="@continuum-boot-options"
auto_start_config_default=""
osx_auto_start_file_name="Tmux.Start.plist"
osx_auto_start_file_path="${HOME}/Library/LaunchAgents/${osx_auto_start_file_name}"
status_interpolation_string="\#{continuum_status}"
status_script="#($CURRENT_DIR/scripts/continuum_status.sh)"
# below options set style/color for #{continuum_status} interpolation
status_on_style_wrap_option="@continuum-status-on-wrap-style" # example value: "#[fg=green]#{value}#[fg=white]"
status_off_style_wrap_option="@continuum-status-off-wrap-style" # example value: "#[fg=yellow,bold]#{value}#[fg=white,nobold]"
status_wrap_string="\#{value}"
systemd_service_name="tmux.service"
systemd_unit_file_path="$HOME/.config/systemd/user/${systemd_service_name}"
systemd_tmux_server_start_cmd_option="@continuum-systemd-start-cmd"
systemd_tmux_server_start_cmd_default="new-session -d"

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,142 @@
# Tmux copycat
[![Build Status](https://travis-ci.org/tmux-plugins/tmux-copycat.svg?branch=master)](https://travis-ci.org/tmux-plugins/tmux-copycat)
This plugin enables:
- regex searches
- search result highlighting
- predefined searches
Predefined searches are plugin killer feature. It speeds the workflow and
reduces mouse usage with Tmux.
It works even better when paired with
[tmux yank](https://github.com/tmux-plugins/tmux-yank). Tested and working on
Linux, OSX and Cygwin.
### Screencast
[![screencast screenshot](/video/screencast_img.png)](https://vimeo.com/101867689)
#### Search
- `prefix + /` - regex search (strings work too)
Example search entries:
- `foo` - searches for string `foo`
- `[0-9]+` - regex search for numbers
Grep is used for searching.<br/>
Searches are case insensitive.<br/>
#### Predefined searches
- `prefix + ctrl-f` - simple *f*ile search
- `prefix + ctrl-g` - jumping over *g*it status files (best used after `git status` command)
- `prefix + alt-h` - jumping over SHA-1/SHA-256 hashes (best used after `git log` command)
- `prefix + ctrl-u` - *u*rl search (http, ftp and git urls)
- `prefix + ctrl-d` - number search (mnemonic d, as digit)
- `prefix + alt-i` - *i*p address search
These start "copycat mode" and jump to first match.
#### "Copycat mode" bindings
These are enabled when you search with copycat:
- `n` - jumps to the next match
- `N` - jumps to the previous match
To copy a highlighted match:
- `Enter` - if you're using Tmux `vi` mode
- `ctrl-w` or `alt-w` - if you're using Tmux `emacs` mode
Copying a highlighted match will take you "out" of copycat mode. Paste with
`prefix + ]` (this is Tmux default paste).
Copying highlighted matches can be enhanced with
[tmux yank](https://github.com/tmux-plugins/tmux-yank).
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-copycat'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
Optional (but recommended) install `gawk` via your package manager of choice
for better UTF-8 character support.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-copycat ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/copycat.tmux
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`. You should now
be able to use the plugin.
Optional (but recommended) install `gawk` via your package manager of choice
for better UTF-8 character support.
### Installation for Tmux 2.3 and earlier
Due to the changes in tmux, the latest version of this plugin doesn't support
tmux 2.3 and earlier. It is recommended you upgrade to tmux version 2.4 or
later. If you must continue using older version, please follow
[these steps for installation](docs/installation_for_tmux_2.3.md).
### Limitations
This plugin has some known limitations. Please read about it
[here](docs/limitations.md).
### Docs
- Most of the behavior of tmux-copycat can be customized via tmux options.
[Check out the full options list](docs/customizations.md).
- To speed up the workflow you can define new bindings in `.tmux.conf` for
searches you use often, more info [here](docs/defining_new_stored_searches.md)
### Other goodies
`tmux-copycat` works great with:
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
highlighted text to system clipboard
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
opening a highlighted file or a url
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
restoring and continuous saving of tmux env
### Test suite
This plugin has a pretty extensive integration test suite that runs on
[travis](https://travis-ci.org/tmux-plugins/tmux-copycat).
When run locally, it depends on `vagrant`. Run it with:
# within project top directory
$ ./run-tests
### Contributions and new features
Bug fixes and contributions are welcome.
Feel free to suggest new features, via github issues.
If you have a bigger idea you'd like to work on, please get in touch, also via
github issues.
### License
[MIT](LICENSE.md)

View File

@ -0,0 +1,70 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/variables.sh"
source "$CURRENT_DIR/scripts/helpers.sh"
source "$CURRENT_DIR/scripts/stored_search_helpers.sh"
# this function defines default stored searches
set_default_stored_searches() {
local file_search="$(get_tmux_option "$copycat_file_search_option" "$default_file_search_key")"
local url_search="$(get_tmux_option "$copycat_url_search_option" "$default_url_search_key")"
local digit_search="$(get_tmux_option "$copycat_digit_search_option" "$default_digit_search_key")"
local hash_search="$(get_tmux_option "$copycat_hash_search_option" "$default_hash_search_key")"
local ip_search="$(get_tmux_option "$copycat_ip_search_option" "$default_ip_search_key")"
if stored_search_not_defined "$url_search"; then
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${url_search}" "(https?://|git@|git://|ssh://|ftp://|file:///)[[:alnum:]?=%/_.:,;~@!#$&()*+-]*"
fi
if stored_search_not_defined "$file_search"; then
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${file_search}" "(^|^\.|[[:space:]]|[[:space:]]\.|[[:space:]]\.\.|^\.\.)[[:alnum:]~_-]*/[][[:alnum:]_.#$%&+=/@-]*"
fi
if stored_search_not_defined "$digit_search"; then
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${digit_search}" "[[:digit:]]+"
fi
if stored_search_not_defined "$hash_search"; then
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${hash_search}" "\b([0-9a-f]{7,40}|[[:alnum:]]{52}|[0-9a-f]{62})\b"
fi
if stored_search_not_defined "$ip_search"; then
tmux set-option -g "${COPYCAT_VAR_PREFIX}_${ip_search}" "[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}"
fi
}
set_start_bindings() {
set_default_stored_searches
local stored_search_vars="$(stored_search_vars)"
local search_var
local key
local pattern
for search_var in $stored_search_vars; do
key="$(get_stored_search_key "$search_var")"
pattern="$(get_stored_search_pattern "$search_var")"
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_mode_start.sh '$pattern'"
done
}
set_copycat_search_binding() {
local key_bindings
read -r -d '' -a key_bindings <<<"$(get_tmux_option "$copycat_search_option" "$default_copycat_search_key")"
local key
for key in "${key_bindings[@]}"; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_search.sh"
done
}
set_copycat_git_special_binding() {
local key_bindings
read -r -d '' -a key_bindings <<<"$(get_tmux_option "$copycat_git_search_option" "$default_git_search_key")"
local key
for key in "${key_bindings[@]}"; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/copycat_git_special.sh #{pane_current_path}"
done
}
main() {
set_start_bindings
set_copycat_search_binding
set_copycat_git_special_binding
}
main

View File

@ -0,0 +1,78 @@
#!/usr/bin/env bash
VERSION="$1"
UNSUPPORTED_MSG="$2"
get_tmux_option() {
local option=$1
local default_value=$2
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
# this is used to get "clean" integer version number. Examples:
# `tmux 1.9` => `19`
# `1.9a` => `19`
get_digits_from_string() {
local string="$1"
local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
echo "$only_digits"
}
tmux_version_int() {
local tmux_version_string=$(tmux -V)
echo "$(get_digits_from_string "$tmux_version_string")"
}
unsupported_version_message() {
if [ -n "$UNSUPPORTED_MSG" ]; then
echo "$UNSUPPORTED_MSG"
else
echo "Error, Tmux version unsupported! Please install Tmux version $VERSION or greater!"
fi
}
exit_if_unsupported_version() {
local current_version="$1"
local supported_version="$2"
if [ "$current_version" -lt "$supported_version" ]; then
display_message "$(unsupported_version_message)"
exit 1
fi
}
main() {
local supported_version_int="$(get_digits_from_string "$VERSION")"
local current_version_int="$(tmux_version_int)"
exit_if_unsupported_version "$current_version_int" "$supported_version_int"
}
main

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
search_pattern="$1"
capture_pane() {
local file=$1
# copying 9M lines back will hopefully fetch the whole scrollback
tmux capture-pane -S -9000000 -p > "$file"
}
# doing 2 things in 1 step so that we don't write to disk too much
reverse_and_create_copycat_file() {
local file=$1
local copycat_file=$2
local grep_pattern=$3
(tac 2>/dev/null || tail -r) < "$file" | grep -oniE "$grep_pattern" > "$copycat_file"
}
delete_old_files() {
local scrollback_filename="$(get_scrollback_filename)"
local copycat_filename="$(get_copycat_filename)"
rm -f "$scrollback_filename" "$copycat_filename"
}
generate_copycat_file() {
local grep_pattern="$1"
local scrollback_filename="$(get_scrollback_filename)"
local copycat_filename="$(get_copycat_filename)"
mkdir -p "$(_get_tmp_dir)"
chmod 0700 "$(_get_tmp_dir)"
capture_pane "$scrollback_filename"
reverse_and_create_copycat_file "$scrollback_filename" "$copycat_filename" "$grep_pattern"
}
if_no_results_exit_with_message() {
local copycat_filename="$(get_copycat_filename)"
# check for empty filename
if ! [ -s "$copycat_filename" ]; then
display_message "No results!"
exit 0
fi
}
main() {
local grep_pattern="$1"
if not_in_copycat_mode; then
delete_old_files
generate_copycat_file "$grep_pattern"
if_no_results_exit_with_message
set_copycat_mode
copycat_increase_counter
fi
}
main "$search_pattern"

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_CURRENT_PATH="$1"
source "$CURRENT_DIR/helpers.sh"
git_status_files() {
local git_working_dir="$PANE_CURRENT_PATH"
local git_dir="$PANE_CURRENT_PATH/.git"
echo "$(git --git-dir="$git_dir" --work-tree="$git_working_dir" status --porcelain)"
}
formatted_git_status() {
local raw_gist_status="$(git_status_files)"
echo "$(echo "$raw_gist_status" | cut -c 4-)"
}
exit_if_no_results() {
local results="$1"
if [ -z "$results" ]; then
display_message "No results!"
exit 0
fi
}
concatenate_files() {
local git_status_files="$(formatted_git_status)"
exit_if_no_results "$git_status_files"
local result=""
# Undefined until later within a while loop.
local file_separator
while read -r line; do
result="${result}${file_separator}${line}"
file_separator="|"
done <<< "$git_status_files"
echo "$result"
}
# Creates one, big regex out of git status files.
# Example:
# `git status` shows files `foo.txt` and `bar.txt`
# output regex will be:
# `(foo.txt|bar.txt)
git_status_files_regex() {
local concatenated_files="$(concatenate_files)"
local regex_result="(${concatenated_files})"
echo "$regex_result"
}
main() {
local search_regex="$(git_status_files_regex)"
# starts copycat mode
$CURRENT_DIR/copycat_mode_start.sh "$search_regex"
}
main

View File

@ -0,0 +1,289 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
MAXIMUM_PADDING="25" # maximum padding below the result when it can't be centered
# jump to 'next' or 'prev' match
# global var for this file
NEXT_PREV="$1"
# 'vi' or 'emacs', this variable used as a global file constant
TMUX_COPY_MODE="$(tmux_copy_mode)"
_file_number_of_lines() {
local file="$1"
echo "$(wc -l $file | $AWK_CMD '{print $1}')"
}
_get_result_line() {
local file="$1"
local number="$2"
echo "$(head -"$number" "$file" | tail -1)"
}
_string_starts_with_digit() {
local string="$1"
echo "$string" |
\grep -q '^[[:digit:]]\+:'
}
_get_line_number() {
local string="$1"
local copycat_file="$2" # args 2 & 3 used to handle bug in OSX grep
local position_number="$3"
if _string_starts_with_digit "$string"; then
# we have a number!
local grep_line_number="$(echo "$string" | cut -f1 -d:)"
# grep line number index starts from 1, tmux line number index starts from 0
local tmux_line_number="$((grep_line_number - 1))"
else
# no number in the results line This is a bug in OSX grep.
# Fetching a number from a previous line.
local previous_line_num="$((position_number - 1))"
local result_line="$(_get_result_line "$copycat_file" "$previous_line_num")"
# recursively invoke this same function
tmux_line_number="$(_get_line_number "$result_line" "$copycat_file" "$previous_line_num")"
fi
echo "$tmux_line_number"
}
_get_match() {
local string="$1"
local full_match
if _string_starts_with_digit "$string"; then
full_match="$(echo "$string" | cut -f2- -d:)"
else
# This scenario handles OS X grep bug "no number in the results line".
# When there's no number at the beginning of the line, we're taking the
# whole line as a match. This handles the result line like this:
# `http://www.example.com` (the `http` would otherwise get cut off)
full_match="$string"
fi
echo -n "$full_match"
}
_escape_backslash() {
local string="$1"
echo "$(echo "$string" | sed 's/\\/\\\\/g')"
}
_get_match_line_position() {
local file="$1"
local line_number="$2"
local match="$3"
local adjusted_line_num=$((line_number + 1))
local result_line=$(tail -"$adjusted_line_num" "$file" | head -1)
# OS X awk cannot have `=` as the first char in the variable (bug in awk).
# If exists, changing the `=` character with `.` to avoid error.
local platform="$(uname)"
if [ "$platform" == "Darwin" ]; then
result_line="$(echo "$result_line" | sed 's/^=/./')"
match="$(echo "$match" | sed 's/^=/./')"
fi
# awk treats \r, \n, \t etc as single characters and that messes up match
# highlighting. For that reason, we're escaping backslashes so above chars
# are treated literally.
result_line="$(_escape_backslash "$result_line")"
match="$(_escape_backslash "$match")"
local index=$($AWK_CMD -v a="$result_line" -v b="$match" 'BEGIN{print index(a,b)}')
local zero_index=$((index - 1))
echo "$zero_index"
}
_copycat_jump() {
local line_number="$1"
local match_line_position="$2"
local match="$3"
local scrollback_line_number="$4"
_copycat_enter_mode
_copycat_exit_select_mode
_copycat_jump_to_line "$line_number" "$scrollback_line_number"
_copycat_position_to_match_start "$match_line_position"
_copycat_select "$match"
}
_copycat_enter_mode() {
tmux copy-mode
}
# clears selection from a previous match
_copycat_exit_select_mode() {
tmux send-keys -X clear-selection
}
# "manually" go up in the scrollback for a number of lines
_copycat_manually_go_up() {
local line_number="$1"
tmux send-keys -X -N "$line_number" cursor-up
tmux send-keys -X start-of-line
}
_copycat_create_padding_below_result() {
local number_of_lines="$1"
local maximum_padding="$2"
local padding
# Padding should not be greater than half pane height
# (it wouldn't be centered then).
if [ "$number_of_lines" -gt "$maximum_padding" ]; then
padding="$maximum_padding"
else
padding="$number_of_lines"
fi
# cannot create padding, exit function
if [ "$padding" -eq "0" ]; then
return
fi
tmux send-keys -X -N "$padding" cursor-down
tmux send-keys -X -N "$padding" cursor-up
}
# performs a jump to go to line
_copycat_go_to_line_with_jump() {
local line_number="$1"
# first jumps to the "bottom" in copy mode so that jumps are consistent
tmux send-keys -X history-bottom
tmux send-keys -X start-of-line
tmux send-keys -X goto-line $line_number
}
# maximum line number that can be reached via tmux 'jump'
_get_max_jump() {
local scrollback_line_number="$1"
local window_height="$2"
local max_jump=$((scrollback_line_number - $window_height))
# max jump can't be lower than zero
if [ "$max_jump" -lt "0" ]; then
max_jump="0"
fi
echo "$max_jump"
}
_copycat_jump_to_line() {
local line_number="$1"
local scrollback_line_number="$2"
local window_height="$(tmux display-message -p '#{pane_height}')"
local correct_line_number
local max_jump=$(_get_max_jump "$scrollback_line_number" "$window_height")
local correction="0"
if [ "$line_number" -gt "$max_jump" ]; then
# We need to 'reach' a line number that is not accessible via 'jump'.
# Introducing 'correction'
correct_line_number="$max_jump"
correction=$((line_number - $correct_line_number))
else
# we can reach the desired line number via 'jump'. Correction not needed.
correct_line_number="$line_number"
fi
_copycat_go_to_line_with_jump "$correct_line_number"
if [ "$correction" -gt "0" ]; then
_copycat_manually_go_up "$correction"
fi
# If no corrections (meaning result is not at the top of scrollback)
# we can then 'center' the result within a pane.
if [ "$correction" -eq "0" ]; then
local half_window_height="$((window_height / 2))"
# creating as much padding as possible, up to half pane height
_copycat_create_padding_below_result "$line_number" "$half_window_height"
fi
}
_copycat_position_to_match_start() {
local match_line_position="$1"
[ "$match_line_position" -eq "0" ] && return 0
tmux send-keys -X -N "$match_line_position" cursor-right
}
_copycat_select() {
local match="$1"
local length="${#match}"
tmux send-keys -X begin-selection
tmux send-keys -X -N "$length" cursor-right
if [ "$TMUX_COPY_MODE" == "vi" ]; then
tmux send-keys -X cursor-left # selection correction for 1 char
fi
}
# all functions above are "private", called from `do_next_jump` function
get_new_position_number() {
local copycat_file="$1"
local current_position="$2"
local new_position
# doing a forward/up jump
if [ "$NEXT_PREV" == "next" ]; then
local number_of_results=$(wc -l "$copycat_file" | $AWK_CMD '{ print $1 }')
if [ "$current_position" -eq "$number_of_results" ]; then
# position can't go beyond the last result
new_position="$current_position"
else
new_position="$((current_position + 1))"
fi
# doing a backward/down jump
elif [ "$NEXT_PREV" == "prev" ]; then
if [ "$current_position" -eq "1" ]; then
# position can't go below 1
new_position="1"
else
new_position="$((current_position - 1))"
fi
fi
echo "$new_position"
}
do_next_jump() {
local position_number="$1"
local copycat_file="$2"
local scrollback_file="$3"
local scrollback_line_number=$(_file_number_of_lines "$scrollback_file")
local result_line="$(_get_result_line "$copycat_file" "$position_number")"
local line_number=$(_get_line_number "$result_line" "$copycat_file" "$position_number")
local match=$(_get_match "$result_line")
local match_line_position=$(_get_match_line_position "$scrollback_file" "$line_number" "$match")
_copycat_jump "$line_number" "$match_line_position" "$match" "$scrollback_line_number"
}
notify_about_first_last_match() {
local current_position="$1"
local next_position="$2"
local message_duration="1500"
# if position didn't change, we are either on a 'first' or 'last' match
if [ "$current_position" -eq "$next_position" ]; then
if [ "$NEXT_PREV" == "next" ]; then
display_message "Last match!" "$message_duration"
elif [ "$NEXT_PREV" == "prev" ]; then
display_message "First match!" "$message_duration"
fi
fi
}
main() {
if in_copycat_mode; then
local copycat_file="$(get_copycat_filename)"
local scrollback_file="$(get_scrollback_filename)"
local current_position="$(get_copycat_position)"
local next_position="$(get_new_position_number "$copycat_file" "$current_position")"
do_next_jump "$next_position" "$copycat_file" "$scrollback_file"
notify_about_first_last_match "$current_position" "$next_position"
set_copycat_position "$next_position"
fi
}
main

View File

@ -0,0 +1,55 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
AWK_CMD='awk'
if command_exists gawk; then
AWK_CMD='gawk'
fi
# Extends a keyboard key.
# Benefits: tmux won't report errors and everything will work fine even if the
# script is deleted.
extend_key() {
local key="$1"
local script="$2"
local cmd
# 1. 'cmd' or 'key' is sent to tmux. This ensures the default key action is done.
# 2. Script is executed.
# 3. `true` command ensures an exit status 0 is returned. This ensures a
# user never gets an error msg - even if the script file from step 2 is
# deleted.
# We fetch the current behavior of the 'key' mapping in
# variable 'cmd'
cmd=$(tmux list-keys -T $(tmux_copy_mode_string) | $AWK_CMD '$4 == "'$key'"' | $AWK_CMD '{ $1=""; $2=""; $3=""; $4=""; sub(" ", " "); print }')
# If 'cmd' is already a copycat command, we do nothing
if echo "$cmd" | grep -q copycat; then
return
fi
# We save the previous mapping to a file in order to be able to recover
# the previous mapping when we unbind
tmux list-keys -T $(tmux_copy_mode_string) | $AWK_CMD '$4 == "'$key'"' >> "${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys"
tmux bind-key -T $(tmux_copy_mode_string) "$key" run-shell "tmux $cmd; $script; true"
}
copycat_cancel_bindings() {
# keys that quit copy mode are enhanced to quit copycat mode as well.
local cancel_mode_bindings=$(copycat_quit_copy_mode_keys)
local key
for key in $cancel_mode_bindings; do
extend_key "$key" "$CURRENT_DIR/copycat_mode_quit.sh"
done
}
copycat_mode_bindings() {
extend_key "$(copycat_next_key)" "$CURRENT_DIR/copycat_jump.sh 'next'"
extend_key "$(copycat_prev_key)" "$CURRENT_DIR/copycat_jump.sh 'prev'"
}
main() {
copycat_mode_bindings
copycat_cancel_bindings
}
main

View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
unbind_cancel_bindings() {
local cancel_mode_bindings=$(copycat_quit_copy_mode_keys)
local key
for key in $cancel_mode_bindings; do
tmux unbind-key -n "$key"
done
}
unbind_prev_next_bindings() {
tmux unbind-key -n "$(copycat_next_key)"
tmux unbind-key -n "$(copycat_prev_key)"
}
unbind_all_bindings() {
grep -v copycat <"${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys" | while read -r key_cmd; do
sh -c "tmux $key_cmd"
done < /dev/stdin
rm "${TMPDIR:-/tmp}/copycat_$(whoami)_recover_keys"
}
main() {
if in_copycat_mode; then
reset_copycat_position
unset_copycat_mode
copycat_decrease_counter
# removing all bindings only if no panes are in copycat mode
if copycat_counter_zero; then
unbind_all_bindings
fi
fi
}
main

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SUPPORTED_VERSION="1.9"
PATTERN="$1"
supported_tmux_version_ok() {
$CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_VERSION"
}
main() {
local pattern="$1"
if supported_tmux_version_ok; then
$CURRENT_DIR/copycat_generate_results.sh "$pattern" # will `exit 0` if no results
$CURRENT_DIR/copycat_mode_bindings.sh
$CURRENT_DIR/copycat_jump.sh 'next'
fi
}
main "$PATTERN"

View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
main() {
tmux command-prompt -p "copycat search:" "run-shell \"$CURRENT_DIR/copycat_mode_start.sh '%1'\""
}
main

View File

@ -0,0 +1,192 @@
# config options
default_next_key="n"
tmux_option_next="@copycat_next"
default_prev_key="N"
tmux_option_prev="@copycat_prev"
# keeps track of number of panes in copycat mode
tmux_option_counter="@copycat_counter"
# === awk vs gawk ===
command_exists() {
command -v "$@" > /dev/null 2>&1
}
AWK_CMD='awk'
if command_exists gawk; then
AWK_CMD='gawk'
fi
# === general helpers ===
get_tmux_option() {
local option=$1
local default_value=$2
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
set_tmux_option() {
local option=$1
local value=$2
tmux set-option -gq "$option" "$value"
}
tmux_copy_mode() {
tmux show-option -gwv mode-keys
}
tmux_copy_mode_string() {
if [ $(tmux_copy_mode) == 'vi' ]; then
echo copy-mode-vi
else
echo copy-mode
fi
}
# === copycat mode specific helpers ===
set_copycat_mode() {
set_tmux_option "$(_copycat_mode_var)" "true"
}
unset_copycat_mode() {
set_tmux_option "$(_copycat_mode_var)" "false"
}
in_copycat_mode() {
local copycat_mode=$(get_tmux_option "$(_copycat_mode_var)" "false")
[ "$copycat_mode" == "true" ]
}
not_in_copycat_mode() {
if in_copycat_mode; then
return 1
else
return 0
fi
}
# === copycat mode position ===
get_copycat_position() {
local copycat_position_variable=$(_copycat_position_var)
echo $(get_tmux_option "$copycat_position_variable" "0")
}
set_copycat_position() {
local position="$1"
local copycat_position_variable=$(_copycat_position_var)
set_tmux_option "$copycat_position_variable" "$position"
}
reset_copycat_position() {
set_copycat_position "0"
}
# === scrollback and results position ===
get_scrollback_filename() {
echo "$(_get_tmp_dir)/scrollback-$(_pane_unique_id)"
}
get_copycat_filename() {
echo "$(_get_tmp_dir)/results-$(_pane_unique_id)"
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
# === counter functions ===
copycat_increase_counter() {
local count=$(get_tmux_option "$tmux_option_counter" "0")
local new_count="$((count + 1))"
set_tmux_option "$tmux_option_counter" "$new_count"
}
copycat_decrease_counter() {
local count="$(get_tmux_option "$tmux_option_counter" "0")"
if [ "$count" -gt "0" ]; then
# decreasing the counter only if it won't go below 0
local new_count="$((count - 1))"
set_tmux_option "$tmux_option_counter" "$new_count"
fi
}
copycat_counter_zero() {
local count="$(get_tmux_option "$tmux_option_counter" "0")"
[ "$count" -eq "0" ]
}
# === key binding functions ===
copycat_next_key() {
echo "$(get_tmux_option "$tmux_option_next" "$default_next_key")"
}
copycat_prev_key() {
echo "$(get_tmux_option "$tmux_option_prev" "$default_prev_key")"
}
# function expected output: 'C-c Enter q'
copycat_quit_copy_mode_keys() {
local commands_that_quit_copy_mode="cancel"
local copy_mode="$(tmux_copy_mode_string)"
tmux list-keys -T "$copy_mode" |
\grep "$commands_that_quit_copy_mode" |
$AWK_CMD '{ print $4 }' |
sort -u |
sed 's/C-j//g' |
xargs echo
}
# === 'private' functions ===
_copycat_mode_var() {
local pane_id="$(_pane_unique_id)"
echo "@copycat_mode_$pane_id"
}
_copycat_position_var() {
local pane_id="$(_pane_unique_id)"
echo "@copycat_position_$pane_id"
}
_get_tmp_dir() {
echo "${TMPDIR:-/tmp}/tmux-$EUID-copycat"
}
# returns a string unique to current pane
# sed removes `$` sign because `session_id` contains is
_pane_unique_id() {
tmux display-message -p "#{session_id}-#{window_index}-#{pane_index}" |
sed 's/\$//'
}

View File

@ -0,0 +1,23 @@
stored_search_not_defined() {
local key="$1"
local search_value="$(tmux show-option -gqv "${COPYCAT_VAR_PREFIX}_${key}")"
[ -z $search_value ]
}
stored_search_vars() {
tmux show-options -g |
\grep -i "^${COPYCAT_VAR_PREFIX}_" |
cut -d ' ' -f1 | # cut just variable names
xargs # splat var names in one line
}
# get the search key from the variable name
get_stored_search_key() {
local search_var="$1"
echo "$(echo "$search_var" | sed "s/^${COPYCAT_VAR_PREFIX}_//")"
}
get_stored_search_pattern() {
local search_var="$1"
echo "$(get_tmux_option "$search_var" "")"
}

View File

@ -0,0 +1,26 @@
# stored search variable prefix
COPYCAT_VAR_PREFIX="@copycat_search"
# basic search
default_copycat_search_key="/"
copycat_search_option="@copycat_search"
# git special search
default_git_search_key="C-g"
copycat_git_search_option="@copycat_git_special"
# regular searches
default_file_search_key="C-f"
copycat_file_search_option="@copycat_file_search"
default_url_search_key="C-u"
copycat_url_search_option="@copycat_url_search"
default_digit_search_key="C-d"
copycat_digit_search_option="@copycat_digit_search"
default_hash_search_key="M-h"
copycat_hash_search_option="@copycat_hash_search"
default_ip_search_key="M-i"
copycat_ip_search_option="@copycat_ip_search"

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,92 @@
# Tmux online status
Tmux plugin that enables displaying online status for your workstation.
Introduces a new `#{online_status}` format.
This plugin is useful if:
- you spend most of your time in Tmux and don't want to "switch" away from
the terminal to check whether you're connected.
- you have a flaky internet connection and you don't want to be surprised
when a simple `curl` or `wget` fails because the connection just broke.
Tested and working on Linux, OSX, FreeBSD, and Cygwin.
### Usage
Add `#{online_status}` format string to your existing `status-right` tmux
option.
Here's the example in `.tmux.conf`:
set -g status-right "Online: #{online_status} | %a %h-%d %H:%M "
**OS X**<br/>
On OS X the above will look like this when online<br/>
![online indicator](/screenshots/online_indicator.png)<br/>
or this when offline<br/>
![offline indicator](/screenshots/offline_indicator.png)<br/>
**Linux**<br/>
Online status on Linux<br/>
![online indicator](/screenshots/online_indicator_linux.png)<br/>
offline status<br/>
![offline indicator](/screenshots/offline_indicator_linux.png)<br/>
#### Configure icons
If the icons don't display well on your machine you can change them in
`.tmux.conf`:
set -g @online_icon "ok"
set -g @offline_icon "offline!"
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-online-status'
Hit `prefix + I` to fetch the plugin and source it.
`#{online_status}` interpolation should now work.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-online-status ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/online_status.tmux
Reload TMUX environment:
# type this in terminal
$ tmux source-file ~/.tmux.conf
`#{online_status}` interpolation should now work.
### Limitations
Online status icon most likely won't be instant. The duration depends on the
`status-interval` Tmux option. So, it might take anywhere between 5 and 60
seconds for online status icon to change.
Set `status-interval` to a low number to make this faster, example:
# in .tmux.conf
set -g status-interval 5
### Other plugins
You might also find these useful:
- [battery](https://github.com/tmux-plugins/tmux-battery) - battery status in
Tmux `status-right`
- [logging](https://github.com/tmux-plugins/tmux-logging) - easy logging and
screen capturing
### License
[MIT](LICENSE.md)

View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
online_status_icon="#($CURRENT_DIR/scripts/online_status_icon.sh)"
online_status_interpolation_string="\#{online_status}"
source $CURRENT_DIR/scripts/shared.sh
do_interpolation() {
local string="$1"
local interpolated="${string/$online_status_interpolation_string/$online_status_icon}"
echo "$interpolated"
}
update_tmux_option() {
local option="$1"
local option_value="$(get_tmux_option "$option")"
local new_option_value="$(do_interpolation "$option_value")"
set_tmux_option "$option" "$new_option_value"
}
main() {
update_tmux_option "status-right"
update_tmux_option "status-left"
}
main

View File

@ -0,0 +1,77 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
online_option_string="@online_icon"
offline_option_string="@offline_icon"
ping_timeout_string="@ping_timeout"
route_to_ping_string="@route_to_ping"
online_icon_osx="✅ "
online_icon="✔"
offline_icon_osx="⛔️ "
offline_icon_cygwin="X"
offline_icon="❌ "
ping_timeout_default="3"
route_to_ping_default="www.google.com"
source $CURRENT_DIR/shared.sh
is_osx() {
[ $(uname) == "Darwin" ]
}
is_cygwin() {
[[ $(uname) =~ CYGWIN ]]
}
is_freebsd() {
[ $(uname) == FreeBSD ]
}
online_icon_default() {
if is_osx; then
echo "$online_icon_osx"
else
echo "$online_icon"
fi
}
offline_icon_default() {
if is_osx; then
echo "$offline_icon_osx"
elif is_cygwin; then
echo "$offline_icon_cygwin"
else
echo "$offline_icon"
fi
}
online_status() {
if is_osx || is_freebsd; then
local timeout_flag="-t"
else
local timeout_flag="-w"
fi
if is_cygwin; then
local number_pings_flag="-n"
else
local number_pings_flag="-c"
fi
local ping_timeout="$(get_tmux_option "$ping_timeout_string" "$ping_timeout_default")"
local ping_route="$(get_tmux_option "$route_to_ping_string" "$route_to_ping_default")"
ping "$number_pings_flag" 1 "$timeout_flag" "$ping_timeout" "$ping_route" >/dev/null 2>&1
}
print_icon() {
if $(online_status); then
printf "$(get_tmux_option "$online_option_string" "$(online_icon_default)")"
else
printf "$(get_tmux_option "$offline_option_string" "$(offline_icon_default)")"
fi
}
main() {
print_icon
}
main

View File

@ -0,0 +1,16 @@
get_tmux_option() {
local option=$1
local default_value=$2
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
set_tmux_option() {
local option="$1"
local value="$2"
tmux set-option -gq "$option" "$value"
}

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,95 @@
# Tmux open
Plugin for opening highlighted selection directly from Tmux copy mode.
Tested and working on Linux, OSX and Cygwin.
### Key bindings
In tmux copy mode:
- `o` - "open" a highlighted selection with the system default program. `open`
for OS X or `xdg-open` for Linux.
- `Ctrl-o` - open a highlighted selection with the `$EDITOR`
- `Shift-s` - search the highlighted selection directly inside a search engine (defaults to google).
### Examples
In copy mode:
- highlight `file.pdf` and press `o` - file will open in the default PDF viewer.
- highlight `file.doc` and press `o` - file will open in system default `.doc`
file viewer.
- highlight `http://example.com` and press `o` - link will be opened in the
default browser.
- highlight `file.txt` and press `Ctrl-o` - file will open in `$EDITOR`.
- highlight `TypeError: 'undefined' is not a function` and press `Shift-s` - the text snipped will be searched directly inside google by default
### Screencast
[![screencast screenshot](/video/screencast_img.png)](http://vimeo.com/102455265)
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-open'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-open ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/open.tmux
Reload TMUX environment:
# type this in terminal
$ tmux source-file ~/.tmux.conf
You should now be able to use the plugin.
### Configuration
> How can I change the default "o" key binding to something else? For example,
> key "x"?
Put `set -g @open 'x'` in `tmux.conf`.
> How can I change the default "Ctrl-o" key binding to "Ctrl-x"?
Put `set -g @open-editor 'C-x'` in `tmux.conf`.
> How can I change the default search engine to "duckduckgo" or any other one?
Put `set -g @open-S 'https://www.duckduckgo.com/'` in `tmux.conf`
> How can I use multiple search engines?
Put:
```
set -g @open-B 'https://www.bing.com/search?q='
set -g @open-S 'https://www.google.com/search?q='
```
in `tmux.conf`
### Other goodies
`tmux-open` works great with:
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
highlighted text to system clipboard
### License
[MIT](LICENSE.md)

142
tmux/plugins/tmux-open/open.tmux Executable file
View File

@ -0,0 +1,142 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/helpers.sh"
default_open_key="o"
open_option="@open"
default_open_editor_key="C-o"
open_editor_option="@open-editor"
open_editor_override="@open-editor-command"
command_exists() {
local command="$1"
type "$command" >/dev/null 2>&1
}
is_osx() {
local platform=$(uname)
[ "$platform" == "Darwin" ]
}
is_cygwin() {
[[ "$(uname)" =~ CYGWIN ]]
}
get_editor_from_the_env_var() {
if [ -z $EDITOR ]; then
# $EDITOR not set, fallback
echo "vi"
else
echo "$EDITOR"
fi
}
command_generator() {
local command_string="$1"
echo "xargs -I {} tmux run-shell -b 'cd #{pane_current_path}; $command_string \"{}\" > /dev/null'"
}
search_command_generator() {
local command_string="$1"
local engine="$2"
echo "xargs -I {} tmux run-shell -b 'cd #{pane_current_path}; $command_string $engine\"{}\" > /dev/null'"
}
generate_open_command() {
if is_osx; then
echo "$(command_generator "open")"
elif is_cygwin; then
echo "$(command_generator "cygstart")"
elif command_exists "xdg-open"; then
echo "$(command_generator "xdg-open")"
else
# error command for Linux machines when 'xdg-open' not installed
"$CURRENT_DIR/scripts/tmux_open_error_message.sh" "xdg-open"
fi
}
generate_open_search_command() {
local engine="$1"
if is_osx; then
echo "$(search_command_generator "open" "$engine")"
elif is_cygwin; then
echo "$(command_generator "cygstart")"
elif command_exists "xdg-open"; then
echo "$(search_command_generator "xdg-open" "$engine")"
else
# error command for Linux machines when 'xdg-open' not installed
"$CURRENT_DIR/scripts/tmux_open_error_message.sh" "xdg-open"
fi
}
# 1. write a command to the terminal, example: 'vim -- some_file.txt'
# 2. invoke the command by pressing enter/C-m
generate_editor_command() {
local environment_editor=$(get_editor_from_the_env_var)
local editor=$(get_tmux_option "$open_editor_override" "$environment_editor")
# vim freezes terminal unless there's the '--' argument. Other editors seem
# to be fine with it (textmate [mate], light table [table]).
echo "xargs -I {} tmux send-keys '$editor -- \"{}\"'; tmux send-keys 'C-m'"
}
set_copy_mode_open_bindings() {
local open_command="$(generate_open_command)"
local key_bindings=$(get_tmux_option "$open_option" "$default_open_key")
local key
for key in $key_bindings; do
if tmux-is-at-least 2.4; then
tmux bind-key -T copy-mode-vi "$key" send-keys -X copy-pipe-and-cancel "$open_command"
tmux bind-key -T copy-mode "$key" send-keys -X copy-pipe-and-cancel "$open_command"
else
tmux bind-key -t vi-copy "$key" copy-pipe "$open_command"
tmux bind-key -t emacs-copy "$key" copy-pipe "$open_command"
fi
done
}
set_copy_mode_open_editor_bindings() {
local editor_command="$(generate_editor_command)"
local key_bindings=$(get_tmux_option "$open_editor_option" "$default_open_editor_key")
local key
for key in $key_bindings; do
if tmux-is-at-least 2.4; then
tmux bind-key -T copy-mode-vi "$key" send-keys -X copy-pipe-and-cancel "$editor_command"
tmux bind-key -T copy-mode "$key" send-keys -X copy-pipe-and-cancel "$editor_command"
else
tmux bind-key -t vi-copy "$key" copy-pipe "$editor_command"
tmux bind-key -t emacs-copy "$key" copy-pipe "$editor_command"
fi
done
}
set_copy_mode_open_search_bindings() {
local stored_engine_vars="$(stored_engine_vars)"
local engine_var
local engine
local key
for engine_var in $stored_engine_vars; do
engine="$(get_engine "$engine_var")"
if tmux-is-at-least 2.4; then
tmux bind-key -T copy-mode-vi "$engine_var" send-keys -X copy-pipe-and-cancel "$(generate_open_search_command "$engine")"
tmux bind-key -T copy-mode "$engine_var" send-keys -X copy-pipe-and-cancel "$(generate_open_search_command "$engine")"
else
tmux bind-key -t vi-copy "$engine_var" copy-pipe "$(generate_open_search_command "$engine")"
tmux bind-key -t emacs-copy "$engine_var" copy-pipe "$(generate_open_search_command "$engine")"
fi
done
}
main() {
set_copy_mode_open_bindings
set_copy_mode_open_editor_bindings
set_copy_mode_open_search_bindings
}
main

View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2017 Alexey Samoshkin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,372 @@
Tmux sysstat plugin
===================
Allow to print CPU usage, memory & swap, load average, net I/O metrics in Tmux status bar
![intro](/screenshots/intro.png)
You might checkout [tmux-config](https://github.com/samoshkin/tmux-config) repo to see this plugin in action.
Features
--------
- CPU usage
- Memory available/free, used, total (KiB,MiB,GiB), free/used %
- Swap used, free, total, free/used %
- load average for last 1,5,15 minutes
- configurable thresholds (low, medium, stress) with custom colors
- tweak each metric output using templates (e.g, 'used 10% out of 16G')
- configurable size scale (K,M,G)
- OSX, Linux support
- [ ] **TODO:** network I/O metric support
Tested on: OS X El Capitan 10.11.5, Ubuntu 14 LTS, CentOS 7, FreeBSD 11.1.
Installation
------------
Best installed through [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (TMP). Add following line to your `.tmux.conf` file:
```
set -g @plugin 'samoshkin/tmux-plugin-sysstat'
```
Use `prefix + I` from inside tmux to install all plugins and source them. If you prefer, same effect can be achieved from [command line](https://github.com/tmux-plugins/tpm/blob/master/docs/managing_plugins_via_cmd_line.md):
```
$ ~.tmux/plugins/tpm/bin/install_plugins
```
Basic usage
-----------
Once plugged in, tmux `status-left` or `status-right` options can be configured with following placeholders. Each placeholder will be expanded to metric's default output.
- `#{sysstat_cpu}`, CPU usage - `CPU:40.2%`
- `#{sysstat_mem}`, memory usage - `MEM:73%`
- `#{sysstat_swap}`, swap usage - `SW:66%`
- `#{sysstat_loadavg}`, system load average - `0.25 0.04 0.34`
For example:
```
set -g status-right "#{sysstat_cpu} | #{sysstat_mem} | #{sysstat_swap} | #{sysstat_loadavg} | #[fg=cyan]#(echo $USER)#[default]@#H"
```
Changing default output
------------------------
You can change default output for CPU and memory metrics, if you need more fields to show, or you want to provide custom template. In your `.tmux.conf`:
For example, to get `Used 4.5G out of 16G` output for memory metric:
```
set -g @sysstat_mem_view_tmpl '#Used [fg=#{mem.color}]#{mem.used}#[default] out of #{mem.total}'
```
If you don't want `CPU:` prefix and don't like colored output for CPU metric:
```
set -g @sysstat_cpu_view_tmpl '#{cpu.pused}'
```
### Supported fields
As you can see, each metric can be configured with template, containing fixed text (`CPU:`), color placeholder (`#[fg=#{mem.color}]`) and field placeholder (`#{mem.used}`). This approach gives you the ultimate control over the output for each metric. Following field placeholders are supported:
<table>
<th>CPU</th>
<tr>
<td><code>#{cpu.color}</code></td>
<td>main metric color</td>
</tr>
<tr>
<td><code>#{cpu.pused}</code></td>
<td>CPU usage percentage</td>
</tr>
</table>
<table>
<th>Memory</th>
<tr>
<td><code>#{mem.color}</code></td>
<td>main metric color</td>
</tr>
<tr>
<td><code>#{mem.free}</code></td>
<td>free/available memory</td>
</tr>
<tr>
<td><code>#{mem.pfree}</code></td>
<td>free memory percentage against total</td>
</tr>
<tr>
<td><code>#{mem.used}</code></td>
<td>used memory</td>
</tr>
<tr>
<td><code>#{mem.pused}</code></td>
<td>used memory percentage against total</td>
</tr>
<tr>
<td><code>#{mem.total}</code></td>
<td>total installed memory</td>
</tr>
</table>
<table>
<th>Swap</th>
<tr>
<td><code>#{swap.color}</code></td>
<td>main swap metric color</td>
</tr>
<tr>
<td><code>#{swap.free}</code></td>
<td>free swap memory</td>
</tr>
<tr>
<td><code>#{swap.pfree}</code></td>
<td>free swap memory percentage against total swap space</td>
</tr>
<tr>
<td><code>#{swap.used}</code></td>
<td>used swap memory</td>
</tr>
<tr>
<td><code>#{swap.pused}</code></td>
<td>used swap memory percentage against total swap space</td>
</tr>
<tr>
<td><code>#{swap.total}</code></td>
<td>total swap space</td>
</tr>
</table>
### Change size scale
free/used/total memory can be shown both in absolute and relative units. When it comes to absolute units, you can choose *size scale factor* to choose between GiB, MiB, KiB. Default is GiB. If you have less than 3-4G memory installed, it makes sense to use MiB. KiB option is less practical, because it yields pretty lengthy output, which does not fit status bar limited estate.
```
set -g @sysstat_mem_size_unit "G"
```
If you choose `G` for size scale, output will have `%.1f` (1 digit after floating point), otherwise size is integer (4.5G, 1024M, 1232345K).
Thresholds and colored output
---------------
Each metric output is colored by default. Colors vary depending on metric value.
<table>
<tr>
<td><b>Threshold</b></td>
<td><b>CPU</b></td>
<td><b>Memory</b></td>
<td><b>Swap</b></td>
<td><b>Default color</b></td>
</tr>
<tr>
<td>low</td>
<td>x &lt; 30%</td>
<td>x &lt; 75%</td>
<td>x &lt; 25%</td>
<td>green</td>
</tr>
<tr>
<td>medium</td>
<td>30% &lt; x &lt; 80%</td>
<td>75% &lt; x &lt; 90%</td>
<td>25% &lt; x &lt; 75%</td>
<td>yellow</td>
</tr>
<tr>
<td>high</td>
<td>x &gt; 80%</td>
<td>x &gt; 90%</td>
<td>x &gt; 75%</td>
<td>red</td>
</tr>
</table>
You can change thresholds in your `.tmux.conf`:
```
set -g @sysstat_cpu_medium_threshold "75"
set -g @sysstat_cpu_stress_threshold "95"
set -g @sysstat_mem_medium_threshold "85"
set -g @sysstat_mem_stress_threshold "95"
set -g @sysstat_swap_medium_threshold "80"
set -g @sysstat_swap_stress_threshold "90"
```
You can change colors for each threshold individually. You can use ANSI basic colors (red, cyan, green) or if your terminal supports 256 colors (and most do nowadays), use `colourXXX` format.
```
set -g @sysstat_cpu_color_low "colour076"
set -g @sysstat_cpu_color_medium "colour220"
set -g @sysstat_cpu_color_stress "colour160"
set -g @sysstat_mem_color_low "green"
set -g @sysstat_mem_color_medium "blue"
set -g @sysstat_mem_color_stress "cyan"
```
`#{(mem|cpu|swap).color}` placeholder in your `@sysstat_(mem|cpu|swap)_view_tmpl` would be replaced by corresponding color, depending on whether metric value falls in particular threshold.
### 256 color palette support
For 256 color palette support, make sure that `tmux` and parent terminal are configured with correct terminal type. See [here](https://unix.stackexchange.com/questions/1045/getting-256-colors-to-work-in-tmux) and [there](https://github.com/tmux/tmux/wiki/FAQ)
```
# ~/.tmux.conf
set -g default-terminal "screen-256color"
```
```
# parent terminal
$ echo $TERM
xterm-256color
# jump into a tmux session
$ tmux new
$ echo $TERM
screen-256color
```
### Multiple colors for each threshold
You can have up to *3* colors configured for each threshold. To understand why you might need this, let tackle this task. Note, this is rather advanced use case.
> I want `CPU: #{cpu.pused}` metric output, have green and yellow text colors at "low" and "medium" threshold, and finally, for "high" threshold, I want to use red color, but reverse foreground and background, that is use red for background, and white for text. More over I want "CPU:" text colored apart in red
Like this:
![cpu threshold with custom colors](/screenshots/cpu_thresholds.png)
You can achieve the result using following configuration:
```
set -g @sysstat_cpu_view_tmpl '#[fg=#{cpu.color3}]CPU:#[default] #[fg=#{cpu.color},bg=#{cpu.color2}]#{cpu.pused}#[default]'
set -g @sysstat_cpu_color_low "$color_level_ok default default"
set -g @sysstat_cpu_color_medium "$color_level_warn default default"
set -g @sysstat_cpu_color_stress "white,bold $color_level_stress $color_level_stress"
```
Tmux status-interval setting
-----------------------------
You can configure status refresh interval, increasing or reducing frequency of `tmux-plugin-sysstat` command invocations.
```
set -g status-interval 5
```
It's adviced to set `status-interval` to some reasonable value, like 5-10 seconds. More frequent updates (1 second) are useless, because they distract, and results in extra resource stress spent on metrics calculation itself.
Internals: CPU calculation
--------------------------------------------------
<span style="color: blue">**NOTE:** Stop here if you want to just use this plugin without making your feet wet. If you're hardcore tmux user and are curious about internals, keep reading</span>
Internally, we use `iostat` and `top` on OSX, and `vmstat` and `top` on Linux to collect metric value. Neither requires you to install extra packages. These commands are run in sampling mode to report stats every N seconds M times. First sample include average values since the system start. Second one is the average CPU per second for last N seconds (exactly what we need)
For example:
```
$ iostat -c 2 -w 5
disk0 cpu load average
KB/t tps MB/s us sy id 1m 5m 15m
44.22 6 0.26 3 2 95 1.74 1.90 2.15
5.47 8 0.04 4 5 91 1.84 1.92 2.16 << use this row, 2nd sample
```
We align CPU calculation intervals (`-w`) with tmux status bar refresh interval (`status-interval` setting).
Internals: memory calculation
----------------------------
You might ask what we treat as `free` memory and how it's calculated.
### OSX
Let's start with OSX. We use `vm_stat` command (not same as `vmstat` on Linux), which reports following data (number of memory pages, not KB):
```
$ vm_stat
Pages free: 37279
Pages active: 1514200
Pages inactive: 1152997
Pages speculative: 6214
Pages throttled: 0
Pages wired down: 1174408
Pages purgeable: 15405
Pages stored in compressor: 1615663
Pages occupied by compressor: 306717
```
Total installed memory formula is:
```
Total = free + active + inactive + speculative + occupied by compressor + wired
```
where
- `free`, completely unused memory by the system
- `wired`, critical information stored in RAM by system, kernel and key applications. Never swapped to the hard drive, never replaced with user-level data.
- `active`, information currently in use or very recently used by applications. When this kind of memory is not used for long (or application is closed), it's move to inactive memory.
- `inactive`, like buffers/cached memory in Linux. Memory for applications, which recently exited, retained for faster start-up of same application in future.
So the question what constitutes `free` and `used` memory. It turns out, that various monitoring and system statistics tools on OSX each calculate it differently.
- htop: `used = active + wired`, `free` = `total - used`
- top. Used = `used = active + inactive + occupied by compressor + wired`; Free = `free + speculative` Resident set size (RSS) = `active`
- OSX activity Monitor. Used = `app memory + wired + compressor`. Note, it's not clear what is app memory.
In general, they either treat currently used memory, which can be reclaimed in case of need (cached, inactive, occupied by compressor), as `used` or `free`.
It makes sense to talk about `available` memory rather than `free` one. Available memory is unused memory + any used memory which can be reclaimed for application needs.
So, `tmux-plugin-sysstat`, uses following formula:
```
used = active + wired
available/free = free/unused + inactive + speculative + occupied by compressor
```
### Linux
Same thinking can be applied to Linux systems.
Usually commands like `free` report free/unused, used, buffers, cache memory kinds.
```
$ free
total used free shared buffers cached
Mem: 1016464 900236 116228 21048 93448 241544
-/+ buffers/cache: 565244 451220
Swap: 1046524 141712 904812
```
Second line indicates available memory (free + buffers + cache), with an assumption that buffers and cache can be 100% reclaimed in case of need.
However, we're not using free, because its output varies per system. For example on RHEL7, there is no `-/+ buffers/cache`, and `available` memory is reported in different way. We read directly from `/proc/meminfo`
```
$ cat /proc/meminfo
MemTotal: 1016232 kB
MemFree: 152672 kB
MemAvailable: 637832 kB
Buffers: 0 kB
Cached: 529040 kB
```
`tmux-plugin-sysstat` uses following formula:
```
free/available = MemAvailable; // if MemAvailable present
free/available = MemFree + Buffers + Cached;
used = MemTotal - free/avaialble
```
Using `MemAvailable` is more accurate way of getting available memory, rather than manual calculation `free + buffers + cache`, because the assumption that `buffers + cache` can be 100% reclaimed for new application needs might be wrong. When using `MemAvailable`, OS calculates available memory for you, which is apparently better and accurate approach.
See [this topic](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773) on more reasoning about `MemAvailable` field.

View File

@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -u
set -e
LC_NUMERIC=C
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
cpu_tmp_dir=$(tmux show-option -gqv "@sysstat_cpu_tmp_dir")
cpu_view_tmpl=$(get_tmux_option "@sysstat_cpu_view_tmpl" 'CPU:#[fg=#{cpu.color}]#{cpu.pused}#[default]')
cpu_medium_threshold=$(get_tmux_option "@sysstat_cpu_medium_threshold" "30")
cpu_stress_threshold=$(get_tmux_option "@sysstat_cpu_stress_threshold" "80")
cpu_color_low=$(get_tmux_option "@sysstat_cpu_color_low" "green")
cpu_color_medium=$(get_tmux_option "@sysstat_cpu_color_medium" "yellow")
cpu_color_stress=$(get_tmux_option "@sysstat_cpu_color_stress" "red")
get_cpu_color(){
local cpu_used=$1
if fcomp "$cpu_stress_threshold" "$cpu_used"; then
echo "$cpu_color_stress";
elif fcomp "$cpu_medium_threshold" "$cpu_used"; then
echo "$cpu_color_medium";
else
echo "$cpu_color_low";
fi
}
print_cpu_usage() {
local cpu_pused=$(get_cpu_usage_or_collect)
local cpu_color=$(get_cpu_color "$cpu_pused")
local cpu_view="$cpu_view_tmpl"
cpu_view="${cpu_view//'#{cpu.pused}'/$(printf "%.1f%%" "$cpu_pused")}"
cpu_view="${cpu_view//'#{cpu.color}'/$(echo "$cpu_color" | awk '{ print $1 }')}"
cpu_view="${cpu_view//'#{cpu.color2}'/$(echo "$cpu_color" | awk '{ print $2 }')}"
cpu_view="${cpu_view//'#{cpu.color3}'/$(echo "$cpu_color" | awk '{ print $3 }')}"
echo "$cpu_view"
}
get_cpu_usage_or_collect() {
local collect_cpu_metric="$cpu_tmp_dir/cpu_collect.metric"
# read cpu metric from file, otherwise 0 as a temporary null value, until first cpu metric is collected
[ -f "$collect_cpu_metric" ] && cat "$collect_cpu_metric" || echo "0.0"
start_cpu_collect_if_required >/dev/null 2>&1
}
start_cpu_collect_if_required() {
local collect_cpu_pidfile="$cpu_tmp_dir/cpu_collect.pid"
# check if cpu collect process is running, otherwise start it in background
if [ -f "$collect_cpu_pidfile" ] && ps -p "$(cat "$collect_cpu_pidfile")" > /dev/null 2>&1; then
return;
fi
jobs >/dev/null 2>&1
"$CURRENT_DIR/cpu_collect.sh" &>/dev/null &
if [ -n "$(jobs -n)" ]; then
echo "$!" > "${collect_cpu_pidfile}"
else
echo "Failed to start CPU collect job" >&2
exit 1
fi
}
main(){
print_cpu_usage
}
main

View File

@ -0,0 +1,53 @@
#!/usr/bin/env bash
LC_NUMERIC=C
set -u
set -e
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
refresh_interval=$(get_tmux_option "status-interval" "5")
samples_count="60"
cpu_metric_file="$(get_tmux_option "@sysstat_cpu_tmp_dir" "/dev/null")/cpu_collect.metric"
get_cpu_usage() {
if is_osx; then
if command_exists "iostat"; then
iostat -w "$refresh_interval" -c "$samples_count" \
| stdbuf -o0 awk 'NR > 2 { print 100-$(NF-3); }'
else
top -l "$samples_count" -s "$refresh_interval" -n 0 \
| sed -u -nr '/CPU usage/s/.*,[[:space:]]*([0-9]+[.,][0-9]*)%[[:space:]]*idle.*/\1/p' \
| stdbuf -o0 awk '{ print 100-$0 }'
fi
elif ! command_exists "vmstat"; then
if is_freebsd; then
vmstat -n "$refresh_interval" -c "$samples_count" \
| stdbuf -o0 awk 'NR>2 {print 100-$(NF-0)}'
else
vmstat -n "$refresh_interval" "$samples_count" \
| stdbuf -o0 awk 'NR>2 {print 100-$(NF-2)}'
fi
else
if is_freebsd; then
top -d"$samples_count" \
| sed -u -nr '/CPU:/s/.*,[[:space:]]*([0-9]+[.,][0-9]*)%[[:space:]]*id.*/\1/p' \
| stdbuf -o0 awk '{ print 100-$0 }'
else
top -b -n "$samples_count" -d "$refresh_interval" \
| sed -u -nr '/%Cpu/s/.*,[[:space:]]*([0-9]+[.,][0-9]*)[[:space:]]*id.*/\1/p' \
| stdbuf -o0 awk '{ print 100-$0 }'
fi
fi
}
main() {
get_cpu_usage | while read -r value; do
echo "$value" | tee "$cpu_metric_file"
done
}
main

View File

@ -0,0 +1,76 @@
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value="$(tmux show-option -gqv "$option")"
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
set_tmux_option() {
local option="$1"
local value="$2"
tmux set-option -gq "$option" "$value"
}
is_osx() {
[ $(uname) == "Darwin" ]
}
is_linux(){
[ $(uname -s) == "Linux" ]
}
is_freebsd() {
[ $(uname) == FreeBSD ]
}
command_exists() {
local command="$1"
type "$command" >/dev/null 2>&1
}
# because bash does not support floating-point math
# but awk does
calc() {
local stdin;
read -d '' -u 0 stdin;
awk "BEGIN { print $stdin }";
}
# "<" math operator which works with floats, once again based on awk
fcomp() {
awk -v n1="$1" -v n2="$2" 'BEGIN {if (n1<n2) exit 0; exit 1}'
}
# get_mem_usage* function returns values in KiB
# 1 - scale to KiB
# 1024 - scale to MiB
# 1048576 - scale to GiB
function get_size_scale_factor(){
local size_unit="$1"
case "$size_unit" in
G) echo 1048576;;
M) echo 1024;;
K) echo 1;;
esac
}
# Depending on scale factor, change precision
# 12612325K - no digits after floating point
# 1261M - no digits after floating point
# 1.1G - 1 digit after floating point
function get_size_format(){
local size_unit="$1"
case "$size_unit" in
G) echo '%.1f%s';;
M) echo '%.0f%s';;
K) echo '%.0f%s';;
esac
}

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -u
set -e
LC_NUMERIC=C
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
loadavg_per_cpu_core=$(get_tmux_option "@sysstat_loadavg_per_cpu_core" "true")
get_num_of_cores(){
is_osx && sysctl -n hw.ncpu || nproc
}
main(){
local num_cores=$([ "$loadavg_per_cpu_core" == "true" ] && get_num_of_cores || echo 1)
uptime | awk -v num_cores="$num_cores" '{
sub(/,$/, "", $(NF-2));
sub(/,$/, "", $(NF-1));
sub(/,$/, "", $NF);
printf "%.2f %.2f %.2f", $(NF-2)/num_cores, $(NF-1)/num_cores, $NF/num_cores
}'
}
main

View File

@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -u
set -e
LC_NUMERIC=C
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
mem_view_tmpl=$(get_tmux_option "@sysstat_mem_view_tmpl" 'MEM:#[fg=#{mem.color}]#{mem.pused}#[default]')
mem_medium_threshold=$(get_tmux_option "@sysstat_mem_medium_threshold" "75")
mem_stress_threshold=$(get_tmux_option "@sysstat_mem_stress_threshold" "90")
mem_color_low=$(get_tmux_option "@sysstat_mem_color_low" "green")
mem_color_medium=$(get_tmux_option "@sysstat_mem_color_medium" "yellow")
mem_color_stress=$(get_tmux_option "@sysstat_mem_color_stress" "red")
size_unit=$(get_tmux_option "@sysstat_mem_size_unit" "G")
get_mem_color() {
local mem_pused=$1
if fcomp "$mem_stress_threshold" "$mem_pused"; then
echo "$mem_color_stress";
elif fcomp "$mem_medium_threshold" "$mem_pused"; then
echo "$mem_color_medium";
else
echo "$mem_color_low";
fi
}
print_mem() {
local mem_usage
local scale
local size_format
if is_osx; then
mem_usage=$(get_mem_usage_osx)
elif is_linux; then
mem_usage=$(get_mem_usage_linux)
elif is_freebsd; then
mem_usage=$(get_mem_usage_freebsd)
fi
local size_scale="$(get_size_scale_factor "$size_unit")"
local size_format="$(get_size_format "$size_unit")"
# Extract free and used memory in MiB, calculate total and percentage
local mem_free=$(echo $mem_usage | awk -v scale="$size_scale" '{ print $1/scale }')
local mem_used=$(echo $mem_usage | awk -v scale="$size_scale" '{ print $2/scale }')
local mem_total=$(echo "$mem_free + $mem_used" | calc)
local mem_pused=$(echo "($mem_used / $mem_total) * 100" | calc)
local mem_pfree=$(echo "($mem_free / $mem_total) * 100" | calc)
# Calculate colors for mem and swap
local mem_color=$(get_mem_color "$mem_pused")
local mem_view="$mem_view_tmpl"
mem_view="${mem_view//'#{mem.used}'/$(printf "$size_format" "$mem_used" "$size_unit")}"
mem_view="${mem_view//'#{mem.pused}'/$(printf "%.0f%%" "$mem_pused")}"
mem_view="${mem_view//'#{mem.free}'/$(printf "$size_format" "$mem_free" "$size_unit")}"
mem_view="${mem_view//'#{mem.pfree}'/$(printf "%.0f%%" "$mem_pfree")}"
mem_view="${mem_view//'#{mem.total}'/$(printf "$size_format" "$mem_total" "$size_unit")}"
mem_view="${mem_view//'#{mem.color}'/$(echo "$mem_color" | awk '{ print $1 }')}"
mem_view="${mem_view//'#{mem.color2}'/$(echo "$mem_color" | awk '{ print $2 }')}"
mem_view="${mem_view//'#{mem.color3}'/$(echo "$mem_color" | awk '{ print $3 }')}"
echo "$mem_view"
}
# Report like it does htop on OSX:
# used = active + wired
# free = free + inactive + speculative + occupied by compressor
# see `vm_stat` command
get_mem_usage_osx(){
local page_size=$(sysctl -nq "vm.pagesize")
vm_stat | awk -v page_size=$page_size -F ':' '
BEGIN { free=0; used=0 }
/Pages active/ ||
/Pages wired/ {
gsub(/^[ \t]+|[ \t]+$/, "", $2); used+=$2;
}
/Pages free/ ||
/Pages inactive/ ||
/Pages speculative/ ||
/Pages occupied by compressor/ {
gsub(/^[ \t]+|[ \t]+$/, "", $2); free+=$2;
}
END { print (free * page_size)/1024, (used * page_size)/1024 }
'
}
# Relies on vmstat, but could also be done with top on FreeBSD
get_mem_usage_freebsd(){
vmstat -H | tail -n 1 | awk '{ print $5, $4 }'
}
# Method #1. Sum up free+buffers+cached, treat it as "available" memory, assuming buff+cache can be reclaimed. Note, that this assumption is not 100% correct, buff+cache most likely cannot be 100% reclaimed, but this is how memory calculation is used to be done on Linux
# Method #2. If "MemAvailable" is provided by system, use it. This is more correct method, because we're not relying on fragile "free+buffer+cache" equation.
# See: Interpreting /proc/meminfo and free output for Red Hat Enterprise Linux 5, 6 and 7 - Red Hat Customer Portal - https://access.redhat.com/solutions/406773
# See: kernel/git/torvalds/linux.git - /proc/meminfo: provide estimated available memory - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
get_mem_usage_linux(){
</proc/meminfo awk '
BEGIN { total=0; free=0; }
/MemTotal:/ { total=$2; }
/MemFree:/ { free+=$2; }
/Buffers:/ { free+=$2; }
/Cached:/ { free+=$2; }
/MemAvailable:/ { free=$2; exit;}
END { print free, total-free }
'
}
main() {
print_mem
}
main

View File

@ -0,0 +1,95 @@
#!/usr/bin/env bash
set -u
set -e
LC_NUMERIC=C
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/helpers.sh"
swap_view_tmpl=$(get_tmux_option "@sysstat_swap_view_tmpl" 'SW:#[fg=#{swap.color}]#{swap.pused}#[default]')
swap_medium_threshold=$(get_tmux_option "@sysstat_swap_medium_threshold" "25")
swap_stress_threshold=$(get_tmux_option "@sysstat_swap_stress_threshold" "75")
swap_color_low=$(get_tmux_option "@sysstat_swap_color_low" "green")
swap_color_medium=$(get_tmux_option "@sysstat_swap_color_medium" "yellow")
swap_color_stress=$(get_tmux_option "@sysstat_swap_color_stress" "red")
size_unit=$(get_tmux_option "@sysstat_swap_size_unit" "G")
get_swap_color() {
local swap_pused=$1
if fcomp "$swap_stress_threshold" "$swap_pused"; then
echo "$swap_color_stress";
elif fcomp "$swap_medium_threshold" "$swap_pused"; then
echo "$swap_color_medium";
else
echo "$swap_color_low";
fi
}
print_swap() {
local swap_usage
if is_osx; then
swap_usage=$(get_swap_usage_osx)
elif is_linux; then
swap_usage=$(get_swap_usage_linux)
fi
local size_scale="$(get_size_scale_factor "$size_unit")"
local size_format="$(get_size_format "$size_unit")"
# Extract swap free and used in MiB, calculate total and percentage
local swap_free=$(echo $swap_usage | awk -v scale="$size_scale" '{ print $1/scale }')
local swap_used=$(echo $swap_usage | awk -v scale="$size_scale" '{ print $2/scale }')
local swap_total=$(echo "$swap_free + $swap_used" | calc)
local swap_pused=$(echo "($swap_used / $swap_total) * 100" | calc)
local swap_pfree=$(echo "($swap_free / $swap_total) * 100" | calc)
# Calculate colors for mem and swap
local swap_color=$(get_swap_color "$swap_pused")
local swap_view="$swap_view_tmpl"
swap_view="${swap_view//'#{swap.used}'/$(printf "$size_format" "$swap_used" "$size_unit")}"
swap_view="${swap_view//'#{swap.pused}'/$(printf "%.0f%%" "$swap_pused")}"
swap_view="${swap_view//'#{swap.free}'/$(printf "$size_format" "$swap_free" "$size_unit")}"
swap_view="${swap_view//'#{swap.pfree}'/$(printf "%.0f%%" "$swap_pfree")}"
swap_view="${swap_view//'#{swap.total}'/$(printf "$size_format" "$swap_total" "$size_unit")}"
swap_view="${swap_view//'#{swap.color}'/$(echo "$swap_color" | awk '{ print $1 }')}"
swap_view="${swap_view//'#{swap.color2}'/$(echo "$swap_color" | awk '{ print $2 }')}"
swap_view="${swap_view//'#{swap.color3}'/$(echo "$swap_color" | awk '{ print $3 }')}"
echo "$swap_view"
}
get_swap_usage_osx(){
# assume swap size in MB
local swap_used=$(sysctl -nq vm.swapusage | awk -F ' ' '{ print $2 }' | awk -F '=' '{gsub(/^[ ]|[M]$/, "", $2); printf "%d", $2 * 1024 }')
local swap_free=$(sysctl -nq vm.swapusage | awk -F ' ' '{ print $3 }' | awk -F '=' '{gsub(/^[ ]|[M]$/, "", $2); printf "%d", $2 * 1024 }')
printf "%s %s" "$swap_free" "$swap_used"
}
get_swap_usage_linux(){
</proc/meminfo awk '
BEGIN { total=0; free=0; }
/SwapTotal:/ { total=$2; }
/SwapFree:/ { free=$2; }
END { print free, total-free }
'
}
main() {
print_swap
}
main

View File

@ -0,0 +1,43 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/helpers.sh"
placeholders=(
"\#{sysstat_cpu}"
"\#{sysstat_mem}"
"\#{sysstat_swap}"
"\#{sysstat_loadavg}"
)
commands=(
"#($CURRENT_DIR/scripts/cpu.sh)"
"#($CURRENT_DIR/scripts/mem.sh)"
"#($CURRENT_DIR/scripts/swap.sh)"
"#($CURRENT_DIR/scripts/loadavg.sh)"
)
do_interpolation() {
local all_interpolated="$1"
for ((i=0; i<${#commands[@]}; i++)); do
all_interpolated=${all_interpolated//${placeholders[$i]}/${commands[$i]}}
done
echo "$all_interpolated"
}
update_tmux_option() {
local option="$1"
local option_value="$(get_tmux_option "$option")"
local new_option_value="$(do_interpolation "$option_value")"
set_tmux_option "$option" "$new_option_value"
}
main() {
cpu_tmp_dir=$(mktemp -d)
tmux set-option -gq "@sysstat_cpu_tmp_dir" "$cpu_tmp_dir"
update_tmux_option "status-right"
update_tmux_option "status-left"
}
main

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Erick Pintor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,111 @@
# Tmux prefix highlight
Plugin that highlights when you press tmux prefix key. Inspired by
[this](http://stackoverflow.com/questions/12003726/give-a-hint-when-press-prefix-key-in-tmux)
thread on stackoverflow.
Many thanks to [@obxhdx](https://github.com/obxhdx) for showing me this trick.
Prefix off:
![prefix_off](screenshots/prefix_off.png)
Prefix on:
![prefix_on](screenshots/prefix_on.png)
### Usage
Just add `#{prefix_highlight}` to your left/right status bar.
```tmux.conf
set -g status-right '#{prefix_highlight} | %a %Y-%m-%d %H:%M'
```
The plugin can also be configured to show when copy mode is active; see the
**Configurations** section for details.
### Installation with Tmux Plugin Manager (recommended)
Add plugin to the list of TPM plugins:
```tmux.conf
set -g @plugin 'tmux-plugins/tmux-prefix-highlight'
```
Press prefix + I to install it.
### Manual Installation
Clone the repo:
```bash
$ git clone https://github.com/tmux-plugins/tmux-prefix-highlight.git ~/clone/path
```
Add this line to your .tmux.conf:
```tmux.conf
run-shell ~/clone/path/prefix_highlight.tmux
```
Reload TMUX environment with:
```bash
$ tmux source-file ~/.tmux.conf
```
### Configurations
The colors used for the prefix highlight can be configured:
```tmux.conf
set -g @prefix_highlight_fg 'white' # default is 'colour231'
set -g @prefix_highlight_bg 'blue' # default is 'colour04'
```
The plugin can also be configured to show when copy mode is active. If enabled,
the `#{prefix_highlight}` token will be replaced with the string `Copy` when
copy mode is enabled. The style for copy mode can be configured as a
comma-separated list of colors and attributes:
```tmux.conf
set -g @prefix_highlight_show_copy_mode 'on'
set -g @prefix_highlight_copy_mode_attr 'fg=black,bg=yellow,bold' # default is 'fg=default,bg=yellow'
```
The `prefix` prompt and `copy` prompt can also be configured:
```tmux.conf
set -g @prefix_highlight_prefix_prompt 'Wait'
set -g @prefix_highlight_copy_prompt 'Copy'
```
Additionally, the plugin can be configured to attach optional affixes to the
value contained in `#{prefix_highlight}`.
(e.g. `< ^B >`)
```tmux.conf
set -g @prefix_highlight_output_prefix '< '
set -g @prefix_highlight_output_suffix ' >'
```
The empty (shown when prefix is off) prompt and attribute can be configured,
It is useful for aligning segments.
```tmux.conf
set -g @prefix_highlight_empty_prompt ' ' # default is '' (empty char)
set -g @prefix_highlight_empty_attr 'fg=default,bg=green' # default is 'fg=default,bg=default'
```
Defaultly, empty prompt can't be attached optional affixes.
If you want attach affixes on empty prompt, config `@prefix_highlight_empty_has_affixes` to `on`.
```tmux.conf
set -g @prefix_highlight_empty_has_affixes 'on' # default is 'off'
set -g @prefix_highlight_empty_prompt 'Tmux'
set -g @prefix_highlight_output_prefix '< '
set -g @prefix_highlight_output_suffix ' >'
```
### License
[MIT](LICENSE)

View File

@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -e
# Place holder for status left/right
place_holder="\#{prefix_highlight}"
# Possible configurations
fg_color_config='@prefix_highlight_fg'
bg_color_config='@prefix_highlight_bg'
output_prefix='@prefix_highlight_output_prefix'
output_suffix='@prefix_highlight_output_suffix'
show_copy_config='@prefix_highlight_show_copy_mode'
copy_attr_config='@prefix_highlight_copy_mode_attr'
prefix_prompt='@prefix_highlight_prefix_prompt'
copy_prompt='@prefix_highlight_copy_prompt'
empty_prompt='@prefix_highlight_empty_prompt'
empty_attr_config='@prefix_highlight_empty_attr'
empty_has_affixes='@prefix_highlight_empty_has_affixes'
tmux_option() {
local -r value=$(tmux show-option -gqv "$1")
local -r default="$2"
if [ ! -z "$value" ]; then
echo "$value"
else
echo "$default"
fi
}
# Defaults
default_fg='colour231'
default_bg='colour04'
default_copy_attr='fg=default,bg=yellow'
default_empty_attr='fg=default,bg=default'
default_prefix_prompt=$(tmux_option prefix | tr "[:lower:]" "[:upper:]" | sed 's/C-/\^/')
default_copy_prompt='Copy'
default_empty_prompt=''
highlight() {
local -r \
status="$1" \
prefix="$2" \
prefix_highlight="$3" \
show_copy_mode="$4" \
copy_highlight="$5" \
output_prefix="$6" \
output_suffix="$7" \
copy="$8" \
empty="$9"
local -r status_value="$(tmux_option "$status")"
local -r prefix_with_optional_affixes="$output_prefix$prefix$output_suffix"
local -r copy_with_optional_affixes="$output_prefix$copy$output_suffix"
if [[ "on" = "$empty_has_affixes" ]]; then
local -r empty_with_optional_affixes="$output_prefix$empty$output_suffix"
else
local -r empty_with_optional_affixes="$empty"
fi
if [[ "on" = "$show_copy_mode" ]]; then
local -r fallback="${copy_highlight}#{?pane_in_mode,$copy_with_optional_affixes,${empty_highlight}$empty_with_optional_affixes}"
else
local -r fallback="${empty_highlight}$empty_with_optional_affixes"
fi
local -r highlight_on_prefix="${prefix_highlight}#{?client_prefix,$prefix_with_optional_affixes,$fallback}#[default]"
tmux set-option -gq "$status" "${status_value/$place_holder/$highlight_on_prefix}"
}
main() {
local -r \
fg_color=$(tmux_option "$fg_color_config" "$default_fg") \
bg_color=$(tmux_option "$bg_color_config" "$default_bg") \
show_copy_mode=$(tmux_option "$show_copy_config" "off") \
output_prefix=$(tmux_option "$output_prefix" " ") \
output_suffix=$(tmux_option "$output_suffix" " ") \
copy_attr=$(tmux_option "$copy_attr_config" "$default_copy_attr") \
prefix_prompt=$(tmux_option "$prefix_prompt" "$default_prefix_prompt") \
copy_prompt=$(tmux_option "$copy_prompt" "$default_copy_prompt") \
empty_prompt=$(tmux_option "$empty_prompt" "$default_empty_prompt") \
empty_attr=$(tmux_option "$empty_attr_config" "$default_empty_attr") \
empty_has_affixes=$(tmux_option "$empty_has_affixes" "off")
local -r \
prefix_highlight="#[fg=$fg_color,bg=$bg_color]" \
copy_highlight="${copy_attr:+#[default,$copy_attr]}" \
empty_highlight="${empty_attr:+#[default,$empty_attr]}"
highlight "status-right" \
"$prefix_prompt" \
"$prefix_highlight" \
"$show_copy_mode" \
"$copy_highlight" \
"$output_prefix" \
"$output_suffix" \
"$copy_prompt" \
"$empty_prompt" \
"$empty_highlight" \
"$empty_has_affixes"
highlight "status-left" \
"$prefix_prompt" \
"$prefix_highlight" \
"$show_copy_mode" \
"$copy_highlight" \
"$output_prefix" \
"$output_suffix" \
"$copy_prompt" \
"$empty_prompt" \
"$empty_highlight" \
"$empty_has_affixes"
}
main

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,133 @@
# Tmux Resurrect
[![Build Status](https://travis-ci.org/tmux-plugins/tmux-resurrect.svg?branch=master)](https://travis-ci.org/tmux-plugins/tmux-resurrect)
Restore `tmux` environment after system restart.
Tmux is great, except when you have to restart the computer. You lose all the
running programs, working directories, pane layouts etc.
There are helpful management tools out there, but they require initial
configuration and continuous updates as your workflow evolves or you start new
projects.
`tmux-resurrect` saves all the little details from your tmux environment so it
can be completely restored after a system restart (or when you feel like it).
No configuration is required. You should feel like you never quit tmux.
It even (optionally)
[restores vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)!
Automatic restoring and continuous saving of tmux env is also possible with
[tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) plugin.
### Screencast
[![screencast screenshot](/video/screencast_img.png)](https://vimeo.com/104763018)
### Key bindings
- `prefix + Ctrl-s` - save
- `prefix + Ctrl-r` - restore
### About
This plugin goes to great lengths to save and restore all the details from your
`tmux` environment. Here's what's been taken care of:
- all sessions, windows, panes and their order
- current working directory for each pane
- **exact pane layouts** within windows (even when zoomed)
- active and alternative session
- active and alternative window for each session
- windows with focus
- active pane for each window
- "grouped sessions" (useful feature when using tmux with multiple monitors)
- programs running within a pane! More details in the
[restoring programs doc](docs/restoring_programs.md).
Optional:
- [restoring vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)
- [restoring pane contents](docs/restoring_pane_contents.md)
- [restoring shell history](docs/restoring_shell_history.md) (experimental)
Requirements / dependencies: `tmux 1.9` or higher, `bash`.
Tested and working on Linux, OSX and Cygwin.
`tmux-resurrect` is idempotent! It will not try to restore panes or windows that
already exist.<br/>
The single exception to this is when tmux is started with only 1 pane in order
to restore previous tmux env. Only in this case will this single pane be
overwritten.
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-resurrect'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-resurrect ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/resurrect.tmux
Reload TMUX environment with: `$ tmux source-file ~/.tmux.conf`.
You should now be able to use the plugin.
### Docs
- [Guide for migrating from tmuxinator](docs/migrating_from_tmuxinator.md)
**Configuration**
- [Changing the default key bindings](docs/custom_key_bindings.md).
- [Setting up hooks on save & restore](docs/hooks.md).
- Only a conservative list of programs is restored by default:<br/>
`vi vim nvim emacs man less more tail top htop irssi weechat mutt`.<br/>
[Restoring programs doc](docs/restoring_programs.md) explains how to restore
additional programs.
- [Change a directory](docs/save_dir.md) where `tmux-resurrect` saves tmux
environment.
**Optional features**
- [Restoring vim and neovim sessions](docs/restoring_vim_and_neovim_sessions.md)
is nice if you're a vim/neovim user.
- [Restoring pane contents](docs/restoring_pane_contents.md) feature.
**Experimental features (also optional)**
- [restoring shell history](docs/restoring_shell_history.md)
### Other goodies
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-yank](https://github.com/tmux-plugins/tmux-yank) - enables copying
highlighted text to system clipboard
- [tmux-open](https://github.com/tmux-plugins/tmux-open) - a plugin for quickly
opening highlighted file or a url
- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic
restoring and continuous saving of tmux env
### Reporting bugs and contributing
Both contributing and bug reports are welcome. Please check out
[contributing guidelines](CONTRIBUTING.md).
### Credits
[Mislav Marohnić](https://github.com/mislav) - the idea for the plugin came from his
[tmux-session script](https://github.com/mislav/dotfiles/blob/2036b5e03fb430bbcbc340689d63328abaa28876/bin/tmux-session).
### License
[MIT](LICENSE.md)

View File

@ -0,0 +1,11 @@
# Custom key bindings
The default key bindings are:
- `prefix + Ctrl-s` - save
- `prefix + Ctrl-r` - restore
To change these, add to `.tmux.conf`:
set -g @resurrect-save 'S'
set -g @resurrect-restore 'R'

View File

@ -0,0 +1,39 @@
# Save & Restore Hooks
Hooks allow to set custom commands that will be executed during session save
and restore. Most hooks are called with zero arguments, unless explicitly
stated otherwise.
Currently the following hooks are supported:
- `@resurrect-hook-post-save-layout`
Called after all sessions, panes and windows have been saved.
Passed single argument of the state file.
- `@resurrect-hook-post-save-all`
Called at end of save process right before the spinner is turned off.
- `@resurrect-hook-pre-restore-all`
Called before any tmux state is altered.
- `@resurrect-hook-pre-restore-history`
Called after panes and layout have been restores, but before bash history is
restored (if it is enabled) -- the hook is always called even if history
saving is disabled.
- `@resurrect-hook-pre-restore-pane-processes`
Called after history is restored, but before running processes are restored.
### Examples
Here is an example how to save and restore window geometry for most terminals in X11.
Add this to `.tmux.conf`:
set -g @resurrect-hook-post-save-all 'eval $(xdotool getwindowgeometry --shell $WINDOWID); echo 0,$X,$Y,$WIDTH,$HEIGHT > $HOME/.tmux/resurrect/geometry'
set -g @resurrect-hook-pre-restore-all 'wmctrl -i -r $WINDOWID -e $(cat $HOME/.tmux/resurrect/geometry)'

View File

@ -0,0 +1,72 @@
# Migrating from `tmuxinator`
### Why migrate to `tmux-resurrect`?
Here are some reasons why you'd want to migrate from `tmuxinator` to
`tmux-resurrect`:
- **Less dependencies**<br/>
`tmuxinator` depends on `ruby` which can be a hassle to set up if you're not a
rubyist.<br/>
`tmux-resurrect` depends just on `bash` which is virtually
omnipresent.
- **Simplicity**<br/>
`tmuxinator` has an executable, CLI interface with half dozen commands and
command completion.<br/>
`tmux-resurrect` defines just 2 tmux key bindings.
- **No configuration**<br/>
`tmuxinator` is all about config files (and their constant updating).<br/>
`tmux-resurrect` requires no configuration to work.
- **Better change handling**<br/>
When you make a change to any aspect of tmux layout, you also have to
update related `tmuxinator` project file (and test to make sure change is
ok).<br/>
With `tmux-resurrect` there's nothing to do: your change will be
remembered on the next save.
### How to migrate?
1. Install `tmux-resurrect`.
2. Open \*all* existing `tmuxinator` projects.<br/>
Verify all projects are open by pressing `prefix + s` and checking they are
all on the list.
3. Perform a `tmux-resurrect` save.
That's it! You can continue using just `tmux-resurrect` should you choose so.
Note: it probably makes no sense to use both tools at the same time as they do
the same thing (creating tmux environment for you to work in).
Technically however, there should be no issues.
### Usage differences
`tmuxinator` focuses on managing individual tmux sessions (projects).
`tmux-resurrect` keeps track of the \*whole* tmux environment: all sessions are
saved and restored together.
A couple tips if you decide to switch to `tmux-resurrect`:
- Keep all tmux sessions (projects) running all the time.<br/>
If you want to work on an existing project, you should be able to just
\*switch* to an already open session using `prefix + s`.<br/>
This is different from `tmuxinator` where you'd usually run `mux new [project]`
in order to start working on something.
- No need to kill sessions with `tmux kill-session` (unless you really don't
want to work on it ever).<br/>
It's the recurring theme by now: just keep all the sessions running all the
time. This is convenient and also cheap in terms of resources.
- The only 2 situations when you need `tmux-resurrect`:<br/>
1) Save tmux environment just before restarting/shutting down your
computer.<br/>
2) Restore tmux env after you turn the computer on.
### Other questions?
Still have questions? Feel free to open an
[issue](ihttps://github.com/tmux-plugins/tmux-resurrect/issues). We'll try to
answer it and also update this doc.

View File

@ -0,0 +1,31 @@
# Restoring pane contents
This plugin enables saving and restoring tmux pane contents.
This feature can be enabled by adding this line to `.tmux.conf`:
set -g @resurrect-capture-pane-contents 'on'
##### Known issue
When using this feature, please check the value of `default-command`
tmux option. That can be done with `$ tmux show -g default-command`.
The value should NOT contain `&&` or `||` operators. If it does, simplify the
option so those operators are removed.
Example:
- this will cause issues (notice the `&&` and `||` operators):
set -g default-command "which reattach-to-user-namespace > /dev/null && reattach-to-user-namespace -l $SHELL || $SHELL -l"
- this is ok:
set -g default-command "reattach-to-user-namespace -l $SHELL"
Related [bug](https://github.com/tmux-plugins/tmux-resurrect/issues/98).
Alternatively, you can let
[tmux-sensible](https://github.com/tmux-plugins/tmux-sensible)
handle this option in a cross-platform way and you'll have no problems.

View File

@ -0,0 +1,179 @@
# Restoring programs
- [General instructions](#general-instructions)
- [Clarifications](#clarifications)
- [Working with NodeJS](#nodejs)
- [Restoring Mosh](#mosh)
### General instructions <a name="general-instructions"></a>
Only a conservative list of programs is restored by default:<br/>
`vi vim nvim emacs man less more tail top htop irssi weechat mutt`.
This can be configured with `@resurrect-processes` option in `.tmux.conf`. It
contains space-separated list of additional programs to restore.
- Example restoring additional programs:
set -g @resurrect-processes 'ssh psql mysql sqlite3'
- Programs with arguments should be double quoted:
set -g @resurrect-processes 'some_program "git log"'
- Start with tilde to restore a program whose process contains target name:
set -g @resurrect-processes 'irb pry "~rails server" "~rails console"'
- Use `->` to specify a command to be used when restoring a program (useful if
the default restore command fails ):
set -g @resurrect-processes 'some_program "grunt->grunt development"'
- Don't restore any programs:
set -g @resurrect-processes 'false'
- Restore **all** programs (be careful with this!):
set -g @resurrect-processes ':all:'
### Clarifications <a name="clarfications"></a>
> I don't understand tilde `~`, what is it and why is it used when restoring
programs?
Let's say you use `rails server` command often. You want `tmux-resurrect` to
save and restore it automatically. You might try adding `rails server` to the
list of programs that will be restored:
set -g @resurrect-processes '"rails server"' # will NOT work
Upon save, `rails server` command will actually be saved as this command:
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server`
(if you wanna see how is any command saved, check it yourself in
`~/.tmux/resurrect/last` file).
When programs are restored, the `rails server` command will NOT be restored
because it does not **strictly** match the long
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server` string.
The tilde `~` at the start of the string relaxes process name matching.
set -g @resurrect-processes '"~rails server"' # OK
The above option says: "restore full process if `rails server` string is found
ANYWHERE in the process name".
If you check long process string, there is in fact a `rails server` string at
the end, so now the process will be successfully restored.
> What is arrow `->` and why is is used?
(Please read the above clarification about tilde `~`).
Continuing with our `rails server` example, when the process is finally restored
correctly it might not look pretty as you'll see the whole
`/Users/user/.rbenv/versions/2.0.0-p481/bin/ruby script/rails server` string in
the command line.
Naturally, you'd rather want to see just `rails server` (what you initially
typed), but that information is now unfortunately lost.
To aid this, you can use arrow `->`: (**note**: there is no space before and after `->`)
set -g @resurrect-processes '"~rails server->rails server"' # OK
This option says: "when this process is restored use `rails server` as the
command name".
Full (long) process name is now ignored and you'll see just `rails server` in
the command line when the program is restored.
> Now I understand the tilde and the arrow, but things still don't work for me
Here's the general workflow for figuring this out:
- Set up your whole tmux environment manually.<br/>
In our example case, we'd type `rails server` in a pane where we want it to
run.
- Save tmux env (it will get saved to `~/.tmux/resurrect/last`).
- Open `~/.tmux/resurrect/last` file and try to find full process string for
your program.<br/>
Unfortunately this is a little vague but it should be easy. A smart
thing to do for our example is to search for string `rails` in the `last`
file.
- Now that you know the full and the desired process string use tilde `~` and
arrow `->` in `.tmux.conf` to make things work.
### Working with NodeJS <a name="nodejs"></a>
If you are working with NodeJS, you may get some troubles with configuring restoring programs.
Particularly, some programs like `gulp`, `grunt` or `npm` are not saved with parameters so tmux-resurrect cannot restore it. This is actually **not tmux-resurrect's issue** but more likely, those programs' issues. For example if you run `gulp watch` or `npm start` and then try to look at `ps` or `pgrep`, you will only see `gulp` or `npm`.
To deal with these issues, one solution is to use [yarn](https://yarnpkg.com/en/docs/install) which a package manager for NodeJS and an alternative for `npm`. It's nearly identical to `npm` and very easy to use. Therefore you don't have to do any migration, you can simply use it immediately. For example:
- `npm test` is equivalent to `yarn test`,
- `npm run watch:dev` is equivalent to `yarn watch:dev`
- more interestingly, `gulp watch:dev` is equivalent to `yarn gulp watch:dev`
Before continuing, please ensure that you understand the [clarifications](#clarifications) section about `~` and `->`
#### yarn
It's fairly straight forward if you have been using `yarn` already.
set -g @resurrect-processes '"~yarn watch"'
set -g @resurrect-processes '"~yarn watch->yarn watch"'
#### npm
Instead of
set -g @resurrect-processes '"~npm run watch"' # will NOT work
we use
set -g @resurrect-processes '"~yarn watch"' # OK
#### gulp
Instead of
set -g @resurrect-processes '"~gulp test"' # will NOT work
we use
set -g @resurrect-processes '"~yarn gulp test"' # OK
#### nvm
If you use `nvm` in your project, here is how you could config tmux-resurrect:
set -g @resurrect-processes '"~yarn gulp test->nvm use && gulp test"'
#### Another problem
Let take a look at this example
set -g @resurrect-processes '\
"~yarn gulp test->gulp test" \
"~yarn gulp test-it->gulp test-it" \
'
**This will not work properly**, only `gulp test` is run, although you can see the command `node /path/to/yarn gulp test-it` is added correctly in `.tmux/resurrect/last` file.
The reason is when restoring program, the **command part after the dash `-` is ignored** so instead of command `gulp test-it`, the command `gulp test` which will be run.
A work around, for this problem until it's fixed, is:
- the config should be like this:
set -g @resurrect-processes '\
"~yarn gulp test->gulp test" \
"~yarn gulp \"test-it\"->gulp test-it" \
- and in `.tmux/resurrect/last`, we should add quote to `test-it` word
... node:node /path/to/yarn gulp "test-it"
### Restoring Mosh <a name="#mosh"></a>
Mosh spawns a `mosh-client` process, so we need to specify that as the process to be resurrected.
set -g @resurrect-processes 'mosh-client'
Additionally a mosh-client strategy is provided to handle extracting the original arguments and re-run Mosh.

View File

@ -0,0 +1,19 @@
# Restoring shell history (experimental)
**Supported shells**: `bash` and `zsh`.
Enable feature with this option in `.tmux.conf`:
set -g @resurrect-save-shell-history 'on'
**Note**: the older `@resurrect-save-bash-history` is now an alias to
`@resurrect-save-shell-history`.
Shell `history` for individual panes will now be saved and restored. Due to
technical limitations, this only works for panes which have no program running
in foreground when saving. `tmux-resurrect` will send history write command to
each such pane.
To prevent these commands from being added to `bash` history
themselves, add `HISTCONTROL=ignoreboth` to your `.bashrc`
(this is set by default in Ubuntu).

View File

@ -0,0 +1,15 @@
# Restoring vim and neovim sessions
- save vim/neovim sessions. I recommend
[tpope/vim-obsession](https://github.com/tpope/vim-obsession) (as almost every
plugin, it works for both vim and neovim).
- in `.tmux.conf`:
# for vim
set -g @resurrect-strategy-vim 'session'
# for neovim
set -g @resurrect-strategy-nvim 'session'
`tmux-resurrect` will now restore vim and neovim sessions if `Session.vim` file
is present.

View File

@ -0,0 +1,15 @@
# Resurrect save dir
By default Tmux environment is saved to a file in `~/.tmux/resurrect` dir.
Change this with:
set -g @resurrect-dir '/some/path'
Using environment variables or shell interpolation in this option is not
allowed as the string is used literally. So the following won't do what is
expected:
set -g @resurrect-dir '/path/$MY_VAR/$(some_executable)'
Only the following variables and special chars are allowed:
`$HOME`, `$HOSTNAME`, and `~`.

View File

@ -0,0 +1,40 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/scripts/variables.sh"
source "$CURRENT_DIR/scripts/helpers.sh"
set_save_bindings() {
local key_bindings=$(get_tmux_option "$save_option" "$default_save_key")
local key
for key in $key_bindings; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/save.sh"
done
}
set_restore_bindings() {
local key_bindings=$(get_tmux_option "$restore_option" "$default_restore_key")
local key
for key in $key_bindings; do
tmux bind-key "$key" run-shell "$CURRENT_DIR/scripts/restore.sh"
done
}
set_default_strategies() {
tmux set-option -gq "${restore_process_strategy_option}irb" "default_strategy"
tmux set-option -gq "${restore_process_strategy_option}mosh-client" "default_strategy"
}
set_script_path_options() {
tmux set-option -gq "$save_path_option" "$CURRENT_DIR/scripts/save.sh"
tmux set-option -gq "$restore_path_option" "$CURRENT_DIR/scripts/restore.sh"
}
main() {
set_save_bindings
set_restore_bindings
set_default_strategies
set_script_path_options
}
main

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
gdb -batch --eval "attach $PANE_PID" --eval "call write_history(\"/tmp/bash_history-${PANE_PID}.txt\")" --eval 'detach' --eval 'q' >/dev/null 2>&1
\tail -1 "/tmp/bash_history-${PANE_PID}.txt"
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
\pgrep -lf -P "$PANE_PID" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -0,0 +1,24 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PANE_PID="$1"
exit_safely_if_empty_ppid() {
if [ -z "$PANE_PID" ]; then
exit 0
fi
}
full_command() {
ps -ao "ppid command" |
sed "s/^ *//" |
grep "^${PANE_PID}" |
cut -d' ' -f2-
}
main() {
exit_safely_if_empty_ppid
full_command
}
main

View File

@ -0,0 +1,78 @@
#!/usr/bin/env bash
VERSION="$1"
UNSUPPORTED_MSG="$2"
get_tmux_option() {
local option=$1
local default_value=$2
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
# this is used to get "clean" integer version number. Examples:
# `tmux 1.9` => `19`
# `1.9a` => `19`
get_digits_from_string() {
local string="$1"
local only_digits="$(echo "$string" | tr -dC '[:digit:]')"
echo "$only_digits"
}
tmux_version_int() {
local tmux_version_string=$(tmux -V)
echo "$(get_digits_from_string "$tmux_version_string")"
}
unsupported_version_message() {
if [ -n "$UNSUPPORTED_MSG" ]; then
echo "$UNSUPPORTED_MSG"
else
echo "Error, Tmux version unsupported! Please install Tmux version $VERSION or greater!"
fi
}
exit_if_unsupported_version() {
local current_version="$1"
local supported_version="$2"
if [ "$current_version" -lt "$supported_version" ]; then
display_message "$(unsupported_version_message)"
exit 1
fi
}
main() {
local supported_version_int="$(get_digits_from_string "$VERSION")"
local current_version_int="$(tmux_version_int)"
exit_if_unsupported_version "$current_version_int" "$supported_version_int"
}
main

View File

@ -0,0 +1,168 @@
default_resurrect_dir="$HOME/.tmux/resurrect"
resurrect_dir_option="@resurrect-dir"
SUPPORTED_VERSION="1.9"
RESURRECT_FILE_PREFIX="tmux_resurrect"
RESURRECT_FILE_EXTENSION="txt"
_RESURRECT_DIR=""
_RESURRECT_FILE_PATH=""
d=$'\t'
# helper functions
get_tmux_option() {
local option="$1"
local default_value="$2"
local option_value=$(tmux show-option -gqv "$option")
if [ -z "$option_value" ]; then
echo "$default_value"
else
echo "$option_value"
fi
}
# Ensures a message is displayed for 5 seconds in tmux prompt.
# Does not override the 'display-time' tmux option.
display_message() {
local message="$1"
# display_duration defaults to 5 seconds, if not passed as an argument
if [ "$#" -eq 2 ]; then
local display_duration="$2"
else
local display_duration="5000"
fi
# saves user-set 'display-time' option
local saved_display_time=$(get_tmux_option "display-time" "750")
# sets message display time to 5 seconds
tmux set-option -gq display-time "$display_duration"
# displays message
tmux display-message "$message"
# restores original 'display-time' value
tmux set-option -gq display-time "$saved_display_time"
}
supported_tmux_version_ok() {
$CURRENT_DIR/check_tmux_version.sh "$SUPPORTED_VERSION"
}
remove_first_char() {
echo "$1" | cut -c2-
}
capture_pane_contents_option_on() {
local option="$(get_tmux_option "$pane_contents_option" "off")"
[ "$option" == "on" ]
}
files_differ() {
! cmp -s "$1" "$2"
}
save_shell_history_option_on() {
local option_shell="$(get_tmux_option "$shell_history_option" "off")"
local option_bash="$(get_tmux_option "$bash_history_option" "off")"
[ "$option_shell" == "on" ] || [ "$option_bash" == "on" ]
}
get_grouped_sessions() {
local grouped_sessions_dump="$1"
export GROUPED_SESSIONS="${d}$(echo "$grouped_sessions_dump" | cut -f2 -d"$d" | tr "\\n" "$d")"
}
is_session_grouped() {
local session_name="$1"
[[ "$GROUPED_SESSIONS" == *"${d}${session_name}${d}"* ]]
}
# pane content file helpers
pane_contents_create_archive() {
tar cf - -C "$(resurrect_dir)/save/" ./pane_contents/ |
gzip > "$(pane_contents_archive_file)"
}
pane_content_files_restore_from_archive() {
local archive_file="$(pane_contents_archive_file)"
if [ -f "$archive_file" ]; then
mkdir -p "$(pane_contents_dir "restore")"
gzip -d < "$archive_file" |
tar xf - -C "$(resurrect_dir)/restore/"
fi
}
# path helpers
resurrect_dir() {
if [ -z "$_RESURRECT_DIR" ]; then
local path="$(get_tmux_option "$resurrect_dir_option" "$default_resurrect_dir")"
# expands tilde, $HOME and $HOSTNAME if used in @resurrect-dir
echo "$path" | sed "s,\$HOME,$HOME,g; s,\$HOSTNAME,$(hostname),g; s,\~,$HOME,g"
else
echo "$_RESURRECT_DIR"
fi
}
_RESURRECT_DIR="$(resurrect_dir)"
resurrect_file_path() {
if [ -z "$_RESURRECT_FILE_PATH" ]; then
local timestamp="$(date +"%Y%m%dT%H%M%S")"
echo "$(resurrect_dir)/${RESURRECT_FILE_PREFIX}_${timestamp}.${RESURRECT_FILE_EXTENSION}"
else
echo "$_RESURRECT_FILE_PATH"
fi
}
_RESURRECT_FILE_PATH="$(resurrect_file_path)"
last_resurrect_file() {
echo "$(resurrect_dir)/last"
}
pane_contents_dir() {
echo "$(resurrect_dir)/$1/pane_contents/"
}
pane_contents_file() {
local save_or_restore="$1"
local pane_id="$2"
echo "$(pane_contents_dir "$save_or_restore")/pane-${pane_id}"
}
pane_contents_file_exists() {
local pane_id="$1"
[ -f "$(pane_contents_file "restore" "$pane_id")" ]
}
pane_contents_archive_file() {
echo "$(resurrect_dir)/pane_contents.tar.gz"
}
resurrect_history_file() {
local pane_id="$1"
local shell_name="$2"
echo "$(resurrect_dir)/${shell_name}_history-${pane_id}"
}
execute_hook() {
local kind="$1"
shift
local args="" hook=""
hook=$(get_tmux_option "$hook_prefix$kind" "")
# If there are any args, pass them to the hook (in a way that preserves/copes
# with spaces and unusual characters.
if [ "$#" -gt 0 ]; then
printf -v args "%q " "$@"
fi
if [ -n "$hook" ]; then
eval "$hook $args"
fi
}

View File

@ -0,0 +1,172 @@
restore_pane_processes_enabled() {
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
if [ "$restore_processes" == "false" ]; then
return 1
else
return 0
fi
}
restore_pane_process() {
local pane_full_command="$1"
local session_name="$2"
local window_number="$3"
local pane_index="$4"
local dir="$5"
local command
if _process_should_be_restored "$pane_full_command" "$session_name" "$window_number" "$pane_index"; then
tmux switch-client -t "${session_name}:${window_number}"
tmux select-pane -t "$pane_index"
local inline_strategy="$(_get_inline_strategy "$pane_full_command")" # might not be defined
if [ -n "$inline_strategy" ]; then
# inline strategy exists
# check for additional "expansion" of inline strategy, e.g. `vim` to `vim -S`
if _strategy_exists "$inline_strategy"; then
local strategy_file="$(_get_strategy_file "$inline_strategy")"
local inline_strategy="$($strategy_file "$pane_full_command" "$dir")"
fi
command="$inline_strategy"
elif _strategy_exists "$pane_full_command"; then
local strategy_file="$(_get_strategy_file "$pane_full_command")"
local strategy_command="$($strategy_file "$pane_full_command" "$dir")"
command="$strategy_command"
else
# just invoke the raw command
command="$pane_full_command"
fi
tmux send-keys -t "${session_name}:${window_number}.${pane_index}" "$command" "C-m"
fi
}
# private functions below
_process_should_be_restored() {
local pane_full_command="$1"
local session_name="$2"
local window_number="$3"
local pane_index="$4"
if is_pane_registered_as_existing "$session_name" "$window_number" "$pane_index"; then
# Scenario where pane existed before restoration, so we're not
# restoring the proces either.
return 1
elif ! pane_exists "$session_name" "$window_number" "$pane_index"; then
# pane number limit exceeded, pane does not exist
return 1
elif _restore_all_processes; then
return 0
elif _process_on_the_restore_list "$pane_full_command"; then
return 0
else
return 1
fi
}
_restore_all_processes() {
local restore_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
if [ "$restore_processes" == ":all:" ]; then
return 0
else
return 1
fi
}
_process_on_the_restore_list() {
local pane_full_command="$1"
# TODO: make this work without eval
eval set $(_restore_list)
local proc
local match
for proc in "$@"; do
match="$(_get_proc_match_element "$proc")"
if _proc_matches_full_command "$pane_full_command" "$match"; then
return 0
fi
done
return 1
}
_proc_matches_full_command() {
local pane_full_command="$1"
local match="$2"
if _proc_starts_with_tildae "$match"; then
match="$(remove_first_char "$match")"
# regex matching the command makes sure `$match` string is somewhere in the command string
if [[ "$pane_full_command" =~ ($match) ]]; then
return 0
fi
else
# regex matching the command makes sure process is a "word"
if [[ "$pane_full_command" =~ (^${match} ) ]] || [[ "$pane_full_command" =~ (^${match}$) ]]; then
return 0
fi
fi
return 1
}
_get_proc_match_element() {
echo "$1" | sed "s/${inline_strategy_token}.*//"
}
_get_proc_restore_element() {
echo "$1" | sed "s/.*${inline_strategy_token}//"
}
_restore_list() {
local user_processes="$(get_tmux_option "$restore_processes_option" "$restore_processes")"
local default_processes="$(get_tmux_option "$default_proc_list_option" "$default_proc_list")"
if [ -z "$user_processes" ]; then
# user didn't define any processes
echo "$default_processes"
else
echo "$default_processes $user_processes"
fi
}
_proc_starts_with_tildae() {
[[ "$1" =~ (^~) ]]
}
_get_inline_strategy() {
local pane_full_command="$1"
# TODO: make this work without eval
eval set $(_restore_list)
local proc
local match
for proc in "$@"; do
if [[ "$proc" =~ "$inline_strategy_token" ]]; then
match="$(_get_proc_match_element "$proc")"
if _proc_matches_full_command "$pane_full_command" "$match"; then
echo "$(_get_proc_restore_element "$proc")"
fi
fi
done
}
_strategy_exists() {
local pane_full_command="$1"
local strategy="$(_get_command_strategy "$pane_full_command")"
if [ -n "$strategy" ]; then # strategy set?
local strategy_file="$(_get_strategy_file "$pane_full_command")"
[ -e "$strategy_file" ] # strategy file exists?
else
return 1
fi
}
_get_command_strategy() {
local pane_full_command="$1"
local command="$(_just_command "$pane_full_command")"
get_tmux_option "${restore_process_strategy_option}${command}" ""
}
_just_command() {
echo "$1" | cut -d' ' -f1
}
_get_strategy_file() {
local pane_full_command="$1"
local strategy="$(_get_command_strategy "$pane_full_command")"
local command="$(_just_command "$pane_full_command")"
echo "$CURRENT_DIR/../strategies/${command}_${strategy}.sh"
}

View File

@ -0,0 +1,14 @@
#!/usr/bin/env expect
# start tmux
spawn tmux -S/tmp/foo
# delay with sleep to compensate for tmux starting time
sleep 2
# run restore script directly
send "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh\r"
# long wait until tmux restore is complete
# (things get messed up if expect client isn't attached)
sleep 100

View File

@ -0,0 +1,369 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/process_restore_helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
# delimiter
d=$'\t'
# Global variable.
# Used during the restore: if a pane already exists from before, it is
# saved in the array in this variable. Later, process running in existing pane
# is also not restored. That makes the restoration process more idempotent.
EXISTING_PANES_VAR=""
RESTORING_FROM_SCRATCH="false"
RESTORE_PANE_CONTENTS="false"
is_line_type() {
local line_type="$1"
local line="$2"
echo "$line" |
\grep -q "^$line_type"
}
check_saved_session_exists() {
local resurrect_file="$(last_resurrect_file)"
if [ ! -f $resurrect_file ]; then
display_message "Tmux resurrect file not found!"
return 1
fi
}
pane_exists() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
tmux list-panes -t "${session_name}:${window_number}" -F "#{pane_index}" 2>/dev/null |
\grep -q "^$pane_index$"
}
register_existing_pane() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
local pane_custom_id="${session_name}:${window_number}:${pane_index}"
local delimiter=$'\t'
EXISTING_PANES_VAR="${EXISTING_PANES_VAR}${delimiter}${pane_custom_id}"
}
is_pane_registered_as_existing() {
local session_name="$1"
local window_number="$2"
local pane_index="$3"
local pane_custom_id="${session_name}:${window_number}:${pane_index}"
[[ "$EXISTING_PANES_VAR" =~ "$pane_custom_id" ]]
}
restore_from_scratch_true() {
RESTORING_FROM_SCRATCH="true"
}
is_restoring_from_scratch() {
[ "$RESTORING_FROM_SCRATCH" == "true" ]
}
restore_pane_contents_true() {
RESTORE_PANE_CONTENTS="true"
}
is_restoring_pane_contents() {
[ "$RESTORE_PANE_CONTENTS" == "true" ]
}
window_exists() {
local session_name="$1"
local window_number="$2"
tmux list-windows -t "$session_name" -F "#{window_index}" 2>/dev/null |
\grep -q "^$window_number$"
}
session_exists() {
local session_name="$1"
tmux has-session -t "$session_name" 2>/dev/null
}
first_window_num() {
tmux show -gv base-index
}
tmux_socket() {
echo $TMUX | cut -d',' -f1
}
# Tmux option stored in a global variable so that we don't have to "ask"
# tmux server each time.
cache_tmux_default_command() {
local default_shell="$(get_tmux_option "default-shell" "")"
export TMUX_DEFAULT_COMMAND="$(get_tmux_option "default-command" "$default_shell")"
}
tmux_default_command() {
echo "$TMUX_DEFAULT_COMMAND"
}
pane_creation_command() {
echo "cat '$(pane_contents_file "restore" "${1}:${2}.${3}")'; exec $(tmux_default_command)"
}
new_window() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
local pane_index="$5"
local pane_id="${session_name}:${window_number}.${pane_index}"
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
tmux new-window -d -t "${session_name}:${window_number}" -n "$window_name" -c "$dir" "$pane_creation_command"
else
tmux new-window -d -t "${session_name}:${window_number}" -n "$window_name" -c "$dir"
fi
}
new_session() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
local pane_index="$5"
local pane_id="${session_name}:${window_number}.${pane_index}"
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -n "$window_name" -c "$dir" "$pane_creation_command"
else
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_name" -n "$window_name" -c "$dir"
fi
# change first window number if necessary
local created_window_num="$(first_window_num)"
if [ $created_window_num -ne $window_number ]; then
tmux move-window -s "${session_name}:${created_window_num}" -t "${session_name}:${window_number}"
fi
}
new_pane() {
local session_name="$1"
local window_number="$2"
local window_name="$3"
local dir="$4"
local pane_index="$5"
local pane_id="${session_name}:${window_number}.${pane_index}"
if is_restoring_pane_contents && pane_contents_file_exists "$pane_id"; then
local pane_creation_command="$(pane_creation_command "$session_name" "$window_number" "$pane_index")"
tmux split-window -t "${session_name}:${window_number}" -c "$dir" "$pane_creation_command"
else
tmux split-window -t "${session_name}:${window_number}" -c "$dir"
fi
# minimize window so more panes can fit
tmux resize-pane -t "${session_name}:${window_number}" -U "999"
}
restore_pane() {
local pane="$1"
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_full_command; do
dir="$(remove_first_char "$dir")"
window_name="$(remove_first_char "$window_name")"
pane_full_command="$(remove_first_char "$pane_full_command")"
if pane_exists "$session_name" "$window_number" "$pane_index"; then
tmux rename-window -t "$window_number" "$window_name"
if is_restoring_from_scratch; then
# overwrite the pane
# happens only for the first pane if it's the only registered pane for the whole tmux server
local pane_id="$(tmux display-message -p -F "#{pane_id}" -t "$session_name:$window_number")"
new_pane "$session_name" "$window_number" "$window_name" "$dir" "$pane_index"
tmux kill-pane -t "$pane_id"
else
# Pane exists, no need to create it!
# Pane existence is registered. Later, its process also won't be restored.
register_existing_pane "$session_name" "$window_number" "$pane_index"
fi
elif window_exists "$session_name" "$window_number"; then
tmux rename-window -t "$window_number" "$window_name"
new_pane "$session_name" "$window_number" "$window_name" "$dir" "$pane_index"
elif session_exists "$session_name"; then
new_window "$session_name" "$window_number" "$window_name" "$dir" "$pane_index"
else
new_session "$session_name" "$window_number" "$window_name" "$dir" "$pane_index"
fi
done < <(echo "$pane")
}
restore_state() {
local state="$1"
echo "$state" |
while IFS=$d read line_type client_session client_last_session; do
tmux switch-client -t "$client_last_session"
tmux switch-client -t "$client_session"
done
}
restore_grouped_session() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window active_window; do
TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$grouped_session" -t "$original_session"
done
}
restore_active_and_alternate_windows_for_grouped_sessions() {
local grouped_session="$1"
echo "$grouped_session" |
while IFS=$d read line_type grouped_session original_session alternate_window_index active_window_index; do
alternate_window_index="$(remove_first_char "$alternate_window_index")"
active_window_index="$(remove_first_char "$active_window_index")"
if [ -n "$alternate_window_index" ]; then
tmux switch-client -t "${grouped_session}:${alternate_window_index}"
fi
if [ -n "$active_window_index" ]; then
tmux switch-client -t "${grouped_session}:${active_window_index}"
fi
done
}
never_ever_overwrite() {
local overwrite_option_value="$(get_tmux_option "$overwrite_option" "")"
[ -n "$overwrite_option_value" ]
}
detect_if_restoring_from_scratch() {
if never_ever_overwrite; then
return
fi
local total_number_of_panes="$(tmux list-panes -a | wc -l | sed 's/ //g')"
if [ "$total_number_of_panes" -eq 1 ]; then
restore_from_scratch_true
fi
}
detect_if_restoring_pane_contents() {
if capture_pane_contents_option_on; then
cache_tmux_default_command
restore_pane_contents_true
fi
}
# functions called from main (ordered)
restore_all_panes() {
detect_if_restoring_from_scratch # sets a global variable
detect_if_restoring_pane_contents # sets a global variable
if is_restoring_pane_contents; then
pane_content_files_restore_from_archive
fi
while read line; do
if is_line_type "pane" "$line"; then
restore_pane "$line"
fi
done < $(last_resurrect_file)
if is_restoring_pane_contents; then
rm "$(pane_contents_dir "restore")"/*
fi
}
restore_pane_layout_for_each_window() {
\grep '^window' $(last_resurrect_file) |
while IFS=$d read line_type session_name window_number window_active window_flags window_layout; do
tmux select-layout -t "${session_name}:${window_number}" "$window_layout"
done
}
restore_shell_history() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ { print $2, $3, $7, $10; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index pane_command; do
if ! is_pane_registered_as_existing "$session_name" "$window_number" "$pane_index"; then
local pane_id="$session_name:$window_number.$pane_index"
local history_file="$(resurrect_history_file "$pane_id" "$pane_command")"
if [ "$pane_command" = "bash" ]; then
local read_command="history -r '$history_file'"
tmux send-keys -t "$pane_id" "$read_command" C-m
elif [ "$pane_command" = "zsh" ]; then
local accept_line="$(expr "$(zsh -i -c bindkey | grep -m1 '\saccept-line$')" : '^"\(.*\)".*')"
local read_command="fc -R '$history_file'; clear"
tmux send-keys -t "$pane_id" "$read_command" "$accept_line"
fi
fi
done
}
restore_all_pane_processes() {
if restore_pane_processes_enabled; then
local pane_full_command
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $11 !~ "^:$" { print $2, $3, $7, $8, $11; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number pane_index dir pane_full_command; do
dir="$(remove_first_char "$dir")"
pane_full_command="$(remove_first_char "$pane_full_command")"
restore_pane_process "$pane_full_command" "$session_name" "$window_number" "$pane_index" "$dir"
done
fi
}
restore_active_pane_for_each_window() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $9 == 1 { print $2, $3, $7; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number active_pane; do
tmux switch-client -t "${session_name}:${window_number}"
tmux select-pane -t "$active_pane"
done
}
restore_zoomed_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^pane/ && $6 ~ /Z/ && $9 == 1 { print $2, $3; }' $(last_resurrect_file) |
while IFS=$d read session_name window_number; do
tmux resize-pane -t "${session_name}:${window_number}" -Z
done
}
restore_grouped_sessions() {
while read line; do
if is_line_type "grouped_session" "$line"; then
restore_grouped_session "$line"
restore_active_and_alternate_windows_for_grouped_sessions "$line"
fi
done < $(last_resurrect_file)
}
restore_active_and_alternate_windows() {
awk 'BEGIN { FS="\t"; OFS="\t" } /^window/ && $5 ~ /[*-]/ { print $2, $4, $3; }' $(last_resurrect_file) |
sort -u |
while IFS=$d read session_name active_window window_number; do
tmux switch-client -t "${session_name}:${window_number}"
done
}
restore_active_and_alternate_sessions() {
while read line; do
if is_line_type "state" "$line"; then
restore_state "$line"
fi
done < $(last_resurrect_file)
}
main() {
if supported_tmux_version_ok && check_saved_session_exists; then
start_spinner "Restoring..." "Tmux restore complete!"
execute_hook "pre-restore-all"
restore_all_panes
restore_pane_layout_for_each_window >/dev/null 2>&1
execute_hook "pre-restore-history"
if save_shell_history_option_on; then
restore_shell_history
fi
execute_hook "pre-restore-pane-processes"
restore_all_pane_processes
# below functions restore exact cursor positions
restore_active_pane_for_each_window
restore_zoomed_windows
restore_grouped_sessions # also restores active and alt windows for grouped sessions
restore_active_and_alternate_windows
restore_active_and_alternate_sessions
execute_hook "post-restore-all"
stop_spinner
display_message "Tmux restore complete!"
fi
}
main

View File

@ -0,0 +1,322 @@
#!/usr/bin/env bash
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$CURRENT_DIR/variables.sh"
source "$CURRENT_DIR/helpers.sh"
source "$CURRENT_DIR/spinner_helpers.sh"
# delimiters
d=$'\t'
delimiter=$'\t'
# if "quiet" script produces no output
SCRIPT_OUTPUT="$1"
grouped_sessions_format() {
local format
format+="#{session_grouped}"
format+="${delimiter}"
format+="#{session_group}"
format+="${delimiter}"
format+="#{session_id}"
format+="${delimiter}"
format+="#{session_name}"
echo "$format"
}
pane_format() {
local format
format+="pane"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+=":#{window_name}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{pane_index}"
format+="${delimiter}"
format+=":#{pane_current_path}"
format+="${delimiter}"
format+="#{pane_active}"
format+="${delimiter}"
format+="#{pane_current_command}"
format+="${delimiter}"
format+="#{pane_pid}"
format+="${delimiter}"
format+="#{history_size}"
echo "$format"
}
window_format() {
local format
format+="window"
format+="${delimiter}"
format+="#{session_name}"
format+="${delimiter}"
format+="#{window_index}"
format+="${delimiter}"
format+="#{window_active}"
format+="${delimiter}"
format+=":#{window_flags}"
format+="${delimiter}"
format+="#{window_layout}"
echo "$format"
}
state_format() {
local format
format+="state"
format+="${delimiter}"
format+="#{client_session}"
format+="${delimiter}"
format+="#{client_last_session}"
echo "$format"
}
dump_panes_raw() {
tmux list-panes -a -F "$(pane_format)"
}
dump_windows_raw(){
tmux list-windows -a -F "$(window_format)"
}
toggle_window_zoom() {
local target="$1"
tmux resize-pane -Z -t "$target"
}
_save_command_strategy_file() {
local save_command_strategy="$(get_tmux_option "$save_command_strategy_option" "$default_save_command_strategy")"
local strategy_file="$CURRENT_DIR/../save_command_strategies/${save_command_strategy}.sh"
local default_strategy_file="$CURRENT_DIR/../save_command_strategies/${default_save_command_strategy}.sh"
if [ -e "$strategy_file" ]; then # strategy file exists?
echo "$strategy_file"
else
echo "$default_strategy_file"
fi
}
pane_full_command() {
local pane_pid="$1"
local strategy_file="$(_save_command_strategy_file)"
# execute strategy script to get pane full command
$strategy_file "$pane_pid"
}
number_nonempty_lines_on_screen() {
local pane_id="$1"
tmux capture-pane -pJ -t "$pane_id" |
sed '/^$/d' |
wc -l |
sed 's/ //g'
}
# tests if there was any command output in the current pane
pane_has_any_content() {
local pane_id="$1"
local history_size="$(tmux display -p -t "$pane_id" -F "#{history_size}")"
local cursor_y="$(tmux display -p -t "$pane_id" -F "#{cursor_y}")"
# doing "cheap" tests first
[ "$history_size" -gt 0 ] || # history has any content?
[ "$cursor_y" -gt 0 ] || # cursor not in first line?
[ "$(number_nonempty_lines_on_screen "$pane_id")" -gt 1 ]
}
capture_pane_contents() {
local pane_id="$1"
local start_line="-$2"
local pane_contents_area="$3"
if pane_has_any_content "$pane_id"; then
if [ "$pane_contents_area" = "visible" ]; then
start_line="0"
fi
# the printf hack below removes *trailing* empty lines
printf '%s\n' "$(tmux capture-pane -epJ -S "$start_line" -t "$pane_id")" > "$(pane_contents_file "save" "$pane_id")"
fi
}
save_shell_history() {
if [ "$pane_command" = "bash" ]; then
local history_w='history -w'
local history_r='history -r'
local accept_line='C-m'
local end_of_line='C-e'
local backward_kill_line='C-u'
elif [ "$pane_command" = "zsh" ]; then
# fc -W does not work with -L
# fc -l format is different from what's written by fc -W
# fc -R either reads the format produced by fc -W or considers
# the entire line to be a command. That's why we need -n.
# fc -l only list the last 16 items by default, I think 64 is more reasonable.
local history_w='fc -lLn -64 >'
local history_r='fc -R'
local zsh_bindkey="$(zsh -i -c bindkey)"
local accept_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\saccept-line$')" : '^"\(.*\)".*')"
local end_of_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\send-of-line$')" : '^"\(.*\)".*')"
local backward_kill_line="$(expr "$(echo "$zsh_bindkey" | grep -m1 '\sbackward-kill-line$')" : '^"\(.*\)".*')"
else
return
fi
local pane_id="$1"
local pane_command="$2"
local full_command="$3"
if [ "$full_command" = ":" ]; then
# leading space prevents the command from being saved to history
# (assuming default HISTCONTROL settings)
local write_command=" $history_w '$(resurrect_history_file "$pane_id" "$pane_command")'"
local read_command=" $history_r '$(resurrect_history_file "$pane_id" "$pane_command")'"
# C-e C-u is a Bash shortcut sequence to clear whole line. It is necessary to
# delete any pending input so it does not interfere with our history command.
tmux send-keys -t "$pane_id" "$end_of_line" "$backward_kill_line" "$write_command" "$accept_line"
# Immediately restore after saving
tmux send-keys -t "$pane_id" "$end_of_line" "$backward_kill_line" "$read_command" "$accept_line"
fi
}
get_active_window_index() {
local session_name="$1"
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
awk '$1 ~ /\*/ { print $2; }'
}
get_alternate_window_index() {
local session_name="$1"
tmux list-windows -t "$session_name" -F "#{window_flags} #{window_index}" |
awk '$1 ~ /-/ { print $2; }'
}
dump_grouped_sessions() {
local current_session_group=""
local original_session
tmux list-sessions -F "$(grouped_sessions_format)" |
grep "^1" |
cut -c 3- |
sort |
while IFS=$d read session_group session_id session_name; do
if [ "$session_group" != "$current_session_group" ]; then
# this session is the original/first session in the group
original_session="$session_name"
current_session_group="$session_group"
else
# this session "points" to the original session
active_window_index="$(get_active_window_index "$session_name")"
alternate_window_index="$(get_alternate_window_index "$session_name")"
echo "grouped_session${d}${session_name}${d}${original_session}${d}:${alternate_window_index}${d}:${active_window_index}"
fi
done
}
fetch_and_dump_grouped_sessions(){
local grouped_sessions_dump="$(dump_grouped_sessions)"
get_grouped_sessions "$grouped_sessions_dump"
if [ -n "$grouped_sessions_dump" ]; then
echo "$grouped_sessions_dump"
fi
}
# translates pane pid to process command running inside a pane
dump_panes() {
local full_command
dump_panes_raw |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size; do
# not saving panes from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
full_command="$(pane_full_command $pane_pid)"
dir=$(echo $dir | sed 's/ /\\ /') # escape all spaces in directory path
echo "${line_type}${d}${session_name}${d}${window_number}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${pane_index}${d}${dir}${d}${pane_active}${d}${pane_command}${d}:${full_command}"
done
}
dump_windows() {
dump_windows_raw |
while IFS=$d read line_type session_name window_index window_active window_flags window_layout; do
# not saving windows from grouped sessions
if is_session_grouped "$session_name"; then
continue
fi
echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_active}${d}${window_flags}${d}${window_layout}"
done
}
dump_state() {
tmux display-message -p "$(state_format)"
}
dump_pane_contents() {
local pane_contents_area="$(get_tmux_option "$pane_contents_area_option" "$default_pane_contents_area")"
dump_panes_raw |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command pane_pid history_size; do
capture_pane_contents "${session_name}:${window_number}.${pane_index}" "$history_size" "$pane_contents_area"
done
}
dump_shell_history() {
dump_panes |
while IFS=$d read line_type session_name window_number window_name window_active window_flags pane_index dir pane_active pane_command full_command; do
save_shell_history "$session_name:$window_number.$pane_index" "$pane_command" "$full_command"
done
}
remove_old_backups() {
# remove resurrect files older than 30 days, but keep at least 5 copies of backup.
local -a files
files=($(ls -t $(resurrect_dir)/${RESURRECT_FILE_PREFIX}_*.${RESURRECT_FILE_EXTENSION} | tail -n +6))
[[ ${#files[@]} -eq 0 ]] ||
find "${files[@]}" -type f -mtime +30 -exec rm -v "{}" \;
}
save_all() {
local resurrect_file_path="$(resurrect_file_path)"
local last_resurrect_file="$(last_resurrect_file)"
mkdir -p "$(resurrect_dir)"
fetch_and_dump_grouped_sessions > "$resurrect_file_path"
dump_panes >> "$resurrect_file_path"
dump_windows >> "$resurrect_file_path"
dump_state >> "$resurrect_file_path"
execute_hook "post-save-layout" "$resurrect_file_path"
if files_differ "$resurrect_file_path" "$last_resurrect_file"; then
ln -fs "$(basename "$resurrect_file_path")" "$last_resurrect_file"
else
rm "$resurrect_file_path"
fi
if capture_pane_contents_option_on; then
mkdir -p "$(pane_contents_dir "save")"
dump_pane_contents
pane_contents_create_archive
rm "$(pane_contents_dir "save")"/*
fi
if save_shell_history_option_on; then
dump_shell_history
fi
remove_old_backups
execute_hook "post-save-all"
}
show_output() {
[ "$SCRIPT_OUTPUT" != "quiet" ]
}
main() {
if supported_tmux_version_ok; then
if show_output; then
start_spinner "Saving..." "Tmux environment saved!"
fi
save_all
if show_output; then
stop_spinner
display_message "Tmux environment saved!"
fi
fi
}
main

View File

@ -0,0 +1,8 @@
start_spinner() {
$CURRENT_DIR/tmux_spinner.sh "$1" "$2" &
export SPINNER_PID=$!
}
stop_spinner() {
kill $SPINNER_PID
}

View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# This script shows tmux spinner with a message. It is intended to be running
# as a background process which should be `kill`ed at the end.
#
# Example usage:
#
# ./tmux_spinner.sh "Working..." "End message!" &
# SPINNER_PID=$!
# ..
# .. execute commands here
# ..
# kill $SPINNER_PID # Stops spinner and displays 'End message!'
MESSAGE="$1"
END_MESSAGE="$2"
SPIN='-\|/'
trap "tmux display-message '$END_MESSAGE'; exit" SIGINT SIGTERM
main() {
local i=0
while true; do
i=$(( (i+1) %4 ))
tmux display-message " ${SPIN:$i:1} $MESSAGE"
sleep 0.1
done
}
main

View File

@ -0,0 +1,47 @@
# key bindings
default_save_key="C-s"
save_option="@resurrect-save"
save_path_option="@resurrect-save-script-path"
default_restore_key="C-r"
restore_option="@resurrect-restore"
restore_path_option="@resurrect-restore-script-path"
# default processes that are restored
default_proc_list_option="@resurrect-default-processes"
default_proc_list='vi vim nvim emacs man less more tail top htop irssi weechat mutt'
# User defined processes that are restored
# 'false' - nothing is restored
# ':all:' - all processes are restored
#
# user defined list of programs that are restored:
# 'my_program foo another_program'
restore_processes_option="@resurrect-processes"
restore_processes=""
# Defines part of the user variable. Example usage:
# set -g @resurrect-strategy-vim "session"
restore_process_strategy_option="@resurrect-strategy-"
inline_strategy_token="->"
save_command_strategy_option="@resurrect-save-command-strategy"
default_save_command_strategy="ps"
# Pane contents capture options.
# @resurrect-pane-contents-area option can be:
# 'visible' - capture only the visible pane area
# 'full' - capture the full pane contents
pane_contents_option="@resurrect-capture-pane-contents"
pane_contents_area_option="@resurrect-pane-contents-area"
default_pane_contents_area="full"
bash_history_option="@resurrect-save-bash-history" # deprecated
shell_history_option="@resurrect-save-shell-history"
# set to 'on' to ensure panes are never ever overwritten
overwrite_option="@resurrect-never-overwrite"
# Hooks are set via ${hook_prefix}${name}, i.e. "@resurrect-hook-post-save-all"
hook_prefix="@resurrect-hook-"

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# "irb default strategy"
#
# Example irb process with junk variables:
# irb RBENV_VERSION=1.9.3-p429 GREP_COLOR=34;47 TERM_PROGRAM=Apple_Terminal
#
# When executed, the above will fail. This strategy handles that.
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
original_command_wo_junk_vars() {
echo "$ORIGINAL_COMMAND" |
sed 's/RBENV_VERSION[^ ]*//' |
sed 's/GREP_COLOR[^ ]*//' |
sed 's/TERM_PROGRAM[^ ]*//'
}
main() {
echo "$(original_command_wo_junk_vars)"
}
main

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# "mosh-client default strategy"
#
# Example mosh-client process:
# mosh-client -# charm tmux at | 198.199.104.142 60001
#
# When executed, the above will fail. This strategy handles that.
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
mosh_command() {
local args="$ORIGINAL_COMMAND"
args="${args#*-#}"
args="${args%|*}"
echo "mosh $args"
}
main() {
echo "$(mosh_command)"
}
main

View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
# "nvim session strategy"
#
# Same as vim strategy, see file 'vim_session.sh'
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
nvim_session_file_exists() {
[ -e "${DIRECTORY}/Session.vim" ]
}
original_command_contains_session_flag() {
[[ "$ORIGINAL_COMMAND" =~ "-S" ]]
}
main() {
if nvim_session_file_exists; then
echo "nvim -S"
elif original_command_contains_session_flag; then
# Session file does not exist, yet the original nvim command contains
# session flag `-S`. This will cause an error, so we're falling back to
# starting plain nvim.
echo "nvim"
else
echo "$ORIGINAL_COMMAND"
fi
}
main

View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
# "vim session strategy"
#
# Restores a vim session from 'Session.vim' file, if it exists.
# If 'Session.vim' does not exist, it falls back to invoking the original
# command (without the `-S` flag).
ORIGINAL_COMMAND="$1"
DIRECTORY="$2"
vim_session_file_exists() {
[ -e "${DIRECTORY}/Session.vim" ]
}
original_command_contains_session_flag() {
[[ "$ORIGINAL_COMMAND" =~ "-S" ]]
}
main() {
if vim_session_file_exists; then
echo "vim -S"
elif original_command_contains_session_flag; then
# Session file does not exist, yet the original vim command contains
# session flag `-S`. This will cause an error, so we're falling back to
# starting plain vim.
echo "vim"
else
echo "$ORIGINAL_COMMAND"
fi
}
main

View File

@ -0,0 +1,19 @@
Copyright (C) 2014 Bruno Sutic
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,74 @@
# Tmux Sidebar
`tmux-sidebar` does one thing: it opens a tree directory listing for the current
path. It's fast, convenient and works great with vim.
![screenshot](/screenshot.gif)
Some of the features that make the plugin more appealing than doing the same
thing manually each time:
- **fast**<br/>
Much faster than doing each step manually.
- **smart sizing**<br/>
Sidebar remembers its size, so the next time you open it, it will have the
**exact same** width. This is a per-directory property, so you can have just
the right size for multiple dirs.
- **toggling**<br/>
The same key binding opens and closes the sidebar.
- **uninterrupted workflow**<br/>
The main `prefix + Tab` key binding opens a sidebar but **does not** move
cursor to it.
- **pane layout stays the same**<br/>
No matter which pane layout you prefer, sidebar tries hard not to mess your
pane splits. Open, then close the sidebar and everything should look the same.
Requirements: `tmux 1.9` or higher, `tree` recommended but not required
Tested and working on Linux, OSX and Cygwin.
### Key bindings
- `prefix + Tab` - toggle sidebar with a directory tree
- `prefix + Backspace` - toggle sidebar and move cursor to it (focus it)
### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Add plugin to the list of TPM plugins in `.tmux.conf`:
set -g @plugin 'tmux-plugins/tmux-sidebar'
Hit `prefix + I` to fetch the plugin and source it. You should now be able to
use the plugin.
### Manual Installation
Clone the repo:
$ git clone https://github.com/tmux-plugins/tmux-sidebar ~/clone/path
Add this line to the bottom of `.tmux.conf`:
run-shell ~/clone/path/sidebar.tmux
Reload TMUX environment:
# type this in terminal
$ tmux source-file ~/.tmux.conf
You should now be able to use the plugin.
### Docs
- [customization options](docs/options.md)
### Other goodies
- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for
regex searches in tmux and fast match selection
- [tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) - restore
tmux environment after a system restart
### License
[MIT](LICENSE.md)

Some files were not shown because too many files have changed in this diff Show More