diff --git a/ansible/inventory/group_vars/all.yml b/ansible/inventory/group_vars/all.yml index 43f22ce..0879dac 100644 --- a/ansible/inventory/group_vars/all.yml +++ b/ansible/inventory/group_vars/all.yml @@ -8,6 +8,7 @@ user_shell: /bin/bash common_packages: - bash-completion - bat + - tmux - git - wget - unzip @@ -24,6 +25,14 @@ common_dotfiles: src: .bash_profile dest: .bash_profile mode: "0644" + - name: .tmux.conf + src: .tmux.conf + dest: .tmux.conf + mode: "0644" + - name: tmux config directory + src: .tmux/ + dest: .tmux/ + mode: preserve - name: bat config src: .config/bat/.config/bat/ dest: .config/bat/ diff --git a/dotfiles/common/.tmux.conf b/dotfiles/common/.tmux.conf new file mode 100644 index 0000000..f166554 --- /dev/null +++ b/dotfiles/common/.tmux.conf @@ -0,0 +1,118 @@ +#=== KEYBINDINGS ====# + +# change prefix +unbind C-b +set -g prefix C-a +bind C-a send-prefix + +# split panes using | and - +bind | split-window -h +bind - split-window -v +unbind '"' +unbind % + +# switch panes using Alt-arrow without prefix +bind -n M-Left select-pane -L +bind -n M-Right select-pane -R +bind -n M-Up select-pane -U +bind -n M-Down select-pane -D + +# switch windows using Alt- without prefix +bind-key -n M-1 select-window -t 1 +bind-key -n M-2 select-window -t 2 +bind-key -n M-3 select-window -t 3 +bind-key -n M-4 select-window -t 4 +bind-key -n M-5 select-window -t 5 + +# Other session shortcuts +bind S command-prompt -p "New Session:" "new-session -A -s '%%'" +bind K confirm kill-session +set-option -g detach-on-destroy off + +#=== LAYOUT ===# +set -g renumber-window on +set -g base-index 1 +setw -g pane-base-index 1 +bind c new-window -c '#{pane_current_path}' + +# Enable mouse control (clickable windows, panes, resizable panes) +set -g mouse on + +# Fix Tmux for colors +set -g default-terminal "tmux-256color" +set -ag terminal-features ',alacritty:RGB,foot:RGB,xterm-256color:RGB,gnome*:RGB' +set -g allow-passthrough on +set -ga update-environment TERM +set -ga update-environment TERM_PROGRAM + +#=== PLUGINS ===# +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'catppuccin/tmux' +set -g @plugin 'tmux-plugins/tmux-sensible' +set -g @plugin 'sei40kr/tmux-project' +set -g @plugin 'Alkindi42/tmux-bitwarden' +set-option -g @plugin 'b0o/tmux-autoreload' +set -g @plugin 'tmux-plugins/tmux-resurrect' +set -g @plugin 'tmux-plugins/tmux-continuum' + +# Configure Catppuccin +set -g @catppuccin_flavor "mocha" +set -g @catppuccin_status_background "none" +set -g @catppuccin_window_status_style "none" +set -g @catppuccin_pane_status_enabled "off" +set -g @catppuccin_pane_border_status "off" + +# status left look and feel +set -g status-left-length 100 +set -g status-left "" +set -ga status-left "#{?client_prefix,#{#[bg=#{@thm_red},fg=#{@thm_bg},bold]  #S },#{#[bg=#{@thm_bg},fg=#{@thm_green}]  #S }}" +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_overlay_0},none]│" +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_maroon}]  #{pane_current_command} " +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_overlay_0},none]│" +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_blue}]  #{=/-32/...:#{s|$USER|~|:#{b:pane_current_path}}} " +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_overlay_0},none]#{?window_zoomed_flag,│,}" +set -ga status-left "#[bg=#{@thm_bg},fg=#{@thm_yellow}]#{?window_zoomed_flag,  zoom ,}" + +# status right look and feel +set -g status-right-length 100 +set -g status-right "" +set -g status-right '#(gitmux "#{pane_current_path}") ' +set -ga status-right "#[bg=#{@thm_bg},fg=#{@thm_overlay_0}, none]│" +set -ga status-right "#[bg=#{@thm_bg},fg=#{@thm_blue}] #H" + +set -g status-position top +set -g status-style "bg=#{@thm_bg}" + +# pane border look and feel +setw -g pane-border-status top +setw -g pane-border-format "" +setw -g pane-active-border-style "bg=#{@thm_bg},fg=#{@thm_overlay_0}" +setw -g pane-border-style "bg=#{@thm_bg},fg=#{@thm_surface_0}" +setw -g pane-border-lines single + +# window look and feel +set -wg automatic-rename on +set -g automatic-rename-format "Window" +set -g window-status-format " #I#{?#{!=:#{window_name},Window},: #W,} " +set -g window-status-style "bg=#{@thm_bg},fg=#{@thm_rosewater}" +set -g window-status-last-style "bg=#{@thm_bg},fg=#{@thm_peach}" +set -g window-status-activity-style "bg=#{@thm_red},fg=#{@thm_bg}" +set -g window-status-bell-style "bg=#{@thm_red},fg=#{@thm_bg},bold" +set -gF window-status-separator "#[bg=#{@thm_bg},fg=#{@thm_overlay_0}]│" +set -g window-status-current-format " #I#{?#{!=:#{window_name},Window},: #W,} " +set -g window-status-current-style "bg=#{@thm_peach},fg=#{@thm_bg},bold" + +# Continuum options +set -g @continuum-restore 'on' + +# Project options +set -ag @project-base-dirs ,"${HOME}/.dotfiles","${HOME}/Projects:0:2" + +# Bitwarden plugin setup +set -g @bw-session 'BW_SESSION' +set -g @bw-copy-to-clipboard 'on' +set -g @bw-key 'T' + +# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) +run '~/.tmux/plugins/tpm/tpm' + diff --git a/dotfiles/common/.tmux/plugins/tmux-autoreload/.gitignore b/dotfiles/common/.tmux/plugins/tmux-autoreload/.gitignore new file mode 100644 index 0000000..3811c63 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-autoreload/.gitignore @@ -0,0 +1,139 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/linux,macos,windows,vim,git,zsh +# Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,windows,vim,git,zsh + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Zsh ### +# Zsh compiled script + zrecompile backup +*.zwc +*.zwc.old + +# Zsh completion-optimization dumpfile +*zcompdump* + +# Zsh zcalc history +.zcalc_history + +# A popular plugin manager's files +._zinit +.zinit_lstupd + +# zdharma/zshelldoc tool's files +zsdoc/data + +# robbyrussell/oh-my-zsh/plugins/per-directory-history plugin's files +# (when set-up to store the history in the local directory) +.directory_history + +# MichaelAquilina/zsh-autoswitch-virtualenv plugin's files +# (for Zsh plugins using Python) +.venv + +# Zunit tests' output +/tests/_output/* +!/tests/_output/.gitkeep + +# End of https://www.toptal.com/developers/gitignore/api/linux,macos,windows,vim,git,zsh diff --git a/dotfiles/common/.tmux/plugins/tmux-autoreload/LICENSE b/dotfiles/common/.tmux/plugins/tmux-autoreload/LICENSE new file mode 100644 index 0000000..5876a8b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-autoreload/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Maddison Hellstrom + +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. diff --git a/dotfiles/common/.tmux/plugins/tmux-autoreload/README.md b/dotfiles/common/.tmux/plugins/tmux-autoreload/README.md new file mode 100644 index 0000000..18ddc38 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-autoreload/README.md @@ -0,0 +1,166 @@ +# tmux-autoreload [![version](https://img.shields.io/github/v/tag/b0o/tmux-autoreload?style=flat&color=yellow&label=version&sort=semver)](https://github.com/b0o/tmux-autoreload/releases) [![license: MIT](https://img.shields.io/github/license/b0o/tmux-autoreload?style=flat&color=green)](https://mit-license.org) + +tmux-autoreload watches your tmux configuration file and automatically reloads +it on change. + +## Install + +#### Dependencies + +- [entr](https://github.com/eradman/entr) + +## Installation + +To install tmux-autoreload with TPM (https://github.com/tmux-plugins/tpm), add the +following line to the end of your tmux configuration file: + +```sh +set-option -g @plugin 'b0o/tmux-autoreload' +``` + +Then, inside tmux, press `prefix + I` to fetch the plugin. + +If you don't use a plugin manager, git clone tmux-autoreload to the location of your +choice and run it directly: + +```sh +run-shell "/path/to/tmux-autoreload/tmux-autoreload.tmux" +``` + +## Setup + +Once installed, you should be good to go unless you use non-standard +configuration file paths or want to customize how tmux-autoreload behaves. + +### Configuration file paths + +If your tmux config file is at a non-standard location or if you have multiple, +specify them in `@tmux-autoreload-configs`, separated by commas: + +```sh +set-option -g @tmux-autoreload-configs '/path/to/configs/a.conf,/path/to/configs/b.conf' +``` + +### Entrypoints + +Normally, tmux-autoreload will source whichever file changed. If you wish to +source a specific set of files when any configuration file changes, use +`@tmux-autoreload-entrypoints`: + +```sh +set-option -g @tmux-autoreload-entrypoints '/path/to/entrypoint.conf' +``` + +You can specify multiple entrypoints separated by commas. All entrypoints +will be sourced when any watched file changes. + +Set `@tmux-autoreload-entrypoints` to 1 to use the standard tmux configuration +files as entrypoints, usually `/etc/tmux.conf` and `~/.tmux.conf.` You can see +these files with: + +```sh +tmux display-message -p "#{config_files}" +``` + +#### Entrypoint Notes + +- If entrypoints are configured, a changed file itself will not necessarily + be reloaded unless it's an entrypoint or is sourced by an entrypoint. + +- Entrypoints will not be watched unless they're a standard tmux + configuration file like `~/.tmux.conf` or are included in `@tmux-autoreload-configs.` + +### All Options + +``` +@tmux-autoreload-configs (Default: unset) + A comma-delimited list of paths to configuration files which should be + watched in addition to the base tmux configuration files. + +@tmux-autoreload-entrypoints (Default: unset) + A comma-delimited list of paths to configuration files which should be + reloaded when any watched configuration file changes. If unset, the changed + file itself will be reloaded. + + If set, only the entrypoints will be reloaded, not necessarily the changed + file. + + If set to 1, the base tmux configuration files are used as the entrypoints + (you can see the base configuration files with the command tmux + display-message -p "#{config_files}"). + +@tmux-autoreload-quiet 0|1 (Default: 0) + If set to 1, tmux-autoreload will not display status messages. +``` + +## Advanced Usage + + + +``` + +Usage: tmux-autoreload.tmux [-f] [OPT...] + Automatically reloads your tmux configuration files on change. + +Options + -h Display usage information. + -v Display tmux-autoreload version and copyright information. + -f Run in foreground (do not fork). + -k Kill the running tmux-autoreload instance. + -s Show tmux-autoreload status + +Installation + To install tmux-autoreload with TPM (https://github.com/tmux-plugins/tpm), add the + following line to the end of your tmux configuration file: + set-option -g @plugin 'b0o/tmux-autoreload' + + If you don't use a plugin manager, git clone tmux-autoreload to the location of your + choice and run it directly: + run-shell "/path/to/tmux-autoreload/tmux-autoreload.tmux" + + Once installed, you should be good to go unless you use non-standard + configuration file paths or want to customize how tmux-autoreload behaves. + +Configuration file paths + If your config file is at a non-standard location or if you have multiple, + specify them in @tmux-autoreload-configs, separated by commas: + set-option -g @tmux-autoreload-configs '/path/to/configs/a.conf,/path/to/configs/b.conf' + +Entrypoints + Normally, tmux-autoreload will source whichever file changed. If you wish to + source a specific set of files when any configuration file changes, use + @tmux-autoreload-entrypoints: + set-option -g @tmux-autoreload-entrypoints '/path/to/entrypoint.conf' + + You can specify multiple entrypoints separated by commas. All entrypoints + will be sourced when any watched file changes. + + Set @tmux-autoreload-entrypoints to 1 to use the standard tmux configuration + files as entrypoints, usually /etc/tmux.conf and ~/.tmux.conf. You can see + these files with: + tmux display-message -p "#{config_files}". + + Entrypoint Notes: + - If entrypoints are configured, a changed file itself will not necessarily + be reloaded unless it's an entrypoint or is sourced by an entrypoint. + + - Entrypoints will not be watched unless they're a standard tmux + configuration file like ~/.tmux.conf or are included in @tmux-autoreload-configs. + +Other Options + @tmux-autoreload-quiet 0|1 (Default: 0) + If set to 1, tmux-autoreload will not display messages + +``` + + + +## License + + + +© 2021 Maddison Hellstrom + +Released under the MIT License. + + diff --git a/dotfiles/common/.tmux/plugins/tmux-autoreload/scripts/gendocs.sh b/dotfiles/common/.tmux/plugins/tmux-autoreload/scripts/gendocs.sh new file mode 100755 index 0000000..1d94ae4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-autoreload/scripts/gendocs.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# Copyright (C) 2020-2021 Maddison Hellstrom , MIT License. + +set -Eeuo pipefail +if [[ ${BASH_VERSINFO[0]} -ge 5 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 4) ]]; then + shopt -s inherit_errexit +fi + +declare -g self prog basedir reporoot +self="$(readlink -e "${BASH_SOURCE[0]}")" +prog="$(basename "$self")" +basedir="$(realpath -m "$self/..")" +reporoot="$(realpath -m "$basedir/..")" + +# gendocs configuration {{{ + +declare -g main="${reporoot}/tmux-autoreload.tmux" + +declare -gA targets=( + [readme]="$reporoot/README.md" +) + +declare -gi copyright_start=2021 + +function target_readme() { + section -s USAGE -c <<< "$("$main" -h 2>&1)" + section -s LICENSE << EOF +© ${copyright_start}$( (($(date +%Y) == copyright_start)) || date +-%Y) Maddison Hellstrom + +Released under the MIT License. +EOF +} + +# }}} + +declare -gA sections + +function section() { + local section + local -i code=0 + local lang + + local opt OPTARG + local -i OPTIND + while getopts "s:cC:" opt "$@"; do + case "$opt" in + s) + section="$OPTARG" + ;; + c) + code=1 + ;; + C) + code=1 + lang="$OPTARG" + ;; + \?) + return 1 + ;; + esac + done + shift $((OPTIND - 1)) + + local -a lines=('') + + if [[ $code -eq 1 ]]; then + lines+=('```'"${lang:-}" '') + fi + + mapfile -tO ${#lines[@]} lines + + if [[ $code -eq 1 ]]; then + lines+=('' '```') + fi + + sections["$section"]="$(printf '%s\n' "${lines[@]}")\n" +} + +function regen_section() { + local section="$1" + local content="${sections[$section]}" + awk < "$target" -v "section=$section" -v "content=$content" ' + BEGIN { + d = 0 + } + + { + if (match($0, "^$")) { + d = 1 + print $0 + print content + next + } + if (match($0, "^$")) { + d = 0 + print $0 + next + } + } + + d == 0 { + print $0 + } + ' +} + +function main() { + local opt OPTARG + local -i OPTIND + while getopts "h" opt "$@"; do + case "$opt" in + h) + echo "usage: $prog [opt].. [target].." >&2 + return 0 + ;; + \?) + return 1 + ;; + esac + done + shift $((OPTIND - 1)) + + local -a targets_selected=("${!targets[@]}") + + if [[ $# -gt 0 ]]; then + targets_selected=("$@") + fi + + local t target + for t in "${targets_selected[@]}"; do + [[ -v "targets[$t]" ]] || { + echo "unknown target: $t" >&2 + return 1 + } + target="${targets["$t"]}" + [[ -e "$target" ]] || { + echo "target file not found: $target" >&2 + return 1 + } + sections=() + "target_${t}" || { + echo "unknown target: $t" + return 1 + } + local s + for s in "${!sections[@]}"; do + regen_section "$s" > "${target}_" + mv "${target}_" "$target" + done + done +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-autoreload/tmux-autoreload.tmux b/dotfiles/common/.tmux/plugins/tmux-autoreload/tmux-autoreload.tmux new file mode 100755 index 0000000..0ab882d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-autoreload/tmux-autoreload.tmux @@ -0,0 +1,258 @@ +#!/usr/bin/env bash + +# Automatically reloads your tmux configuration files on change. +# +# Copyright 2021 Maddison Hellstrom , MIT License. + +set -Eeuo pipefail +if [[ ${BASH_VERSINFO[0]} -ge 5 || (${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 4) ]]; then + shopt -s inherit_errexit +fi + +function years_from() { + local from="$1" to + to="${2:-$(date +%Y)}" + if [[ "$from" == "$to" ]]; then + echo "$from" + else + echo "$from-$to" + fi +} + +declare -g self prog name +self="$(realpath -e "${BASH_SOURCE[0]}")" +prog="$(basename "$self")" +name="${prog%.tmux}" + +declare -gr version="0.0.1" +declare -gr authors=("$(years_from 2021) Maddison Hellstrom ") +declare -gr repo_short="b0o/$name" +declare -gr repository="https://github.com/$repo_short" +declare -gr issues="https://github.com/$repo_short/issues" +declare -gr license="MIT" +declare -gr license_url="https://mit-license.org" + +function usage() { + cat </dev/null; then + echo "$instance_pid" + return 0 + fi + return 1 +} + +function reload() { + local -a entrypoints + mapfile -t entrypoints < <(get_entrypoints) + if [[ ${#entrypoints[@]} -eq 0 ]]; then + entrypoints=("$@") + fi + if msg="$(tmux source-file "${entrypoints[@]}")"; then + display_message "Reloaded $( + printf '%s\n' "${entrypoints[@]}" | xargs -n1 basename | tr '\n' ',' | sed 's/,$/\n/; s/,/, /g' + )" + else + display_message -d 0 "#[fg=white,bg=red,bold]ERROR: $msg" + fi +} + +function onexit() { + local -i code=$? + local -i entr_pid=$1 + display_message "$name exited with code $code" || true + if [[ -v entr_pid && $entr_pid -gt 1 ]] && ps "$entr_pid" &>/dev/null; then + kill "$entr_pid" || true + fi + tmux set-option -gu "@$name-pid" & + return "$code" +} + +function kill_instance() { + local -i instance_pid + if instance_pid="$(get_instance)"; then + kill "$instance_pid" + return $? + fi + echo "$name -k: kill failed: no instance found" >&2 + return 1 +} + +function get_status() { + local -i instance_pid + if instance_pid="$(get_instance)"; then + echo "running: $instance_pid" + return 0 + fi + echo "not running" + return 1 +} + +function main() { + if ! [[ "${1:-}" =~ ^-[hvfksr]$ ]]; then + "$self" -f "$@" &>/dev/null & + disown + exit $? + fi + + local opt OPTARG + local -i OPTIND + while getopts "hvfksr:" opt "$@"; do + case "$opt" in + h) + usage + return 0 + ;; + v) + usage_version + return 0 + ;; + f) + # Silently ignore -f + ;; + k) + kill_instance + return + ;; + s) + get_status + return + ;; + r) + reload "$OPTARG" + return + ;; + \?) + return 1 + ;; + esac + done + shift $((OPTIND - 1)) + + if get_instance &>/dev/null; then + return 0 + fi + + command -v entr &>/dev/null || { + echo "Command not found: entr" >&2 + display_message -d 0 "Failed to start $name: Command not found: entr" + return 1 + } + + tmux set-option -g "@$name-pid" $$ + + # shellcheck disable=2016 + entr -np sh -c '"$0" -r "$1"' "$self" /_ <<<"$(printf '%s\n' "$(get_base_configs)" "$(get_user_configs)")" & + # shellcheck disable=2064 + trap "onexit $!" EXIT + wait +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-bitwarden/README.md b/dotfiles/common/.tmux/plugins/tmux-bitwarden/README.md new file mode 100644 index 0000000..ce80321 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-bitwarden/README.md @@ -0,0 +1,57 @@ +# tmux-bitwarden + +## Requirements +You need to have: +* [Bitwarden CLI](https://bitwarden.com/) +* [jq](https://stedolan.github.io/jq/) +* [fzf](https://github.com/junegunn/fzf) +* bash > 4.0 + +## Install + +### Tmux Plugin Manager (recommended) +1. In your `.tmux.conf` add the plugin to the list. +``` +set -g @plugin 'Alkindi42/tmux-bitwarden' +``` +2. Type `prefix + I` to install the plugin. + +## Key bindings +* `prefix + b`: list login items in a bottom pane. + +## Usage +First, log into your Bitwarden user account using the `login` command (you only need to do this once): +```bash +$ bw login your-email@domain.com +? Master password: [input is hidden] +To unlock your vault, set your session key to the `BW_SESSION` environment variable. ex: +$ export BW_SESSION="lpvf7Rt+pAMXW2YJ5O42jJp6ZY0Ny01vq9jaUdFYbroS1CXWgjVdy7j42owHVoLwZf+yDI+ro68Qngo9mdD/vA==" +> $env:BW_SESSION="lpvf7Rt+pAMXW2YJ5O42jJp6ZY0Ny01vq9jaUdFYbroS1CXWgjVdy7j42owHVoLwZf+yDI+ro68Qngo9mdD/vA==" +``` + +In a tmux session, you can run the plugin with the default key binding `prefix + b`. This opens a new pane at the bottom with login items. You can choose your login item with ``, your password will be automatically filled. + +If you have not configured your bitwarden session (`BW_SESSION`), you will be prompted to re-enter your master password before each selection (see configuration section for more information) + +## Configuration + +### Changing the default key-binding +``` +set -g @bw-key 'T' +``` +Default: `u` + +### Define Bitwarden session +To avoid re-entering your master password before each selection, you can define your [session Bitwarden](https://bitwarden.com/help/article/cli/#session-management). +Your `BW_SESSION` comes from the result of the `login` command. +``` +set -g @bw-session 'BW_SESSION' +``` +If the `BW_SESSION` variable exists in your environment variable then it will be used. + +### Copy the password to the clipboard +By default, after selection, the password is sent in the last pane. If you want to have it in your clipboard you have to activate the option: +``` +set -g @bw-copy-to-clipboard 'on' +``` +Default: `off` diff --git a/dotfiles/common/.tmux/plugins/tmux-bitwarden/plugin.tmux b/dotfiles/common/.tmux/plugins/tmux-bitwarden/plugin.tmux new file mode 100755 index 0000000..14b6c2a --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-bitwarden/plugin.tmux @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +declare -r CURRENT_DIR + +# shellcheck source=/dev/null +source "$CURRENT_DIR/scripts/utils.sh" + +declare -a REQUIRED_BINARIES=( + 'jq' + 'fzf' + 'bw' +) + +main() { + for binary in "${REQUIRED_BINARIES[@]}" + do + if ! is_binary_exist "$binary" + then + display_tmux_message "binary $binary does not exist" + return 1 + fi + done + + key_binding=$(get_tmux_option "@bw-key" "b") + tmux bind-key "$key_binding" split-window -l 10 "$CURRENT_DIR/scripts/main.sh" +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/main.sh b/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/main.sh new file mode 100755 index 0000000..2b87dcd --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/main.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +declare -r CURRENT_DIR + +# shellcheck source=/dev/null +source "$CURRENT_DIR/utils.sh" + +is_authenticated() { + [[ $(bw status | jq '.status') != "\"unauthenticated\"" ]] && true +} + +# Get bitwarden items +get_bw_items() { + local session="$1" + filter='map({ (.name|tostring): .login.password })|add' + + if [[ -z "$session" ]]; then + bw list items | jq -r "$filter" + else + bw list items --session "$session" | jq -r "$filter" + fi +} + +get_password() { + local items=$1 + local key=$2 + + password=$(echo "$items" | jq ".\"$key\"") + echo "${password:1:-1}" +} + +main() { + declare -A TMUX_OPTS=( + ["@bw-session"]=$(get_tmux_option "@bw-session" "$BW_SESSION") + ["@bw-copy-to-clipboard"]=$(get_tmux_option "@bw-copy-to-clipboard" "off") + ) + + is_authenticated + if [[ $? -eq 1 ]]; then + display_tmux_message "You are not logged in." + return 1 + fi + + items=$(get_bw_items "${TMUX_OPTS[@bw-session]}") + + # Choice element + key=$(echo "$items" | jq --raw-output '.|keys[]' | fzf --no-multi) || return + + password=$(get_password "$items" "$key") + + if [[ "${TMUX_OPTS[@bw-copy-to-clipboard]}" == "on" ]]; then + cp_to_clipboard "$password" + else + # Send the password in the last pane. + tmux send-keys -t ! "$password" + fi +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/utils.sh b/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/utils.sh new file mode 100644 index 0000000..bdb25c0 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-bitwarden/scripts/utils.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Copy text to the clipboard +cp_to_clipboard() { + if [[ "$(uname)" == "Darwin" ]] && is_binary_exist "pbcopy"; then + echo -n "$1" | pbcopy + elif [[ "$(uname)" == "Linux" ]] && is_binary_exist "wl-copy"; then + echo -n "$1" | wl-copy + elif [[ "$(uname)" == "Linux" ]] && is_binary_exist "xsel"; then + echo -n "$1" | xsel -b + elif [[ "$(uname)" == "Linux" ]] && is_binary_exist "xclip"; then + echo -n "$1" | xclip -i + else + return 1 + fi +} + +# Check if binary exist +is_binary_exist() { + local binary=$1 + + command -v "$binary" &> /dev/null + return $? +} + +# Get tmux option +get_tmux_option() { + local option="$1" + local default_value="$2" + local option_value + option_value=$(tmux show-option -gqv "$option") + + if [[ -z "$option_value" ]]; then + echo "$default_value" + else + echo "$option_value" + fi +} + +# Display tmux message in status bar +display_tmux_message() { + local message=$1 + tmux display-message "tmux-bitwarden: $message" +} diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/.gitattributes b/dotfiles/common/.tmux/plugins/tmux-continuum/.gitattributes new file mode 100644 index 0000000..4cde323 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/.gitattributes @@ -0,0 +1,2 @@ +# Force text files to have unix eols, so Windows/Cygwin does not break them +*.* eol=lf diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tmux-continuum/CHANGELOG.md new file mode 100644 index 0000000..2a56b2f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + +### master +- bugfix: "auto restore" feature stopped working +- bugfix: prevent race condition when auto-saving with locks (@v9v) +- Multiple users on a system can now each run continuum on their own. + +### v3.1.0, 2015-03-14 +- properly quote scripts +- bugfix: "auto restore" feature does not work on tmux `1.9a` +- bugfix: do not count `tmux source-file .tmux.conf` as a tmux process (when + checking if other tmux server is running). Previously, this caused + interpolation command not to be inserted into `status-right` because `tmux + source-file` was falsely detected as another tmux server. +- add `#{continuum_status}` status line interpolation + +### v3.0.0, 2015-02-20 +- rename the plugin from `tmux-resurrect-auto` to `tmux-continuum` + +### v2.2.0, 2015-02-20 +- document tmux multi-server behavior in the readme +- do not auto-restore tmux environment if another tmux server is already running + (we don't want to duplicate stuff) +- bugfixes for 'tmux auto start' OS X Terminal.app and iTerm scripts +- prevent saving for the first 15 minutes only when plugin is sourced the first + time (not on subsequent sources or tmux.conf reloads) +- do not start auto-saving if there's another tmux server running (we don't want + for save files from various tmux environments to override each other) + +### v2.1.0, 2015-02-18 +- enable "tmux auto start" for OS X +- enable customizing "tmux auto start" for OS X +- fix errors when creating a launchd plist file for auto-start on OS X + +### v2.0.0, 2015-02-15 +- enable automatic environment restore when tmux is started + +### v1.0.0, 2015-02-12 +- first working version +- run the save script in the background +- do not start saving right after tmux is started +- add a check for tmux version to the initializer script +- when interval is set to '0' autosave is disabled +- bugfix: helper files not loaded +- update readme with the instructions how to disable auto saving diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/CONTRIBUTING.md b/dotfiles/common/.tmux/plugins/tmux-continuum/CONTRIBUTING.md new file mode 100644 index 0000000..21c18fd --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/CONTRIBUTING.md @@ -0,0 +1,15 @@ +### Contributing + +Code contributions are welcome! + +If you wanna contribute a bigger feature, please open a github issue so we can +discuss it together first. + +### Reporting a bug + +If you find a bug please report it in the issues. When reporting a bug please +attach: +- a file symlinked to `~/.tmux/resurrect/last`. +- your `.tmux.conf` +- if you're getting an error paste it to a [gist](https://gist.github.com/) and + link it in the issue diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/LICENSE.md b/dotfiles/common/.tmux/plugins/tmux-continuum/LICENSE.md new file mode 100644 index 0000000..e6e7350 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/LICENSE.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/README.md b/dotfiles/common/.tmux/plugins/tmux-continuum/README.md new file mode 100644 index 0000000..d263261 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/README.md @@ -0,0 +1,103 @@ +# 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 an interval of 15 minutes. All the saving +happens in the background without impact to your workflow. + +This action starts automatically when the plugin is installed. Note it requires +the status line to be `on` to run (since it uses a hook in status-right to run). + +#### Automatic tmux start + +Tmux is automatically started after the computer/server is turned on. + +See the [instructions](docs/automatic_start.md) on 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 + +### Known Issues + +- In order to be executed periodically, the plugin updates the `status-right` tmux variable. In case some plugin (usually themes) overwrites the `status-right` variable, the autosave feature stops working. To fix this issue, place the plugin last in the TPM plugins list. + +### Reporting bugs and contributing + +Both contributing and bug reports are welcome. Please check out +[contributing guidelines](CONTRIBUTING.md). + +### License +[MIT](LICENSE.md) diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/continuum.tmux b/dotfiles/common/.tmux/plugins/tmux-continuum/continuum.tmux new file mode 100755 index 0000000..0a2490f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/continuum.tmux @@ -0,0 +1,89 @@ +#!/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}')" + local restore_max_delay + restore_max_delay="$(get_tmux_option "$auto_restore_max_delay_option" "${auto_restore_max_delay_default}")" + [ "$tmux_start_time" == "" ] || [ "$tmux_start_time" -gt "$(($(date +%s)-${restore_max_delay}))" ] +} + +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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/docs/automatic_start.md b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/automatic_start.md new file mode 100644 index 0000000..2b7e443 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/automatic_start.md @@ -0,0 +1,47 @@ +# Automatic Tmux start + +Tmux is automatically started after the computer/server is turned on. + +### OS X + +To enable this feature: +- put `set -g @continuum-boot 'on'` in `.tmux.conf` +- reload tmux config with this shell command: `$ tmux source-file ~/.tmux.conf` + +Next time the computer is started: +- `Terminal.app` window will open and resize to maximum size +- `tmux` command will be executed in the terminal window +- if "auto restore" feature is enabled, tmux will start restoring previous env + +Config options: +- `set -g @continuum-boot-options 'fullscreen'` - terminal window + will go fullscreen +- `set -g @continuum-boot-options 'iterm'` - start [iTerm](https://www.iterm2.com) instead + of `Terminal.app` +- `set -g @continuum-boot-options 'iterm,fullscreen'` - start `iTerm` + in fullscreen +- `set -g @continuum-boot-options 'kitty'` - start [kitty](https://sw.kovidgoyal.net/kitty) instead + of `Terminal.app` +- `set -g @continuum-boot-options 'kitty,fullscreen'` - start `kitty` + in fullscreen +- `set -g @continuum-boot-options 'alacritty'` - start [alacritty](https://github.com/alacritty/alacritty) instead of `Terminal.app` +- `set -g @continuum-boot-options 'alacritty,fullscreen'` - start `alacritty` + in fullscreen + +Note: The first time you reboot your machine and activate this feature you may be prompted about a script requiring +access to a system program (i.e. - System Events). If this happens tmux will not start automatically and you will need +to go to `System Preferences -> Security & Privacy -> Accessability` and add the script to the list of apps that are +allowed to control your computer. + +### Linux + +Help with this would be greatly appreciated. Please get in touch. + +#### Systemd + +##### this will only start the tmux server, it will *not* start any terminal emulator + +To enable automatic start with systemd: +- Put `set -g @continuum-boot 'on'` in `.tmux.conf` +- reload tmux config with this shell command: `$ tmux source-file ~/.tmux.conf` +- see [systemd](/docs/systemd_details.md) for more details about how this is implemented diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/docs/continuum_status.md b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/continuum_status.md new file mode 100644 index 0000000..c154ae4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/continuum_status.md @@ -0,0 +1,17 @@ +## Continuum status in tmux status line + +There is an option to display current status of tmux continuum in tmux status +line. This is done via `#{continuum_status}` interpolation and it works with +both `status-right` and `status-left` tmux native options. + +Example usage: + + set -g status-right 'Continuum status: #{continuum_status}' + +When running, `#{continuum_status}` will show continuum save interval: + + Continuum status: 15 + +or if continuous saving is disabled: + + Continuum status: off diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/docs/faq.md b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/faq.md new file mode 100644 index 0000000..42fd26d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/faq.md @@ -0,0 +1,45 @@ +### FAQ + +> Will a previous save be overwritten immediately after I start tmux? + +No, first automatic save starts 15 minutes after tmux is started. If automatic +restore is not enabled, that gives you enough time to manually restore from a +previous save. + +> I want to make a restore to a previous point in time, but it seems that save +is now overwritten? + +Read how to [restore a previously saved environment](https://github.com/tmux-plugins/tmux-resurrect/blob/master/docs/restoring_previously_saved_environment.md) + +> Will this plugin fill my hard disk? + +Most likely no. A regular save file is in the range of 5Kb. And `tmux-resurrect` already has a `remove_old_backups()` routine that will ["remove resurrect files older than 30 days, but keep at least 5 copies of backup."](https://github.com/tmux-plugins/tmux-resurrect/blob/da1a7558024b8552f7262b39ed22e3d679304f99/scripts/save.sh#L271-L277) + +> How do I change the save interval to i.e. 1 hour? + +The interval is always measured in minutes. So setting the interval to `60` +(minutes) will do the trick. Put this in `.tmux.conf`: + + set -g @continuum-save-interval '60' + +and then source `tmux.conf` by executing this command in the shell +`$ tmux source-file ~/.tmux.conf`. + +> How do I stop automatic saving? + +Just set the save interval to `0`. Put this in `.tmux.conf` + + set -g @continuum-save-interval '0' + +and then source `tmux.conf` by executing this command in the shell +`$ tmux source-file ~/.tmux.conf`. + +> I had automatic restore turned on, how do I disable it now? + +Just remove `set -g @continuum-restore 'on'` from `tmux.conf`. + +To be absolutely sure automatic restore doesn't happen, create a +`tmux_no_auto_restore` file in your home directory (command: +`$ touch ~/tmux_no_auto_restore`). Automatic restore won't happen if this file +exists. + diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/docs/multiple_tmux_servers.md b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/multiple_tmux_servers.md new file mode 100644 index 0000000..98c2506 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/multiple_tmux_servers.md @@ -0,0 +1,20 @@ +### Behavior when running multiple tmux servers + +(This is safe to skip if you're always running a single tmux server.) + +If you're an advanced tmux user, you might be running multiple tmux servers at +the same time. Maybe you start the first tmux server with `$ tmux` and then +later another one with e.g. `$ tmux -S/tmp/foo`. + +You probably don't want to "auto restore" the same environment in the second +tmux that uses `/tmp/foo` socket. You also probably don't want two tmux +environments both having "auto save" feature on (think about overwrites). + +This plugin handles multi-server scenario by giving precedence to the tmux +server that was first started. + +In the above example, the server started with `$ tmux` will do "auto +restore" (if enabled) and will start "auto saving". +"Auto restore" or "auto saving" **will not** happen for the second server that +was started later with the `$ tmux -S/tmp/foo` command. The plugin will +detect the presence of another server (`$ tmux`) and give it precedence. diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/docs/systemd_details.md b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/systemd_details.md new file mode 100644 index 0000000..7b9ed45 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/docs/systemd_details.md @@ -0,0 +1,13 @@ +# Systemd automatic start for tmux + +The first time tmux starts when `@continuum-boot` is set to 'on' tmux-continuum will generate a user level systemd unit file which it will save to `${HOME}/.config/systemd/user/tmux.service` and enable it. From then on when that user logs in, either through a GUI session or on the console or via ssh, Systemd will start the tmux server. + +The command used to start the tmux server is determined via the `@continuum-systemd-start-cmd` option that can be set in .tmux.conf. (Remember to reload your configuration with `tmux source ~/.tmux.conf` afterwards. + +The default command to use is `new-session -d`. If you want more control over what sessions get started then you should set up your sessions in tmux.conf and set `@continuum-systemd-start-cmd = 'start-server'`. As this will be executed as part of systemd's ExecStart statement there will be no shell parsing. See [Systemd manual](http://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines) for more details. + +To control the tmux service you can use all the standard `systemctl` commands using the `--user` argument. eg to see if the tmux server has started: + + systemctl --user status tmux.service + + diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/check_tmux_version.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/check_tmux_version.sh new file mode 100755 index 0000000..f4a887a --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/check_tmux_version.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_restore.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_restore.sh new file mode 100755 index 0000000..27afbc4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_restore.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_save.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_save.sh new file mode 100755 index 0000000..c0e0e7e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_save.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_status.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_status.sh new file mode 100755 index 0000000..280cf00 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/continuum_status.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start.sh new file mode 100755 index 0000000..433798b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/README.md b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/README.md new file mode 120000 index 0000000..0bf63cd --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/README.md @@ -0,0 +1 @@ +../../docs/automatic_start.md \ No newline at end of file diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_alacritty_start_tmux.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_alacritty_start_tmux.sh new file mode 100755 index 0000000..e9f09c6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_alacritty_start_tmux.sh @@ -0,0 +1,68 @@ +#!/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 "alacritty" + activate + delay 0.5 + tell application "System Events" to tell process "alacritty" + set frontmost to true + keystroke "tmux" + key code 36 + end tell + end tell + EOF +} + +resize_window_to_full_screen() { + osascript <<-EOF + tell application "alacritty" + activate + tell application "System Events" + if (every window of process "alacritty") is {} then + keystroke "n" using command down + end if + + tell application "Finder" + set desktopSize to bounds of window of desktop + end tell + + set position of front window of process "alacritty" to {0, 0} + set size of front window of process "alacritty" to {item 3 of desktopSize, item 4 of desktopSize} + end tell + end tell + EOF +} + +resize_to_true_full_screen() { + osascript <<-EOF + tell application "Alacritty" + activate + delay 0.5 + tell application "System Events" to tell process "Alacritty" + if front window exists then + tell front window + if value of attribute "AXFullScreen" then + set value of attribute "AXFullScreen" to false + else + set value of attribute "AXFullScreen" to true + end if + end tell + end if + 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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_disable.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_disable.sh new file mode 100755 index 0000000..19f9af1 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_disable.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_enable.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_enable.sh new file mode 100755 index 0000000..a7fea78 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_enable.sh @@ -0,0 +1,72 @@ +#!/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 fullscreen' + fi + + local content + read -r -d '' content <<-EOF + + + + + Label + ${osx_auto_start_file_name} + ProgramArguments + + ${tmux_start_script}$fullscreen_tag + + RunAtLoad + + + + EOF + echo "$content" +} + +get_strategy() { + local options="$1" + if [[ "$options" =~ "iterm" ]]; then + echo "iterm" + elif [[ "$options" =~ "kitty" ]]; then + echo "kitty" + elif [[ "$options" =~ "alacritty" ]]; then + echo "alacritty" + 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 strategy="$(get_strategy "$options")" + local fullscreen_option_value="$(get_fullscreen_option_value "$options")" + local tmux_start_script_path="${CURRENT_DIR}/osx_${strategy}_start_tmux.sh" + + local launchd_plist_file_content="$(template "$tmux_start_script_path" "$fullscreen_option_value")" + if ! diff "$osx_auto_start_file_path" <(echo "$launchd_plist_file_content") &>/dev/null ; then + echo "$launchd_plist_file_content" > "$osx_auto_start_file_path" + fi +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_iterm_start_tmux.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_iterm_start_tmux.sh new file mode 100755 index 0000000..18ea5e6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_iterm_start_tmux.sh @@ -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 window + 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 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 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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_kitty_start_tmux.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_kitty_start_tmux.sh new file mode 100755 index 0000000..94c1d2b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_kitty_start_tmux.sh @@ -0,0 +1,60 @@ +#!/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 "kitty" + activate + delay 5 + tell application "System Events" to tell process "kitty" + set frontmost to true + keystroke "tmux" + key code 36 + end tell + end tell + EOF +} + +resize_window_to_full_screen() { + osascript <<-EOF + tell application "kitty" + activate + tell application "System Events" + if (every window of process "kitty") is {} then + keystroke "n" using command down + end if + + tell application "Finder" + set desktopSize to bounds of window of desktop + end tell + + set position of front window of process "kitty" to {0, 0} + set size of front window of process "kitty" to {item 3 of desktopSize, item 4 of desktopSize} + end tell + end tell + EOF +} + +resize_to_true_full_screen() { + osascript <<-EOF + tell application "kitty" + activate + delay 1 + tell application "System Events" to tell process "kitty" + 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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_terminal_start_tmux.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_terminal_start_tmux.sh new file mode 100755 index 0000000..5a49391 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/osx_terminal_start_tmux.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_disable.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_disable.sh new file mode 100755 index 0000000..8c7bd6b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_disable.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Maintainer: Sven Vowe @nuclearglow +# Contact maintainer for any change to this file. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source "$CURRENT_DIR/../variables.sh" + +main() { + systemctl --user disable ${systemd_service_name} +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_enable.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_enable.sh new file mode 100755 index 0000000..ec9d349 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/handle_tmux_automatic_start/systemd_enable.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Maintainer: Sven Vowe @nuclearglow +# Contact maintainer for any change to this file. + +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="" + local resurrect_save_script_path="$(get_tmux_option "$resurrect_save_path_option" "$(realpath ${CURRENT_DIR}/../../../tmux-resurrect/scripts/save.sh)")" + local tmux_path="$(command -v tmux)" + + read -r -d '' content <<-EOF + [Unit] + Description=tmux default session (detached) + Documentation=man:tmux(1) + + [Service] + Type=forking + Environment=DISPLAY=:0 + ExecStart=${tmux_path} ${systemd_tmux_server_start_cmd} + + ExecStop=${resurrect_save_script_path} + ExecStop=${tmux_path} kill-server + KillMode=control-group + + 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 +} + +systemd_unit_file() { + 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}") + echo "$systemd_unit_file" +} + +write_unit_file() { + systemd_unit_file > "${systemd_unit_file_path}" +} + +write_unit_file_unless_exists() { + local systemd_unit_file_dir=$(dirname ${systemd_unit_file_path}) + if ! [ -d $systemd_unit_file_dir ]; then + mkdir -p $systemd_unit_file_dir + write_unit_file + elif ! [ -e "${systemd_unit_file_path}" ]; then + write_unit_file + fi +} + +main() { + write_unit_file_unless_exists + enable_tmux_unit_on_boot +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/helpers.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/helpers.sh new file mode 100644 index 0000000..5268e8e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/helpers.sh @@ -0,0 +1,49 @@ +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 + local user_id=$(id -u) + ps -u $user_id -o "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 ] +} diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/shared.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/shared.sh new file mode 100644 index 0000000..5ac8885 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/shared.sh @@ -0,0 +1,7 @@ +current_timestamp() { + echo "$(date +%s)" +} + +set_last_save_timestamp() { + set_tmux_option "$last_auto_save_option" "$(current_timestamp)" +} diff --git a/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/variables.sh b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/variables.sh new file mode 100644 index 0000000..8a5d0c6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-continuum/scripts/variables.sh @@ -0,0 +1,43 @@ +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" + +auto_restore_max_delay_option="@continuum-restore-max-delay" +auto_restore_max_delay_default="10" + +# 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" diff --git a/dotfiles/common/.tmux/plugins/tmux-project/LICENSE b/dotfiles/common/.tmux/plugins/tmux-project/LICENSE new file mode 100644 index 0000000..1124080 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2021 Seong Yong-ju + +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. diff --git a/dotfiles/common/.tmux/plugins/tmux-project/README.md b/dotfiles/common/.tmux/plugins/tmux-project/README.md new file mode 100644 index 0000000..6133df2 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/README.md @@ -0,0 +1,79 @@ +# tmux-project + +Search projects and open them in a new session. + +## Prequisites + +- find +- [fzf](https://github.com/junegunn/fzf) (needs `fzf-tmux` to be installed) + +## Install + +### Installation with [tpm](https://github.com/tmux-plugins/tpm) (recommended) + +Add the following line to your `.tmux.conf`. + +```tmux +set -g @plugin 'sei40kr/tmux-project' +``` + +--- + +It is also recommended to add the following line to your `.tmux.conf` to prevent +a client from being detached when the active session is destroyed. + +```tmux +set detach-on-destroy no-detached +``` + +## Customization + +| Variable | Default value | Description | +| :------------------------------ | :-------------------- | :-------------------------------------------------------------------------------- | +| `@project-key` | `"g"` | The key to invoke the project search. If you set it to `""`, the key is disabled. | +| `@project-base-dirs` | `""` | A comma-separated list of directories and their depths to search for projects. | +| `@project-rooters` | `".git"` | A comma-separated list of rooters. | +| `@project-fzf-tmux-layout-opts` | `""` | The layout options for fzf-tmux. See `fzf-tmux(1)` for details. | +| `@project-fzf-opts` | `"--preview 'ls {}'"` | The options for fzf. See `fzf(1)` for details. | + +### Setting `@project-base-dirs` + +`@project-base-dirs` is a comma-separated list of directories and their depths to search for projects. + +Each element of the list is in the following format: + +``` +/path/to/dir[:[:]] +``` + +- If you omit `` and ``, they are set to `0` and `0` respectively. +- If you omit ``, it is set to ``. (means `` is the exact depth) + +If you omit the depth or explicitly set it to `0`, the directory itself will be +added as a project. In that case, you can add the directory as a project even if +it contains no rooter. + +--- + +For example, if you want to search for ghq repositories as projects: + +```tmux +set -ag @project-base-dirs ,"${GHQ_ROOT}:3" +``` + +For example, if you want to add `~/.vim` itself as a project: + +```tmux +set -ag @project-base-dirs ,"${HOME}/.vim" +``` + +## tmuxinator Integration + +tmux-project can be integrated with [tmuxinator](https://github.com/tmuxinator/tmuxinator). +When you have tmuxinator installed and the selected project has +`.tmuxinator.yml` at the root, tmux-project will start a new session with +tmuxinator. + +## License + +MIT diff --git a/dotfiles/common/.tmux/plugins/tmux-project/flake.lock b/dotfiles/common/.tmux/plugins/tmux-project/flake.lock new file mode 100644 index 0000000..e6f9099 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1683014792, + "narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/dotfiles/common/.tmux/plugins/tmux-project/flake.nix b/dotfiles/common/.tmux/plugins/tmux-project/flake.nix new file mode 100644 index 0000000..45877f1 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/flake.nix @@ -0,0 +1,30 @@ +{ + description = "A very basic flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + packages.default = pkgs.callPackage ./packages/default.nix { }; + + devShells.default = + let + tmux_conf = pkgs.writeText "tmux.conf" '' + run-shell ${self.packages.${system}.default.rtp}/project.tmux + ''; + in + pkgs.mkShell { + buildInputs = with pkgs; [ tmux fzf ]; + shellHook = '' + TMUX= TMUX_TMPDIR= exec tmux -f ${tmux_conf} + ''; + }; + } + ); +} diff --git a/dotfiles/common/.tmux/plugins/tmux-project/lib/tmux.bash b/dotfiles/common/.tmux/plugins/tmux-project/lib/tmux.bash new file mode 100644 index 0000000..af35b4f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/lib/tmux.bash @@ -0,0 +1,8 @@ +get_tmux_option() { + local option=$1 + local default_value=$2 + local option_value + + option_value=$(tmux show -gqv "$option") + echo "${option_value:-$default_value}" +} diff --git a/dotfiles/common/.tmux/plugins/tmux-project/libexec/switch-project.bash b/dotfiles/common/.tmux/plugins/tmux-project/libexec/switch-project.bash new file mode 100755 index 0000000..926d9fe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/libexec/switch-project.bash @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +basedir="$(cd "$(dirname "$0")/.." && pwd)" +# shellcheck source=../lib/tmux.bash +. "$basedir/lib/tmux.bash" + +find_projects() { + local -a base_dirs rooters rooter_opts + + IFS=',' read -ra base_dirs < <(get_tmux_option '@project-base-dirs') + IFS=',' read -ra rooters < <(get_tmux_option '@project-rooters' '.git') + + for rooter in "${rooters[@]}"; do + rooter_opts+=("-o" "-name" "$rooter") + done + rooter_opts=('(' "${rooter_opts[@]:1}" ')') + + for base_dir in "${base_dirs[@]}"; do + # If the base_dir is empty, skip it + if [[ -z "$base_dir" ]]; then + continue + fi + + local -a tmp + IFS=':' read -ra tmp <<<"$base_dir" + path="${tmp[0]}" + min_depth="${tmp[1]:-0}" + max_depth="${tmp[2]:-${min_depth}}" + + if [[ min_depth -eq 0 && max_depth -eq 0 ]]; then + # If min_depth and max_depth are both 0, that means we + # want to add the base_dir itself as a project. + # In that case, add the base_dir as a project even if it + # contains no rooter. + if [[ -d "$path" || -L "$path" ]]; then + echo "$path" + fi + else + find "$path" -mindepth "$((min_depth + 1))" \ + -maxdepth "$((max_depth + 1))" \ + "${rooter_opts[@]}" \ + -printf '%h\n' + fi + done +} + +# Convert a path to a session name. The session name is the last component of +# the path. Dots are replaced with underscores. If the path starts with a dot, +# the dot is removed. +# e.g. /home/username/.emacs.d -> emacs_d +to_session_name() { + local session_name="$1" + + session_name="${session_name##*/}" + + # Dots are not allowed in a tmux session name + # e.g. .emacs.d -> _emacs_d + session_name="${session_name//./_}" + # If the path starts with a slash (a dot), remove it + # e.g. .emacs.d -> _emacs_d -> emacs_d + session_name="${session_name#_}" + + echo "$session_name" +} + +main() { + local fzf_opts fzf_tmux_layout_opts selected_path session_name + + # shellcheck disable=SC2207 + fzf_opts="$(get_tmux_option "@project-fzf-opts" "--preview 'ls {}'")" + # shellcheck disable=SC2207 + fzf_tmux_layout_opts="$(get_tmux_option "@project-fzf-tmux-layout-opts")" + + selected_path="$(find_projects | eval "fzf-tmux ${fzf_tmux_layout_opts} -- ${fzf_opts}")" + if [[ -z "$selected_path" ]]; then + return 0 + fi + + # If the selected project has .tmuxinator.yml file, use tmuxinator to + # start the session. + if command -v tmuxinator &>/dev/null && [[ -f "${selected_path}/.tmuxinator.yml" ]]; then + tmuxinator start -p "${selected_path}/.tmuxinator.yml" + return 0 + fi + + session_name="$(to_session_name "$selected_path")" + + # If the session already exists, attach to it. Otherwise, create a new + # session and attach to it. + if ! tmux has-session -t "$session_name" 2>/dev/null; then + # Return 0 even if creating the session fails. + tmux new-session -d -s "$session_name" -c "$selected_path" \; \ + set -t "$session_name" destroy-unattached off || : + fi + tmux switch-client -t "$session_name" +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-project/packages/default.nix b/dotfiles/common/.tmux/plugins/tmux-project/packages/default.nix new file mode 100644 index 0000000..bac503c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/packages/default.nix @@ -0,0 +1,11 @@ +{ lib, tmuxPlugins }: + +tmuxPlugins.mkTmuxPlugin { + pluginName = "project"; + version = "unstable-2023-05-04"; + src = ../.; + meta = with lib; { + license = licenses.mit; + platforms = platforms.unix; + }; +} diff --git a/dotfiles/common/.tmux/plugins/tmux-project/project.tmux b/dotfiles/common/.tmux/plugins/tmux-project/project.tmux new file mode 100755 index 0000000..ec799da --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-project/project.tmux @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +basedir="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=lib/tmux.bash +. "${basedir}/lib/tmux.bash" + +key="$(get_tmux_option '@project-key' 'g')" +if [[ -n "$key" ]]; then + tmux bind-key "$key" run-shell -b "${basedir}/libexec/switch-project.bash" +fi diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitattributes b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitattributes new file mode 100644 index 0000000..3772b5e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitattributes @@ -0,0 +1,5 @@ +# Force text files to have unix eols, so Windows/Cygwin does not break them +*.* eol=lf + +# Except for images because then on checkout the files have been altered. +*.png binary diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitignore b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitignore new file mode 100644 index 0000000..72523e8 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitignore @@ -0,0 +1,3 @@ +run_tests +tests/run_tests_in_isolation +tests/helpers/helpers.sh diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitmodules b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitmodules new file mode 100644 index 0000000..5e44e3c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/tmux-test"] + path = lib/tmux-test + url = https://github.com/tmux-plugins/tmux-test.git diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/.travis.yml b/dotfiles/common/.tmux/plugins/tmux-resurrect/.travis.yml new file mode 100644 index 0000000..fea6850 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/.travis.yml @@ -0,0 +1,19 @@ +# generic packages and tmux +before_install: + - sudo apt-get update + - sudo apt-get install -y git-core expect + - sudo apt-get install -y python-software-properties software-properties-common + - sudo apt-get install -y libevent-dev libncurses-dev + - git clone https://github.com/tmux/tmux.git + - cd tmux + - git checkout 2.5 + - sh autogen.sh + - ./configure && make && sudo make install + +install: + - git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules + # manual `git clone` required for testing `tmux-test` plugin itself + - git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test; true + - lib/tmux-test/setup + +script: ./tests/run_tests_in_isolation diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/CHANGELOG.md new file mode 100644 index 0000000..2957f18 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/CHANGELOG.md @@ -0,0 +1,163 @@ +# Changelog + +### master +- Remove deprecated "restoring shell history" feature. + +### v4.0.0, 2022-04-10 +- Proper handling of `automatic-rename` window option. +- save and restore tmux pane title (breaking change: you have to re-save to be + able to properly restore!) + +### v3.0.0, 2021-08-30 +- save and restore tmux pane contents (@laomaiweng) +- update tmux-test to solve issue with recursing git submodules in that project +- set options quietly in `resurrect.tmux` script +- improve pane contents restoration: `cat ` is no longer shown in pane + content history +- refactoring: drop dependency on `paste` command +- bugfix for pane contents restoration +- expand tilde char `~` if used with `@resurrect-dir` +- do not save empty trailing lines when pane content is saved +- do not save pane contents if pane is empty (only for 'save pane contents' + feature) +- "save pane contents" feature saves files to a separate directory +- archive and compress pane contents file +- make archive & compress pane contents process more portable +- `mutt` added to the list of automatically restored programs +- added guide for migrating from tmuxinator +- fixed a bug for restoring commands on tmux 2.5 (and probably tmux 2.4) +- do not create another resurrect file if there are no changes (credit @vburdo) +- allow using '$HOSTNAME' in @resurrect-dir +- add zsh history saving and restoring +- delete resurrect files older than 30 days, but keep at least 5 files +- add save and restore hooks +- always use `-ao` flags for `ps` command to detect commands +- Deprecate restoring shell history feature. +- `view` added to the list of automatically restored programs +- Enable vim session strategy to work with custom session files, + e.g. `vim -S Session1.vim`. +- Enable restoring command arguments for inline strategies with `*` character. +- Kill session "0" if it wasn't restored. +- Add `@resurrect-delete-backup-after` option to specify how many days of + backups to keep - default is 30. + +### v2.4.0, 2015-02-23 +- add "tmux-test" +- add test for "resurrect save" feature +- add test for "resurrect restore" feature +- make the tests work and pass on travis +- add travis badge to the readme + +### v2.3.0, 2015-02-12 +- Improve fetching proper window_layout for zoomed windows. In order to fetch + proper value, window has to get unzoomed. This is now done faster so that + "unzoom,fetch value,zoom" cycle is almost unnoticable to the user. + +### v2.2.0, 2015-02-12 +- bugfix: zoomed windows related regression +- export save and restore script paths so that 'tmux-resurrect-save' plugin can + use them +- enable "quiet" saving (used by 'tmux-resurrect-save' plugin) + +### v2.1.0, 2015-02-12 +- if restore is started when there's only **1 pane in the whole tmux server**, + assume the users wants the "full restore" and overrwrite that pane. + +### v2.0.0, 2015-02-10 +- add link to the wiki page for "first pane/window issue" to the README as well + as other tweaks +- save and restore grouped sessions (used with multi-monitor workflow) +- save and restore active and alternate windows in grouped sessions +- if there are no grouped sessions, do not output empty line to "last" file +- restore active and alternate windows only if they are present in the "last" file +- refactoring: prefer using variable with tab character +- remove deprecated `M-s` and `M-r` key bindings (breaking change) + +### v1.5.0, 2014-11-09 +- add support for restoring neovim sessions + +### v1.4.0, 2014-10-25 +- plugin now uses strategies when fetching pane full command. Implemented + 'default' strategy. +- save command strategy: 'pgrep'. It's here only if fallback is needed. +- save command strategy: 'gdb' +- rename default strategy name to 'ps' +- create `expect` script that can fully restore tmux environment +- fix default save command strategy `ps` command flags. Flags are different for + FreeBSD. +- add bash history saving and restoring (@rburny) +- preserving layout of zoomed windows across restores (@Azrael3000) + +### v1.3.0, 2014-09-20 +- remove dependency on `pgrep` command. Use `ps` for fetching process names. + +### v1.2.1, 2014-09-02 +- tweak 'new_pane' creation strategy to fix #36 +- when running multiple tmux server and for a large number of panes (120 +) when + doing a restore, some panes might not be created. When that is the case also + don't restore programs for those panes. + +### v1.2.0, 2014-09-01 +- new feature: inline strategies when restoring a program + +### v1.1.0, 2014-08-31 +- bugfix: sourcing `variables.sh` file in save script +- add `Ctrl` key mappings, deprecate `Alt` keys mappings. + +### v1.0.0, 2014-08-30 +- show spinner during the save process +- add screencast script +- make default program running list even more conservative + +### v0.4.0, 2014-08-29 +- change plugin name to `tmux-resurrect`. Change all the variable names. + +### v0.3.0, 2014-08-29 +- bugfix: when top is running the pane `$PWD` can't be saved. This was causing + issues during the restore and is now fixed. +- restoring sessions multiple times messes up the whole environment - new panes + are all around. This is now fixed - pane restorations are now idempotent. +- if pane exists from before session restore - do not restore the process within + it. This makes the restoration process even more idempotent. +- more panes within a window can now be restored +- restore window zoom state + +### v0.2.0, 2014-08-29 +- bugfix: with vim 'session' strategy, if the session file does not exist - make + sure vim does not contain `-S` flag +- enable restoring programs with arguments (e.g. "rails console") and also + processes that contain program name +- improve `irb` restore strategy + +### v0.1.0, 2014-08-28 +- refactor checking if saved tmux session exists +- spinner while tmux sessions are restored + +### v0.0.5, 2014-08-28 +- restore pane processes +- user option for disabling pane process restoring +- enable whitelisting processes that will be restored +- expand readme with configuration options +- enable command strategies; enable restoring vim sessions +- update readme: explain restoring vim sessions + +### v0.0.4, 2014-08-26 +- restore pane layout for each window +- bugfix: correct pane ordering in a window + +### v0.0.3, 2014-08-26 +- save and restore current and alternate session +- fix a bug with non-existing window names +- restore active pane for each window that has multiple panes +- restore active and alternate window for each session + +### v0.0.2, 2014-08-26 +- saving a new session does not remove the previous one +- make the directory where sessions are stored configurable +- support only Tmux v1.9 or greater +- display a nice error message if saved session file does not exist +- added README + +### v0.0.1, 2014-08-26 +- started a project +- basic saving and restoring works diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/CONTRIBUTING.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/CONTRIBUTING.md new file mode 100644 index 0000000..444098c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/CONTRIBUTING.md @@ -0,0 +1,12 @@ +### Contributing + +Code contributions are welcome! + +### Reporting a bug + +If you find a bug please report it in the issues. When reporting a bug please +attach: +- a file symlinked to `~/.tmux/resurrect/last`. +- your `.tmux.conf` +- if you're getting an error paste it to a [gist](https://gist.github.com/) and + link it in the issue diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/LICENSE.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/LICENSE.md new file mode 100644 index 0000000..40f6ddd --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/LICENSE.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/README.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/README.md new file mode 100644 index 0000000..f137ad8 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/README.md @@ -0,0 +1,129 @@ +# 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 a previously saved environment](docs/restoring_previously_saved_environment.md) + +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.
+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:
+ `vi vim nvim emacs man less more tail top htop irssi weechat mutt`.
+ [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. + +### 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) diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/custom_key_bindings.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/custom_key_bindings.md new file mode 100644 index 0000000..99bfc2c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/custom_key_bindings.md @@ -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' diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/hooks.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/hooks.md new file mode 100644 index 0000000..b373e50 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/hooks.md @@ -0,0 +1,33 @@ +# 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-pane-processes` + + Called 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)' diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/migrating_from_tmuxinator.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/migrating_from_tmuxinator.md new file mode 100644 index 0000000..f59f90f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/migrating_from_tmuxinator.md @@ -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**
+ `tmuxinator` depends on `ruby` which can be a hassle to set up if you're not a + rubyist.
+ `tmux-resurrect` depends just on `bash` which is virtually + omnipresent. + +- **Simplicity**
+ `tmuxinator` has an executable, CLI interface with half dozen commands and + command completion.
+ `tmux-resurrect` defines just 2 tmux key bindings. + +- **No configuration**
+ `tmuxinator` is all about config files (and their constant updating).
+ `tmux-resurrect` requires no configuration to work. + +- **Better change handling**
+ 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).
+ 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.
+ 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.
+ If you want to work on an existing project, you should be able to just + \*switch* to an already open session using `prefix + s`.
+ 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).
+ 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`:
+ 1) Save tmux environment just before restarting/shutting down your + computer.
+ 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. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_bash_history.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_bash_history.md new file mode 100644 index 0000000..2b6af17 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_bash_history.md @@ -0,0 +1,39 @@ +tmux-ressurect no longer restores shell history for each pane, as of [this PR](https://github.com/tmux-plugins/tmux-resurrect/pull/308). + +As a workaround, you can use the `HISTFILE` environment variable to preserve history for each pane separately, and modify +`PROMPT_COMMAND` to make sure history gets saved with each new command. + +Unfortunately, we haven't found a perfect way of getting a unique identifier for each pane, as the `TMUX_PANE` variable +seems to occasionally change when resurrecting. As a workaround, the example below sets a unique ID in each pane's `title`. +The downside of this implementation is that pane titles must all be unique across sessions/windows, and also must use the `pane_id_prefix`. + +Any improvements/suggestions for getting a unique, persistent ID for each pane are welcome! + +```bash +pane_id_prefix="resurrect_" + +# Create history directory if it doesn't exist +HISTS_DIR=$HOME/.bash_history.d +mkdir -p "${HISTS_DIR}" + +if [ -n "${TMUX_PANE}" ]; then + + # Check if we've already set this pane title + pane_id=$(tmux display -pt "${TMUX_PANE:?}" "#{pane_title}") + if [[ $pane_id != "$pane_id_prefix"* ]]; then + + # if not, set it to a random ID + random_id=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 16) + printf "\033]2;$pane_id_prefix$random_id\033\\" + pane_id=$(tmux display -pt "${TMUX_PANE:?}" "#{pane_title}") + fi + + # use the pane's random ID for the HISTFILE + export HISTFILE="${HISTS_DIR}/bash_history_tmux_${pane_id}" +else + export HISTFILE="${HISTS_DIR}/bash_history_no_tmux" +fi + +# Stash the new history each time a command runs. +export PROMPT_COMMAND="$PROMPT_COMMAND;history -a" +``` diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_pane_contents.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_pane_contents.md new file mode 100644 index 0000000..2dff59a --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_pane_contents.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_previously_saved_environment.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_previously_saved_environment.md new file mode 100644 index 0000000..8e845ac --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_previously_saved_environment.md @@ -0,0 +1,14 @@ +# Restoring previously saved environment + +None of the previous saves are deleted (unless you explicitly do that). All save +files are kept in `~/.tmux/resurrect/` directory, or `~/.local/share/tmux/resurrect` +(unless `${XDG_DATA_HOME}` says otherwise).
+Here are the steps to restore to a previous point in time: + +- make sure you start this with a "fresh" tmux instance +- `$ cd ~/.tmux/resurrect/` +- locate the save file you'd like to use for restore (file names have a timestamp) +- symlink the `last` file to the desired save file: `$ ln -sf last` +- do a restore with `tmux-resurrect` key: `prefix + Ctrl-r` + +You should now be restored to the time when `` save happened. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_programs.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_programs.md new file mode 100644 index 0000000..6d316d6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_programs.md @@ -0,0 +1,205 @@ +# Restoring programs + - [General instructions](#general-instructions) + - [Clarifications](#clarifications) + - [Working with NodeJS](#nodejs) + - [Restoring Mosh](#mosh) + +### General instructions +Only a conservative list of programs is restored by default:
+`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"' + +- Use `*` to expand the arguments from the saved command when restoring: + + set -g @resurrect-processes 'some_program "~rails server->rails server *"' + +- Don't restore any programs: + + set -g @resurrect-processes 'false' + +- Restore **all** programs (dangerous!): + + set -g @resurrect-processes ':all:' + + Be *very careful* with this: tmux-resurrect can not know which programs take + which context, and a `sudo mkfs.vfat /dev/sdb` that was just formatting an + external USB stick could wipe your backup hard disk if that's what's attached + after rebooting. + + This option is primarily useful for experimentation (e.g., to find out which + program is recognized in a pane). + +### Clarifications + +> 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. + +> What is asterisk `*` and why is it used? + +(Please read the above clarifications about tilde `~` and arrow `->`). + +Continuing with the `rails server` example, you might have added flags for e.g. +verbose logging, but with the above configuration, the flags would be lost. + +To preserve the command arguments when restoring, use the asterisk `*`: (**note**: there **must** be a space before `*`) + + set -g @resurrect-processes '"~rails server->rails server *"' + +This option says: "when this process is restored use `rails server` as the +command name, but preserve its arguments". + +> 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.
+ 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.
+ 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 +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 +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. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_vim_and_neovim_sessions.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_vim_and_neovim_sessions.md new file mode 100644 index 0000000..f84442b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/restoring_vim_and_neovim_sessions.md @@ -0,0 +1,19 @@ +# 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. + +> If you're using the vim binary provided by MacVim.app then you'll need to set `@resurrect-processes`, for example: +> ``` +> set -g @resurrect-processes '~Vim -> vim' +> ``` diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/save_dir.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/save_dir.md new file mode 100644 index 0000000..bf724c6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/docs/save_dir.md @@ -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 `~`. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.gitignore b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.gitignore new file mode 100644 index 0000000..27281b5 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.gitignore @@ -0,0 +1,2 @@ +.vagrant/ +lib/ diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.travis.yml b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.travis.yml new file mode 100644 index 0000000..fea6850 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/.travis.yml @@ -0,0 +1,19 @@ +# generic packages and tmux +before_install: + - sudo apt-get update + - sudo apt-get install -y git-core expect + - sudo apt-get install -y python-software-properties software-properties-common + - sudo apt-get install -y libevent-dev libncurses-dev + - git clone https://github.com/tmux/tmux.git + - cd tmux + - git checkout 2.5 + - sh autogen.sh + - ./configure && make && sudo make install + +install: + - git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules + # manual `git clone` required for testing `tmux-test` plugin itself + - git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test; true + - lib/tmux-test/setup + +script: ./tests/run_tests_in_isolation diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/CHANGELOG.md new file mode 100644 index 0000000..094834f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/CHANGELOG.md @@ -0,0 +1,46 @@ +# Changelog + +### master +- move `setup` task to `.travis.yml` for travis tests +- "merge" travis.yml and travis_for_plugins.yml files (no need to keep em + separate) +- add more useful helper functions +- remove tmux-test repo as a submodule from self, this causes issues with + `$ git submodule update --recursive --init` command that some users use for + managing other plugins +- add new helper `teardown_helper` +- add `run_tests` helper +- change CLI syntax for choosing vagrant machine to run the tests on +- enable running just a single test via `run_tests` cli interface +- add `--keep-running` cli option to continue running vagrant after the tests + are done executing +- start using tmux 2.0 for tests +- use tmux 2.5 for tests + +### v0.2.0, 2015-02-22 +- `setup` script gitignores `tests/helpers.sh` +- move `tests/helpers.sh` to `tests/helpers/helpers.sh` +- `setup` undo removes added lines from gitignore file + +### v0.1.0, 2015-02-22 +- changes so that 'tmux-test' can be included with tmux plugins +- do not gitignore submodules directory +- add installation and usage instructions +- copy `.travis.yml` to the project root when running `setup` script +- add a brief mention of travis CI to the readme +- add test helpers +- `setup` script symlinks helpers file to `tests/` directory +- `setup` script can undo most of its actions +- add a tmux scripting test +- `tmux-test` uses `tmux-test` to test itself +- update `tmux-test` submodule +- a different `travis.yml` for `tmux-test` and for plugins + +### v0.0.1, 2015-02-21 +- git init +- add vagrant provisioning scripts for ubuntu and debian +- add a ".travis.yml" file +- generic "run_tests" script +- "run_tests_in_isolation" script +- add "Vagrantfile" +- enable passing VM names as arguments to "run_tests" script diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/LICENSE.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/LICENSE.md new file mode 100644 index 0000000..e6e7350 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/LICENSE.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/README.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/README.md new file mode 100644 index 0000000..e01200c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/README.md @@ -0,0 +1,134 @@ +# tmux-test + +[![Build Status](https://travis-ci.org/tmux-plugins/tmux-test.png?branch=master)](https://travis-ci.org/tmux-plugins/tmux-test) + +A small framework for isolated testing of tmux plugins. Isolation is achieved by +running the tests in `Vagrant`. Works on [travis](travis-ci.org) too. + +Extracted from [tmux plugin manager](https://github.com/tmux-plugins/tpm) and +[tmux-copycat](https://github.com/tmux-plugins/tmux-copycat). + +Dependencies: `Vagrant` (not required when running on travis). + +### Setup + +Let's say you made tmux plugin with the following file hierarchy: + +```text +/tmux-plugin +|-- plugin.tmux +`-- scripts + `-- plugin_script.sh +``` + +From your project root directory (tmux-plugin/) execute the following shell +command to fetch `tmux-test` and add it as a submodule: + + $ git submodule add https://github.com/tmux-plugins/tmux-test.git lib/tmux-test + +Run the `setup` script: + + $ lib/tmux-test/setup + +The project directory will now look like this (additions have comments): + +```text +/tmux-plugin +|-- plugin.tmux +|-- run_tests # symlink, gitignored +|-- .gitignore # 2 lines appended to gitignore +|-- .travis.yml # added +|-- lib/tmux-test/ # git submodule +|-- scripts +| `-- plugin_script.sh +`-- tests # dir to put the tests in + `-- run_tests_in_isolation.sh # symlink, gitignored + `-- helpers + `-- helpers.sh # symlinked bash helpers, gitignored +``` + +`tmux-test` is now set up. You are ok to commit the additions to the repo. + +### Writing and running tests + +A test is any executable with a name starting with `test_` in `tests/` +directory. + +Now that you installed `tmux-test` let's create an example test. + +- create a `tests/test_example.sh` file with the following content (it's a + `bash` script but it can be any executable): + + #/usr/bin/env bash + + CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + # bash helpers provided by 'tmux-test' + source $CURRENT_DIR/helpers/helpers.sh + + # installs plugin from current repo in Vagrant (or on Travis) + install_tmux_plugin_under_test_helper + + # start tmux in background (plugin under test is sourced) + tmux new -d + + # get first session name + session_name="$(tmux list-sessions -F "#{session_name}")" + + # fail the test if first session name is not "0" + if [ "$session_name" == "0" ]; then + # fail_helper is also provided by 'tmux-test' + fail_helper "First session name is not '0' by default" + fi + + # sets the right script exit code ('tmux-test' helper) + exit_helper + +- make the test file executable with `$ chmod +x tests/test_example.sh` +- run the test by executing `./run_tests` from the project root directory +- the first invocation might take some time because Vagrant's ubuntu virtual + machine is downloading. You should see `Success, tests pass!` message when it's + done. + +Check out more example test scripts in this project's [tests/ directory](tests/). + +### Continuous integration + +The setup script (`lib/tmux-test/setup`) added a `.travis.yml` file to the +project root. To setup continuous integration, just add/enable the project on +[travis](travis-ci.org). + +### Notes + +- The `tests/` directory for tests and `lib/tmux-test/` for cloning `tmux-test` + into cannot be changed currently +- Don't run `tests/run_tests_in_isolation` script on your local development + environment. That's an internal test runner meant to be executed in an + isolated environment like `vagrant` or `travis`.
+ Use `./run_tests` script. +- You can use `KEEP_RUNNING=true ./run_tests` for faster test running cycle. + If this case `Vagrant` will keep running even after the tests are done. +- You can use `VAGRANT_CWD=lib/tmux-test/ vagrant ssh ubuntu` for ssh login to + `Vagrant`. + +### Running `tmux-test` framework tests + +`tmux-test` uses itself to test itself. To run framework tests: + +- clone this project `$ git clone git@github.com:tmux-plugins/tmux-test.git` +- `$ cd tmux-test` +- run `$ ./run_framework_tests` + +### Other goodies + +- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for + regex searches in tmux and fast match selection +- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic + restoring and continuous saving of tmux env + +You might want to follow [@brunosutic](https://twitter.com/brunosutic) on +twitter if you want to hear about new tmux plugins or feature updates. + +### License + +[MIT](LICENSE.md) diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/Vagrantfile b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/Vagrantfile new file mode 100644 index 0000000..04b3eba --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/Vagrantfile @@ -0,0 +1,17 @@ +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + + config.vm.synced_folder "../../", "/vagrant" + + config.vm.define :ubuntu do |ubuntu| + ubuntu.vm.box = "hashicorp/precise64" + ubuntu.vm.provision "shell", path: "vagrant_ubuntu_provisioning.sh" + end + + config.vm.define :centos do |centos| + centos.vm.box = "chef/centos-6.5" + centos.vm.provision "shell", path: "vagrant_centos_provisioning.sh" + end + +end diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/run_framework_tests b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/run_framework_tests new file mode 100755 index 0000000..fc0620c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/run_framework_tests @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# This file is used to run "tmux-test" framework tests. + +# "setup" script is needed to run the tests, but it overrides some working dir +# files. To address that, "setup" is run before the tests and its actions are +# undone after. + +main() { + git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test + lib/tmux-test/setup + ./run_tests + local exit_value=$? + lib/tmux-test/setup "undo" + exit "$exit_value" +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/setup b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/setup new file mode 100755 index 0000000..575a8a3 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/setup @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# invoke this script from your projects root directory + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# pass "undo" as a script arg to undo most of the setup actions +UNDO_SETUP="$1" +undo() { + [ "$UNDO_SETUP" == "undo" ] +} + +restore() { + local file="$1" + rm -f "$file" + git checkout -- "$file" 2>/dev/null +} + +gitignore() { + local file="$1" + grep -q "^${file}$" .gitignore 2>/dev/null || echo "$file" >> .gitignore +} + +remove_from_gitignore() { + local file="$1" + local escaped_filename="$(echo "$file" | sed "s,/,\\\/,g")" + sed -i"" "/^${escaped_filename}$/d" .gitignore +} + +add_files_to_gitignore() { + if ! undo; then + gitignore "run_tests" + gitignore "tests/run_tests_in_isolation" + gitignore "tests/helpers/helpers.sh" + else + remove_from_gitignore "run_tests" + remove_from_gitignore "tests/run_tests_in_isolation" + remove_from_gitignore "tests/helpers/helpers.sh" + fi +} + +symlink_user_test_runner() { + local file="run_tests" + if ! undo; then + ln -sf "lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +create_directory_for_tests() { + if ! undo; then + mkdir -p tests/helpers/ + fi +} + +symlink_internal_test_runner() { + local file="tests/run_tests_in_isolation" + if ! undo; then + ln -sf "../lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +symlink_test_helpers() { + local file="tests/helpers/helpers.sh" + if ! undo; then + ln -sf "../../lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +copy_travis_yml() { + local file=".travis.yml" + if ! undo; then + cp "lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +main() { + add_files_to_gitignore + symlink_user_test_runner + create_directory_for_tests + symlink_internal_test_runner + symlink_test_helpers + copy_travis_yml +} +main + diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/helpers/helpers.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/helpers/helpers.sh new file mode 100644 index 0000000..32b1ee4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/helpers/helpers.sh @@ -0,0 +1,68 @@ +# This file is a symlink from 'tmux-test' plugin. +# You probably don't want to edit it. + + +# Global variable that keeps the value of test status (success/fail). +# Suggested usage is via `fail_helper` and `exit_helper` functions. +TEST_STATUS="success" + +# PRIVATE FUNCTIONS + +_clone_the_plugin() { + local plugin_path="${HOME}/.tmux/plugins/tmux-plugin-under-test/" + rm -rf "$plugin_path" + git clone --recursive "${CURRENT_DIR}/../" "$plugin_path" >/dev/null 2>&1 +} + +_add_plugin_to_tmux_conf() { + set_tmux_conf_helper<<-HERE + run-shell '~/.tmux/plugins/tmux-plugin-under-test/*.tmux' + HERE +} + +# PUBLIC HELPER FUNCTIONS + +teardown_helper() { + rm -f ~/.tmux.conf + rm -rf ~/.tmux/ + tmux kill-server >/dev/null 2>&1 +} + +set_tmux_conf_helper() { + > ~/.tmux.conf # empty tmux.conf file + while read line; do + echo "$line" >> ~/.tmux.conf + done +} + +fail_helper() { + local message="$1" + echo "$message" >&2 + TEST_STATUS="fail" +} + +exit_helper() { + teardown_helper + if [ "$TEST_STATUS" == "fail" ]; then + echo "FAIL!" + echo + exit 1 + else + echo "SUCCESS" + echo + exit 0 + fi +} + +install_tmux_plugin_under_test_helper() { + _clone_the_plugin + _add_plugin_to_tmux_conf +} + +run_tests() { + # get all the functions starting with 'test_' and invoke them + for test in $(compgen -A function | grep "^test_"); do + "$test" + done + exit_helper +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/run_tests_in_isolation b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/run_tests_in_isolation new file mode 100755 index 0000000..fa39ebe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/run_tests_in_isolation @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# This file is a symlink from 'tmux-test' plugin. +# You probably don't want to edit it. + +# This script should be run within an isolated enviroment (Vagrant, travis). +# Depending on what the tests do, it might NOT be safe to run this script +# directly on the development machine. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +EXIT_VALUE=0 # running a test suite is successful by default + +all_test_files() { + ls -1 "$CURRENT_DIR" | # test files are in the current dir + \grep -i "^test" | # test file names start with "test" + xargs # file names in a single line +} + +set_exit_val_to_false() { + EXIT_VALUE=1 +} + +run_tests() { + local test_file tests_files + if [ "$#" -gt 0 ]; then + test_files="${@//tests\//}" # remove 'tests/' directory prefix + else + test_files="$(all_test_files)" + fi + for test_file in $test_files; do + echo "Running test: $test_file" + "${CURRENT_DIR}/${test_file}" + + # handling exit value + local test_exit_value="$?" + if [ "$test_exit_value" -ne 0 ]; then + set_exit_val_to_false + fi + done +} + +main() { + run_tests "$@" + exit "$EXIT_VALUE" +} +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_basic_script_execution.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_basic_script_execution.sh new file mode 100755 index 0000000..0fdcf09 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_basic_script_execution.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +exit 0 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_default_session_name.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_default_session_name.sh new file mode 100755 index 0000000..c761b93 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_default_session_name.sh @@ -0,0 +1,24 @@ +#/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# bash helpers provided by 'tmux-test' +source $CURRENT_DIR/helpers/helpers.sh + +# installs plugin from current repo in Vagrant (or on Travis) +install_tmux_plugin_under_test_helper + +# start tmux in background (plugin under test is sourced) +tmux new -d + +# get first session name +session_name="$(tmux list-sessions -F "#{session_name}")" + +# fail the test if first session name is not "0" +if ! [ "$session_name" == "0" ]; then + # fail_helper is also provided by 'tmux-test' + fail_helper "First session name is not '0' by default" +fi + +# sets the right script exit code ('tmux-test' helper) +exit_helper diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_tmux_scripting.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_tmux_scripting.sh new file mode 100755 index 0000000..3b4bece --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/tests/test_tmux_scripting.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $CURRENT_DIR/helpers/helpers.sh + +number_of_windows() { + tmux list-windows | + wc -l | + sed "s/ //g" +} + +main() { + # start tmux in the background + tmux new -d + tmux new-window + + local number_of_windows="$(number_of_windows)" + if ! [ "$number_of_windows" -eq 2 ]; then + fail_helper "Incorrect number of windows. Expected 2, got $number_of_windows" + fi + exit_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_centos_provisioning.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_centos_provisioning.sh new file mode 100644 index 0000000..6902d9c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_centos_provisioning.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# libevent2 installation instructions from here +# https://gist.github.com/rschuman/6168833 + +sudo su - + +yum -y install gcc kernel-devel make automake autoconf ncurses-devel +yum -y install git-core expect vim ruby ruby-devel ruby-irb + +# install libevent2 from source +curl http://sourceforge.net/projects/levent/files/latest/download?source=files -L -o libevent2.tar.gz -w 'Last URL was: %{url_effective}' +cd ~/downloads +tar zxvf libevent2.tar.gz +cd ./libevent-* +./configure --prefix=/usr/local +make +make install + +# compile tmux +git clone https://github.com/tmux/tmux.git ~/tmux_source +cd ~/tmux_source +git checkout 2.5 +sh autogen.sh +LDFLAGS="-L/usr/local/lib -Wl,-rpath=/usr/local/lib" ./configure --prefix=/usr/local +make && sudo make install diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_ubuntu_provisioning.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_ubuntu_provisioning.sh new file mode 100644 index 0000000..2a0d9e5 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/lib/tmux-test/vagrant_ubuntu_provisioning.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +sudo apt-get update +sudo apt-get install -y git-core expect vim +sudo apt-get install -y python-software-properties software-properties-common +sudo apt-get install -y build-essential libtool autotools-dev autoconf +sudo apt-get install -y pkg-config libevent-dev libncurses-dev +sudo apt-get install -y man-db + +# install tmux 2.5 +git clone https://github.com/tmux/tmux.git ~/tmux_source +cd ~/tmux_source +git checkout 2.5 +sh autogen.sh +./configure && make && sudo make install diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/resurrect.tmux b/dotfiles/common/.tmux/plugins/tmux-resurrect/resurrect.tmux new file mode 100755 index 0000000..21fed7e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/resurrect.tmux @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/gdb.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/gdb.sh new file mode 100755 index 0000000..2f0ab56 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/gdb.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/linux_procfs.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/linux_procfs.sh new file mode 100755 index 0000000..6b64f7e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/linux_procfs.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +PANE_PID="$1" +COMMAND_PID=$(pgrep -P $PANE_PID) + +exit_safely_if_empty_ppid() { + if [ -z "$PANE_PID" ]; then + exit 0 + fi +} + +full_command() { + [[ -z "$COMMAND_PID" ]] && exit 0 + # See: https://unix.stackexchange.com/a/567021 + # Avoid complications with system printf by using bash subshell interpolation. + # This will properly escape sequences and null in cmdline. + cat /proc/${COMMAND_PID}/cmdline | xargs -0 bash -c 'printf "%q " "$0" "$@"' +} + +main() { + exit_safely_if_empty_ppid + full_command +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/pgrep.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/pgrep.sh new file mode 100755 index 0000000..15d98b3 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/pgrep.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/ps.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/ps.sh new file mode 100755 index 0000000..15bb5aa --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/save_command_strategies/ps.sh @@ -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,args" | + sed "s/^ *//" | + grep "^${PANE_PID}" | + cut -d' ' -f2- +} + +main() { + exit_safely_if_empty_ppid + full_command +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/check_tmux_version.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/check_tmux_version.sh new file mode 100755 index 0000000..b0aedec --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/check_tmux_version.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/helpers.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/helpers.sh new file mode 100644 index 0000000..20d87dc --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/helpers.sh @@ -0,0 +1,159 @@ +if [ -d "$HOME/.tmux/resurrect" ]; then + default_resurrect_dir="$HOME/.tmux/resurrect" +else + default_resurrect_dir="${XDG_DATA_HOME:-$HOME/.local/share}"/tmux/resurrect +fi +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" +} + +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" +} + +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 +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/process_restore_helpers.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/process_restore_helpers.sh new file mode 100644 index 0000000..546dfe1 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/process_restore_helpers.sh @@ -0,0 +1,198 @@ +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}//" +} + +# given full command: 'ruby /Users/john/bin/my_program arg1 arg2' +# and inline strategy: '~bin/my_program->my_program *' +# returns: 'arg1 arg2' +_get_command_arguments() { + local pane_full_command="$1" + local match="$2" + if _proc_starts_with_tildae "$match"; then + match="$(remove_first_char "$match")" + fi + echo "$pane_full_command" | sed "s,^.*${match}[^ ]* *,," +} + +_get_proc_restore_command() { + local pane_full_command="$1" + local proc="$2" + local match="$3" + local restore_element="$(_get_proc_restore_element "$proc")" + if [[ "$restore_element" =~ " ${inline_strategy_arguments_token}" ]]; then + # replaces "%" with command arguments + local command_arguments="$(_get_command_arguments "$pane_full_command" "$match")" + echo "$restore_element" | sed "s,${inline_strategy_arguments_token},${command_arguments}," + else + echo "$restore_element" + fi +} + +_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_command "$pane_full_command" "$proc" "$match")" + 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" +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.exp b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.exp new file mode 100755 index 0000000..8664b1d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.exp @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.sh new file mode 100755 index 0000000..1a5e3f9 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/restore.sh @@ -0,0 +1,387 @@ +#!/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" ] +} + +restored_session_0_true() { + RESTORED_SESSION_0="true" +} + +has_restored_session_0() { + [ "$RESTORED_SESSION_0" == "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" "")" + local opt="" + if [ "$(basename "$default_shell")" == "bash" ]; then + opt="-l " + fi + export TMUX_DEFAULT_COMMAND="$(get_tmux_option "default-command" "$opt$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 dir="$3" + local pane_index="$4" + local pane_id="${session_name}:${window_number}.${pane_index}" + dir="${dir/#\~/$HOME}" + 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}" -c "$dir" "$pane_creation_command" + else + tmux new-window -d -t "${session_name}:${window_number}" -c "$dir" + fi +} + +new_session() { + local session_name="$1" + local window_number="$2" + local dir="$3" + local pane_index="$4" + 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" -c "$dir" "$pane_creation_command" + else + TMUX="" tmux -S "$(tmux_socket)" new-session -d -s "$session_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 dir="$3" + local pane_index="$4" + 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_active window_flags pane_index pane_title dir pane_active pane_command pane_full_command; do + dir="$(remove_first_char "$dir")" + pane_full_command="$(remove_first_char "$pane_full_command")" + if [ "$session_name" == "0" ]; then + restored_session_0_true + fi + if pane_exists "$session_name" "$window_number" "$pane_index"; then + 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" "$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 + new_pane "$session_name" "$window_number" "$dir" "$pane_index" + elif session_exists "$session_name"; then + new_window "$session_name" "$window_number" "$dir" "$pane_index" + else + new_session "$session_name" "$window_number" "$dir" "$pane_index" + fi + # set pane title + tmux select-pane -t "$session_name:$window_number.$pane_index" -T "$pane_title" + 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) +} + +handle_session_0() { + if is_restoring_from_scratch && ! has_restored_session_0; then + local current_session="$(tmux display -p "#{client_session}")" + if [ "$current_session" == "0" ]; then + tmux switch-client -n + fi + tmux kill-session -t "0" + fi +} + +restore_window_properties() { + local window_name + \grep '^window' $(last_resurrect_file) | + while IFS=$d read line_type session_name window_number window_name window_active window_flags window_layout automatic_rename; do + tmux select-layout -t "${session_name}:${window_number}" "$window_layout" + + # Below steps are properly handling window names and automatic-rename + # option. `rename-window` is an extra command in some scenarios, but we + # opted for always doing it to keep the code simple. + window_name="$(remove_first_char "$window_name")" + tmux rename-window -t "${session_name}:${window_number}" "$window_name" + if [ "${automatic_rename}" = ":" ]; then + tmux set-option -u -t "${session_name}:${window_number}" automatic-rename + else + tmux set-option -t "${session_name}:${window_number}" automatic-rename "$automatic_rename" + 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, $6, $8, $11; }' $(last_resurrect_file) | + while IFS=$d read -r 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, $6; }' $(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/ && $5 ~ /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/ && $6 ~ /[*-]/ { print $2, $5, $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) +} + +# A cleanup that happens after 'restore_all_panes' seems to fix fish shell +# users' restore problems. +cleanup_restored_pane_contents() { + if is_restoring_pane_contents; then + rm "$(pane_contents_dir "restore")"/* + fi +} + +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 + handle_session_0 + restore_window_properties >/dev/null 2>&1 + 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 + cleanup_restored_pane_contents + execute_hook "post-restore-all" + stop_spinner + display_message "Tmux restore complete!" + fi +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/save.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/save.sh new file mode 100755 index 0000000..01edcde --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/save.sh @@ -0,0 +1,278 @@ +#!/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_active}" + format+="${delimiter}" + format+=":#{window_flags}" + format+="${delimiter}" + format+="#{pane_index}" + format+="${delimiter}" + format+="#{pane_title}" + 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_name}" + 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 +} + +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_active window_flags pane_index pane_title 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_active}${d}${window_flags}${d}${pane_index}${d}${pane_title}${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_name window_active window_flags window_layout; do + # not saving windows from grouped sessions + if is_session_grouped "$session_name"; then + continue + fi + automatic_rename="$(tmux show-window-options -vt "${session_name}:${window_index}" automatic-rename)" + # If the option was unset, use ":" as a placeholder. + [ -z "${automatic_rename}" ] && automatic_rename=":" + echo "${line_type}${d}${session_name}${d}${window_index}${d}${window_name}${d}${window_active}${d}${window_flags}${d}${window_layout}${d}${automatic_rename}" + 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_active window_flags pane_index pane_title 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 +} + +remove_old_backups() { + # remove resurrect files older than 30 days (default), but keep at least 5 copies of backup. + local delete_after="$(get_tmux_option "$delete_backup_after_option" "$default_delete_backup_after")" + 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 "+${delete_after}" -exec rm -v "{}" \; > /dev/null +} + +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 + 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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/spinner_helpers.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/spinner_helpers.sh new file mode 100644 index 0000000..fe73cd7 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/spinner_helpers.sh @@ -0,0 +1,8 @@ +start_spinner() { + $CURRENT_DIR/tmux_spinner.sh "$1" "$2" & + export SPINNER_PID=$! +} + +stop_spinner() { + kill $SPINNER_PID +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/tmux_spinner.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/tmux_spinner.sh new file mode 100755 index 0000000..9b1b979 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/tmux_spinner.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/variables.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/variables.sh new file mode 100644 index 0000000..9d42e02 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/scripts/variables.sh @@ -0,0 +1,48 @@ +# 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 view 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="->" +inline_strategy_arguments_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" + +# 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-" + +delete_backup_after_option="@resurrect-delete-backup-after" +default_delete_backup_after="30" # days diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/irb_default_strategy.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/irb_default_strategy.sh new file mode 100755 index 0000000..897f5bb --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/irb_default_strategy.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/mosh-client_default_strategy.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/mosh-client_default_strategy.sh new file mode 100755 index 0000000..4d2f06b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/mosh-client_default_strategy.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/nvim_session.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/nvim_session.sh new file mode 100755 index 0000000..4987c68 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/nvim_session.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/vim_session.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/vim_session.sh new file mode 100755 index 0000000..1b5293c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/strategies/vim_session.sh @@ -0,0 +1,23 @@ +#!/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" ] +} + +main() { + if vim_session_file_exists; then + echo "vim -S" + else + echo "$ORIGINAL_COMMAND" + fi +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/restore_file.txt b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/restore_file.txt new file mode 100644 index 0000000..dcf5779 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/restore_file.txt @@ -0,0 +1,21 @@ +pane 0 0 :bash 1 :* 0 :/tmp 1 bash : +pane blue 0 :vim 0 : 0 :/tmp 1 vim :vim foo.txt +pane blue 1 :man 0 :- 0 :/tmp 0 bash : +pane blue 1 :man 0 :- 1 :/usr/share/man 1 man :man echo +pane blue 2 :bash 1 :* 0 :/tmp 1 bash : +pane red 0 :bash 0 : 0 :/tmp 1 bash : +pane red 1 :bash 0 :-Z 0 :/tmp 0 bash : +pane red 1 :bash 0 :-Z 1 :/tmp 0 bash : +pane red 1 :bash 0 :-Z 2 :/tmp 1 bash : +pane red 2 :bash 1 :* 0 :/tmp 0 bash : +pane red 2 :bash 1 :* 1 :/tmp 1 bash : +pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash : +window 0 0 1 :* ce9e,200x49,0,0,1 +window blue 0 0 : ce9f,200x49,0,0,2 +window blue 1 0 :- 178b,200x49,0,0{100x49,0,0,3,99x49,101,0,4} +window blue 2 1 :* cea2,200x49,0,0,5 +window red 0 0 : cea3,200x49,0,0,6 +window red 1 0 :-Z 135b,200x49,0,0[200x24,0,0,7,200x24,0,25{100x24,0,25,8,99x24,101,25,9}] +window red 2 1 :* db81,200x49,0,0[200x24,0,0,10,200x24,0,25,11] +window yellow 0 1 :* 6781,200x49,0,0,12 +state yellow blue diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/save_file.txt b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/save_file.txt new file mode 100644 index 0000000..0301f92 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/fixtures/save_file.txt @@ -0,0 +1,21 @@ +pane 0 0 :bash 1 :* 0 :/tmp 1 bash : +pane blue 0 :vim 0 :! 0 :/tmp 1 vim :vim foo.txt +pane blue 1 :man 0 :!- 0 :/tmp 0 bash : +pane blue 1 :man 0 :!- 1 :/usr/share/man 1 man :man echo +pane blue 2 :bash 1 :* 0 :/tmp 1 bash : +pane red 0 :bash 0 : 0 :/tmp 1 bash : +pane red 1 :bash 0 :-Z 0 :/tmp 0 bash : +pane red 1 :bash 0 :-Z 1 :/tmp 0 bash : +pane red 1 :bash 0 :-Z 2 :/tmp 1 bash : +pane red 2 :bash 1 :* 0 :/tmp 0 bash : +pane red 2 :bash 1 :* 1 :/tmp 1 bash : +pane yellow 0 :bash 1 :* 0 :/tmp/bar 1 bash : +window 0 0 1 :* ce9d,200x49,0,0,0 +window blue 0 0 :! cea4,200x49,0,0,7 +window blue 1 0 :!- 9797,200x49,0,0{100x49,0,0,8,99x49,101,0,9} +window blue 2 1 :* 677f,200x49,0,0,10 +window red 0 0 : ce9e,200x49,0,0,1 +window red 1 0 :-Z 52b7,200x49,0,0[200x24,0,0,2,200x24,0,25{100x24,0,25,3,99x24,101,25,4}] +window red 2 1 :* bd68,200x49,0,0[200x24,0,0,5,200x24,0,25,6] +window yellow 0 1 :* 6780,200x49,0,0,11 +state yellow blue diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/create_and_save_tmux_test_environment.exp b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/create_and_save_tmux_test_environment.exp new file mode 100755 index 0000000..80cca2c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/create_and_save_tmux_test_environment.exp @@ -0,0 +1,42 @@ +#!/usr/bin/env expect + +source "./tests/helpers/expect_helpers.exp" + +expect_setup + +spawn tmux +# delay with sleep to compensate for tmux starting time +sleep 1 + +run_shell_command "cd /tmp" + +# session red +new_tmux_session "red" + +new_tmux_window +horizontal_split +vertical_split +toggle_zoom_pane + +new_tmux_window +horizontal_split + +# session blue +new_tmux_session "blue" + +run_shell_command "touch foo.txt" +run_shell_command "vim foo.txt" + +new_tmux_window +vertical_split +run_shell_command "man echo" + +new_tmux_window + +# session yellow +new_tmux_session "yellow" +run_shell_command "cd /tmp/bar" + +start_resurrect_save + +run_shell_command "tmux kill-server" diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/expect_helpers.exp b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/expect_helpers.exp new file mode 100644 index 0000000..cbf4234 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/expect_helpers.exp @@ -0,0 +1,70 @@ +# a set of expect helpers + +# basic setup for each script +proc expect_setup {} { + # disables script output + log_user 0 + # standard timeout + set timeout 5 +} + +proc new_tmux_window {} { + send "c" + send "cd /tmp\r" + sleep 0.2 +} + +proc rename_current_session {name} { + send "$" + # delete existing name with ctrl-u + send "" + send "$name\r" + sleep 0.2 +} + +proc new_tmux_session {name} { + send "TMUX='' tmux new -d -s $name\r" + sleep 1 + send "tmux switch-client -t $name\r" + send "cd /tmp\r" + sleep 0.5 +} + +proc horizontal_split {} { + send "\"" + sleep 0.2 + send "cd /tmp\r" + sleep 0.1 +} + +proc vertical_split {} { + send "%" + sleep 0.2 + send "cd /tmp\r" + sleep 0.1 +} + +proc toggle_zoom_pane {} { + send "z" + sleep 0.2 +} + +proc run_shell_command {command} { + send "$command\r" + sleep 1 +} + +proc start_resurrect_save {} { + send "" + sleep 5 +} + +proc start_resurrect_restore {} { + send "" + sleep 10 +} + +proc clear_screen_for_window {target} { + send "tmux send-keys -t $target C-l\r" + sleep 0.2 +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/restore_and_save_tmux_test_environment.exp b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/restore_and_save_tmux_test_environment.exp new file mode 100755 index 0000000..82da37f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/restore_and_save_tmux_test_environment.exp @@ -0,0 +1,18 @@ +#!/usr/bin/env expect + +source "./tests/helpers/expect_helpers.exp" + +expect_setup + +spawn tmux +# delay with sleep to compensate for tmux starting time +sleep 1 + +start_resurrect_restore + +# delete all existing resurrect save files +run_shell_command "rm ~/.tmux/resurrect/*" + +start_resurrect_save + +run_shell_command "tmux kill-server" diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/resurrect_helpers.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/resurrect_helpers.sh new file mode 100644 index 0000000..268aca5 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/helpers/resurrect_helpers.sh @@ -0,0 +1,11 @@ +# we want "fixed" dimensions no matter the size of real display +set_screen_dimensions_helper() { + stty cols 200 + stty rows 50 +} + +last_save_file_differs_helper() { + local original_file="$1" + diff "$original_file" "${HOME}/.tmux/resurrect/last" + [ $? -ne 0 ] +} diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_restore.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_restore.sh new file mode 100755 index 0000000..9cf4644 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_restore.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $CURRENT_DIR/helpers/helpers.sh +source $CURRENT_DIR/helpers/resurrect_helpers.sh + +setup_before_restore() { + # setup restore file + mkdir -p ~/.tmux/resurrect/ + cp tests/fixtures/restore_file.txt "${HOME}/.tmux/resurrect/restore_file.txt" + ln -sf restore_file.txt "${HOME}/.tmux/resurrect/last" + + # directory used in restored tmux session + mkdir -p /tmp/bar +} + +restore_tmux_environment_and_save_again() { + set_screen_dimensions_helper + $CURRENT_DIR/helpers/restore_and_save_tmux_test_environment.exp +} + +main() { + install_tmux_plugin_under_test_helper + setup_before_restore + restore_tmux_environment_and_save_again + + if last_save_file_differs_helper "tests/fixtures/restore_file.txt"; then + fail_helper "Saved file not correct after restore" + fi + exit_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_save.sh b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_save.sh new file mode 100755 index 0000000..99fb925 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/tests/test_resurrect_save.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $CURRENT_DIR/helpers/helpers.sh +source $CURRENT_DIR/helpers/resurrect_helpers.sh + +create_tmux_test_environment_and_save() { + set_screen_dimensions_helper + $CURRENT_DIR/helpers/create_and_save_tmux_test_environment.exp +} + +main() { + install_tmux_plugin_under_test_helper + mkdir -p /tmp/bar # setup required dirs + create_tmux_test_environment_and_save + + if last_save_file_differs_helper "tests/fixtures/save_file.txt"; then + fail_helper "Saved file not correct (initial save)" + fi + exit_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/video/issue_vid.png b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/issue_vid.png new file mode 100644 index 0000000..8ea5ea2 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/issue_vid.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/video/screencast_img.png b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/screencast_img.png new file mode 100644 index 0000000..f5f3d83 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/screencast_img.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux-resurrect/video/script.md b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/script.md new file mode 100644 index 0000000..ef38824 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-resurrect/video/script.md @@ -0,0 +1,110 @@ +# Screencast script + +1. Intro +======== +Let's demo tmux resurrect plugin. + +Tmux resurrect enables persisting tmux sessions, so it can survive the dreaded +system restarts. + +The benefit is uninterrupted workflow with no configuration required. + +2. Working session +================== +Script +------ +Let me show you what I have in this tmux demo session. + +First of all, I have vim open and it has a couple files loaded. + +Then there's a tmux window with a couple splits in various directories across +the system. + +Next window contains tmux man page, + and then there's `htop` program. + +And this is just one of many projects I'm currently running. + +Actions +------- +- blank tmux window +- vim + - `ls` to show open files +- multiple pane windows (3) +- man tmux +- htop +- psql +- show a list of session + +3. Saving the environment +========================= +Script +------ +With vanilla tmux, when I restart the computer this whole environment will be +lost and I'll have to invest time to restore it. + +tmux resurrect gives you the ability to persist everything with +prefix plus alt-s. + +Now tmux environment is saved and I can safely shut down tmux with a +kill server command. + +Actions +------- +- prefix + M-s +- :kill-server + +4. Restoring the environment +============================ +Script +------ +At this point restoring everything back is easy. + +I'll fire up tmux again. Notice it's completely empty. + +Now, I'll press prefix plus alt-r and everything will restore. + +Let's see how things look now. +First of all, I'm back to the exact same window I was in when the environment +was saved. Second - you can see the `htop` program was restored. + +Going back there's tmux man page + a window with multiple panes with the exact same layout as before + and vim. + + +tmux resurrect takes special care of vim. By leveraging vim's sessions, it +preserves vim's split windows, open files, even the list of files edited before. + +Check out the project readme for more details about special treatment for vim. + +That was just one of the restored tmux sessions. If I open tmux session list you +can see all the other projects are restored as well. + + +When you see all these programs running you might be concerned that this plugin +started a lot of potentially destructive processes. + +For example, when you restore tmux you don't want to accidentally start backups, +resource intensive or sensitive programs. + +There's no need to be worried though. By default, this plugin starts only a +conservative list of programs like vim, less, tail, htop and similar. +This list of programs restored by default is in the project readme. Also, you +can easily add more programs to it. + +If you feel paranoid, there's an option that prevents restoring any program. + +Actions +------- +- tmux +- prefix + M-r + +- open previous windows +- in vim hit :ls + +- prefix + s for a list of panes + +5. Outro +======== +That's it for this demo. I hope you'll find tmux resurrect useful. diff --git a/dotfiles/common/.tmux/plugins/tmux-sensible/.gitattributes b/dotfiles/common/.tmux/plugins/tmux-sensible/.gitattributes new file mode 100644 index 0000000..4cde323 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-sensible/.gitattributes @@ -0,0 +1,2 @@ +# Force text files to have unix eols, so Windows/Cygwin does not break them +*.* eol=lf diff --git a/dotfiles/common/.tmux/plugins/tmux-sensible/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tmux-sensible/CHANGELOG.md new file mode 100644 index 0000000..579c0db --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-sensible/CHANGELOG.md @@ -0,0 +1,43 @@ +# Changelog + +### master +- remove `detach-on-destroy` +- do not set `aggressive-resize` on iTerm terminal +- disable `detach-on-destroy` + +### v3.0.0, 2015-06-24 +- remove 'almost sensible' feature + +### v2.3.0, 2015-06-24 +- update to support \*THE\* latest tmux version +- bugfix for `prefix + R` key binding +- fix for tmux 2.0 `default-terminal` option (thanks @kwbr) + +### v2.2.0, 2015-02-10 +- bugfix in `key_binding_not_set`: the regex is now properly detecting key + bindings with `-r` flag. +- enable `aggressive-resize` + +### v2.1.0, 2014-12-12 +- check before binding `prefix + prefix` (@m1foley) +- enable `focus-events` +- deprecate 'almost sensible' feature. The reason for this is to focus the + plugin on doing just one thing. + +### v2.0.0, 2014-10-03 +- bugfix: prevent exiting tmux if 'reattach-to-user-namespace' is not installed +- remove all mouse-related options +- introduce 'almost sensible' setting and options + +### v1.1.0, 2014-08-30 +- bugfix: determine the default shell from the $SHELL env var on OS X +- set `mode-mouse on` by default +- do not make any decision about the prefix, just enhance it +- update `README.md`. List options set in the plugin. +- do *not* set `mode-mouse on` by default because some users don't like it +- if a user changes default prefix but binds `C-b` to something else, do not + unbind `C-b` + +### v1.0.0, 2014-07-30 +- initial work on the plugin +- add readme diff --git a/dotfiles/common/.tmux/plugins/tmux-sensible/LICENSE.md b/dotfiles/common/.tmux/plugins/tmux-sensible/LICENSE.md new file mode 100644 index 0000000..40f6ddd --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-sensible/LICENSE.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tmux-sensible/README.md b/dotfiles/common/.tmux/plugins/tmux-sensible/README.md new file mode 100644 index 0000000..fff34fa --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-sensible/README.md @@ -0,0 +1,122 @@ +# Tmux sensible + +A set of tmux options that should be acceptable to everyone. + +Inspired by [vim-sensible](https://github.com/tpope/vim-sensible). + +Tested and working on Linux, OSX and Cygwin. + +### Principles + +- `tmux-sensible` options should be acceptable to **every** tmux user!
+ If any of the options bothers you, please open an issue and it will probably + be updated (or removed). +- if you think a new option should be added, feel free to open a pull request. +- **no overriding** of user defined settings.
+ Your existing `.tmux.conf` settings are respected and they won't be changed. + That way you can use `tmux-sensible` if you have a few specific options. + +### Goals + +- group standard tmux community options in one place +- remove clutter from your `.tmux.conf` +- educate new tmux users about basic options + +### Options + +```tmux +# Address vim mode switching delay (http://superuser.com/a/252717/65504) +set -s escape-time 0 + +# Increase scrollback buffer size from 2000 to 50000 lines +set -g history-limit 50000 + +# Increase tmux messages display duration from 750ms to 4s +set -g display-time 4000 + +# Refresh 'status-left' and 'status-right' more often, from every 15s to 5s +set -g status-interval 5 + +# (OS X) Fix pbcopy/pbpaste for old tmux versions (pre 2.6) +set -g default-command "reattach-to-user-namespace -l $SHELL" + +# Upgrade $TERM +set -g default-terminal "screen-256color" + +# Emacs key bindings in tmux command prompt (prefix + :) are better than +# vi keys, even for vim users +set -g status-keys emacs + +# Focus events enabled for terminals that support them +set -g focus-events on + +# Super useful when using "grouped sessions" and multi-monitor setup +setw -g aggressive-resize on +``` + +### Key bindings + +```tmux +# Easier and faster switching between next/prev window +bind C-p previous-window +bind C-n next-window +``` + +Above bindings enhance the default `prefix + p` and `prefix + n` bindings by +allowing you to hold `Ctrl` and repeat `a + p`/`a + n` (if your prefix is +`C-a`), which is a lot quicker. + +```tmux +# Source .tmux.conf as suggested in `man tmux` +bind R source-file '~/.tmux.conf' +``` + +"Adaptable" key bindings that build upon your `prefix` value: + +```tmux +# If prefix is 'C-a' +bind C-a send-prefix +bind a last-window +``` + +If prefix is `C-b`, above keys will be `C-b` and `b`.
+If prefix is `C-z`, above keys will be `C-z` and `z`... you get the idea. + +### 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-sensible' +``` + +Hit `prefix + I` to fetch the plugin and source it. That's it! + +### Manual Installation + +Clone the repo: + + $ git clone https://github.com/tmux-plugins/tmux-sensible ~/clone/path + +Add this line to the bottom of `.tmux.conf`: + +```tmux +run-shell ~/clone/path/sensible.tmux +``` + +Reload TMUX environment with `$ tmux source-file ~/.tmux.conf`, and that's it. + +### Other goodies + +You might also find these useful: + +- [copycat](https://github.com/tmux-plugins/tmux-copycat) + improve tmux search and reduce mouse usage +- [pain control](https://github.com/tmux-plugins/tmux-pain-control) + useful standard bindings for controlling panes +- [resurrect](https://github.com/tmux-plugins/tmux-resurrect) + persists tmux environment across system restarts + +### License + +[MIT](LICENSE.md) diff --git a/dotfiles/common/.tmux/plugins/tmux-sensible/sensible.tmux b/dotfiles/common/.tmux/plugins/tmux-sensible/sensible.tmux new file mode 100755 index 0000000..66db3c1 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux-sensible/sensible.tmux @@ -0,0 +1,168 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# used to match output from `tmux list-keys` +KEY_BINDING_REGEX="bind-key[[:space:]]\+\(-r[[:space:]]\+\)\?\(-T prefix[[:space:]]\+\)\?" + +is_osx() { + local platform=$(uname) + [ "$platform" == "Darwin" ] +} + +iterm_terminal() { + [[ "${TERM_PROGRAM}" =~ ^iTerm || "${LC_TERMINAL}" =~ ^iTerm ]] +} + +command_exists() { + local command="$1" + type "$command" >/dev/null 2>&1 +} + +# returns prefix key, e.g. 'C-a' +prefix() { + tmux show-option -gv prefix +} + +# if prefix is 'C-a', this function returns 'a' +prefix_without_ctrl() { + local prefix="$(prefix)" + echo "$prefix" | cut -d '-' -f2 +} + +option_value_not_changed() { + local option="$1" + local default_value="$2" + local option_value=$(tmux show-option -gv "$option") + [ "$option_value" == "$default_value" ] +} + +server_option_value_not_changed() { + local option="$1" + local default_value="$2" + local option_value=$(tmux show-option -sv "$option") + [ "$option_value" == "$default_value" ] +} + +key_binding_not_set() { + local key="${1//\\/\\\\}" + if $(tmux list-keys | grep -q "${KEY_BINDING_REGEX}${key}[[:space:]]"); then + return 1 + else + return 0 + fi +} + +key_binding_not_changed() { + local key="$1" + local default_value="$2" + if $(tmux list-keys | grep -q "${KEY_BINDING_REGEX}${key}[[:space:]]\+${default_value}"); then + # key still has the default binding + return 0 + else + return 1 + fi +} + +get_tmux_config() { + local tmux_config_xdg="${XDG_CONFIG_HOME:-$HOME/.config}/tmux/tmux.conf" + local tmux_config="$HOME/.tmux.conf" + + if [ -f "${tmux_config_xdg}" ]; then + echo "${tmux_config_xdg}" + else + echo ${tmux_config} + fi +} + +main() { + # OPTIONS + + # address vim mode switching delay (http://superuser.com/a/252717/65504) + if server_option_value_not_changed "escape-time" "500"; then + tmux set-option -s escape-time 0 + fi + + # increase scrollback buffer size + if option_value_not_changed "history-limit" "2000"; then + tmux set-option -g history-limit 50000 + fi + + # tmux messages are displayed for 4 seconds + if option_value_not_changed "display-time" "750"; then + tmux set-option -g display-time 4000 + fi + + # refresh 'status-left' and 'status-right' more often + if option_value_not_changed "status-interval" "15"; then + tmux set-option -g status-interval 5 + fi + + # required (only) on OS X + if is_osx && command_exists "reattach-to-user-namespace" && option_value_not_changed "default-command" ""; then + tmux set-option -g default-command "reattach-to-user-namespace -l $SHELL" + fi + + # upgrade $TERM, tmux 1.9 + if option_value_not_changed "default-terminal" "screen"; then + tmux set-option -g default-terminal "screen-256color" + fi + # upgrade $TERM, tmux 2.0+ + if server_option_value_not_changed "default-terminal" "screen"; then + tmux set-option -s default-terminal "screen-256color" + fi + + # emacs key bindings in tmux command prompt (prefix + :) are better than + # vi keys, even for vim users + tmux set-option -g status-keys emacs + + # focus events enabled for terminals that support them + tmux set-option -g focus-events on + + # super useful when using "grouped sessions" and multi-monitor setup + if ! iterm_terminal; then + tmux set-window-option -g aggressive-resize on + fi + + # DEFAULT KEY BINDINGS + + local prefix="$(prefix)" + local prefix_without_ctrl="$(prefix_without_ctrl)" + + # if C-b is not prefix + if [ $prefix != "C-b" ]; then + # unbind obsolete default binding + if key_binding_not_changed "C-b" "send-prefix"; then + tmux unbind-key C-b + fi + + # pressing `prefix + prefix` sends to the shell + if key_binding_not_set "$prefix"; then + tmux bind-key "$prefix" send-prefix + fi + fi + + # If Ctrl-a is prefix then `Ctrl-a + a` switches between alternate windows. + # Works for any prefix character. + if key_binding_not_set "$prefix_without_ctrl"; then + tmux bind-key "$prefix_without_ctrl" last-window + fi + + # easier switching between next/prev window + if key_binding_not_set "C-p"; then + tmux bind-key C-p previous-window + fi + if key_binding_not_set "C-n"; then + tmux bind-key C-n next-window + fi + + # source `.tmux.conf` file - as suggested in `man tmux` + if key_binding_not_set "R"; then + local tmux_config=$(get_tmux_config) + + tmux bind-key R run-shell " \ + tmux source-file ${tmux_config} > /dev/null; \ + tmux display-message 'Sourced ${tmux_config}!'" + fi +} +main diff --git a/dotfiles/common/.tmux/plugins/tmux/.editorconfig b/dotfiles/common/.tmux/plugins/tmux/.editorconfig new file mode 100644 index 0000000..d86ac02 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.editorconfig @@ -0,0 +1,34 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 2 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# go +[*.go] +indent_style = tab +indent_size = 4 + +# python +[*.{ini,py,py.tpl,rst}] +indent_size = 4 + +# rust +[*.rs] +indent_size = 4 + +# documentation, utils +[*.{md,mdx,diff}] +trim_trailing_whitespace = false + +# windows shell scripts +[*.{cmd,bat,ps1}] +end_of_line = crlf diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/bug.yml b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..6f312e7 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,46 @@ +name: Bug Report +description: Report your bugs with the theme here! +labels: [bug] +body: + - type: checkboxes + attributes: + label: Is there an existing issue outlining your problem? + description: Please search to see if an issue already exists for your problem. + options: + - label: I have searched the existing issues and they do not solve my problem. + required: true + - type: textarea + attributes: + label: Describe your problem. + description: Also tell us, what do you expect to see? + placeholder: The battery charging icon is missing... + validations: + required: true + - type: textarea + attributes: + label: Paste your configuration. + description: Provide us with the contents of your `.tmux.conf` file. + placeholder: | + ```sh + set -g @plugin 'catppuccin/tmux' + set -g @plugin 'tmux-plugins/tpm' + # ... + run ~/.config/tmux/tpm/tpm + ``` + validations: + required: true + - type: textarea + attributes: + label: Attach screenshots. + description: If applicable, attach screenshots which clearly highlight the issue. + - type: input + attributes: + label: What tmux version are you seeing the issue on? + description: "You can find your version by running `tmux -V`" + placeholder: tmux 3.3a + validations: + required: true + - type: textarea + attributes: + label: Any additional comments? + description: Add any information that hasn't been covered in the previous sections! diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/config.yml b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..0e1b590 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Ask A Question + url: https://github.com/catppuccin/tmux/discussions/new?category=q-a + about: Need help tweaking your Catppuccin tmux configuration? Ask questions in GitHub Discussions! + - name: Show & Tell + url: https://github.com/catppuccin/tmux/discussions/new?category=show-tell + about: Want to showcase your customised Catppuccin tmux configuration? Show it off in GitHub Discussions! + - name: Community Discord + url: https://discord.com/servers/catppuccin-907385605422448742 + about: Chat to other community members! diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/enhancement.yml b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/enhancement.yml new file mode 100644 index 0000000..1e2b053 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/enhancement.yml @@ -0,0 +1,18 @@ +name: Enhancement Issue +description: Request improvements to the theme here! +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Is there an existing issue outlining your improvement? + description: Please search to see if your improvement has already been raised as an issue. + options: + - label: I have searched the existing issues and my improvement has not been raised yet. + required: true + - type: textarea + attributes: + label: What would you like to see added and/or changed? + description: Make sure to mention why you think this is an improvement! + placeholder: I'd like to have an extra configuration option for... + validations: + required: true diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/meta.yml b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/meta.yml new file mode 100644 index 0000000..f471d4a --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/ISSUE_TEMPLATE/meta.yml @@ -0,0 +1,18 @@ +name: Meta Issue +description: Raise any issue regarding the repository here! +labels: [meta] +body: + - type: checkboxes + attributes: + label: Is there an existing issue outlining your problem? + description: Please search to see if an issue already exists for your problem. + options: + - label: I have searched the existing issues and they do not solve my problem. + required: true + - type: textarea + attributes: + label: Describe your issue. + description: Bugs should be raised under a [Bug Report](https://github.com/catppuccin/tmux/issues/new?assignees=&labels=bug&template=bug.yml). + placeholder: The README is missing crucial information such as... + validations: + required: true diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/release-please-config.json b/dotfiles/common/.tmux/plugins/tmux/.github/release-please-config.json new file mode 100644 index 0000000..47fe129 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/release-please-config.json @@ -0,0 +1,13 @@ +{ + "last-release-sha": "408c02ccf44d0a59a7a63ce2b65c5c29982c5c0e", + "packages": { + ".": { + "release-type": "simple", + "changelog-path": "CHANGELOG.md", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "draft": false + } + }, + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/release-please-manifest.json b/dotfiles/common/.tmux/plugins/tmux/.github/release-please-manifest.json new file mode 100644 index 0000000..466df71 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/scripts/test.bash b/dotfiles/common/.tmux/plugins/tmux/.github/scripts/test.bash new file mode 100755 index 0000000..bfe9c32 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/scripts/test.bash @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# +# Usage: +# ./test.bash catpuccin-tmux/catpuccin-tmux.tmux +# ./test.bash catpuccin-tmux/catpuccin-tmux.tmux ./config-example1 +# + +SOCKET_NAME="${SOCKET_NAME:-test}" + +tmux() { + command tmux -L "$SOCKET_NAME" "$@" +} + +start_tmux_server() { + echo "Starting tmux server" >&2 + tmux new -s dummy -d +} + +kill_tmux_server() { + echo "Stopping tmux server" >&2 + tmux kill-server +} + +check() { + local out exit_code + exit_code="$1" + out="$2" + + if [ "$exit_code" -ne 0 ] || [ -n "$out" ]; then + echo "Failed with exit code $exit_code and output:" + echo "$out" + exit 1 + fi + +} + +main() { + local config_file plugin output exit_code + + plugin="$1" + config_file="$2" + + echo "Using socket '$SOCKET_NAME'" >&2 + + # Make sure its a new/clean server + kill_tmux_server 2>/dev/null + start_tmux_server + + if [ -z "$config_file" ]; then + echo "No config file provided" >&2 + else + echo "sourcing config file '$config_file'" >&2 + tmux source-file "$config_file" + fi + + if [ -z "$plugin" ]; then + echo "ERROR: No path to the plugin provided" >&2 + exit 1 + else + echo "Running plugin: $plugin" >&2 + output="$(tmux run-shell "$plugin" 2>&1)" + exit_code="$?" + + check "$exit_code" "$output" + fi + + kill_tmux_server +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/workflows/release.yml b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/release.yml new file mode 100644 index 0000000..f19b845 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/release.yml @@ -0,0 +1,45 @@ +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +name: release-please + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + id: release + with: + token: ${{ secrets.GITHUB_TOKEN }} + config-file: .github/release-please-config.json + manifest-file: .github/release-please-manifest.json + - uses: actions/checkout@v4 + - name: tag major and minor versions + if: ${{ steps.release.outputs.release_created }} + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + + git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/googleapis/release-please-action.git" + + git tag -d latest || true + git tag -d v${{ steps.release.outputs.major }} || true + git tag -d v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }} || true + + git push origin :latest || true + git push origin :v${{ steps.release.outputs.major }} || true + git push origin :v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }} || true + + git tag -a latest -m "Release v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}" + git tag -a v${{ steps.release.outputs.major }} -m "Release v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}" + git tag -a v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }} -m "Release v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }}" + + git push origin latest + git push origin v${{ steps.release.outputs.major }} + git push origin v${{ steps.release.outputs.major }}.${{ steps.release.outputs.minor }} diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/workflows/shellcheck.yml b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..159df99 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/shellcheck.yml @@ -0,0 +1,32 @@ +--- +name: Shellcheck +permissions: + contents: read +on: + pull_request: + paths-ignore: + - "*.md" + - "assets/**" + push: + paths-ignore: + - "*.md" + - "assets/**" + branches: + - main + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + additional_files: "catppuccin.tmux" + env: + SHELLCHECK_OPTS: "-a" diff --git a/dotfiles/common/.tmux/plugins/tmux/.github/workflows/test.yml b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/test.yml new file mode 100644 index 0000000..2365de7 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.github/workflows/test.yml @@ -0,0 +1,51 @@ +--- +name: Tests +permissions: + contents: read +on: + pull_request: + paths-ignore: + - "*.md" + - "assets/**" + push: + paths-ignore: + - "*.md" + - "assets/**" + branches: + - main + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + ubuntu: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Install tmux + run: sudo apt-get update && sudo apt-get install -y tmux=3.2a-4ubuntu0.2 + - uses: actions/checkout@v4 + - name: Config and Plugin + shell: bash + run: | + bash --version + tmux -V + ./.github/scripts/test.bash ./catppuccin.tmux + + old-bash: + name: "Old Bash" + runs-on: ubuntu-latest + container: + image: bash:3.2.57-alpine3.19 # Bash version used by macos + steps: + - uses: actions/checkout@v4 + - name: Check Syntax is Valid + shell: bash + run: | + apk update + apk add tmux + bash --version + tmux -V + ./.github/scripts/test.bash ./catppuccin.tmux diff --git a/dotfiles/common/.tmux/plugins/tmux/.gitignore b/dotfiles/common/.tmux/plugins/tmux/.gitignore new file mode 100644 index 0000000..beab4f1 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.gitignore @@ -0,0 +1,3 @@ +custom +!custom/README.md +!custom/example.sh diff --git a/dotfiles/common/.tmux/plugins/tmux/.shellcheckrc b/dotfiles/common/.tmux/plugins/tmux/.shellcheckrc new file mode 100644 index 0000000..3f7e5f5 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/.shellcheckrc @@ -0,0 +1,6 @@ +shell=bash + +# TODO: Find a way to declare color variables +disable=SC2154 + +external-sources=true diff --git a/dotfiles/common/.tmux/plugins/tmux/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tmux/CHANGELOG.md new file mode 100644 index 0000000..34bd71e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## [0.1.0](https://github.com/catppuccin/tmux/compare/v0.0.1...v0.1.0) (2024-08-04) + + +### Features + +* releases ([#260](https://github.com/catppuccin/tmux/issues/260)) ([5fbacdf](https://github.com/catppuccin/tmux/commit/5fbacdf3559cf4496eef02aead087b3bb715e570)) diff --git a/dotfiles/common/.tmux/plugins/tmux/CONTRIBUTING.md b/dotfiles/common/.tmux/plugins/tmux/CONTRIBUTING.md new file mode 100644 index 0000000..86dd981 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +## Adding a module + +Follow the instructions from [custom](/custom), place the module in the appropriate +directory and document the module in `README.md`. + +## Commit messages + +This repository uses [Conventional Commits](https://conventionalcommits.org). +Commit headers should be lowercase. Most commits should include a body that briefly +describes the motivation and content of the commit. + +### Commit types + +- `fix`: A bug fix that doesn't modify the public API +- `feat`: A code change that modifies the public API +- `refactor`: A code change that doesn't change behavior +- `style`: A style fix or change +- `docs`: Any change to documentation +- `ci`: Any change to CI files +- `revert`: A revert commit. The message should describe the reasoning and the + commit should include the `Refs:` footer with the short hashes of the commits + being reverted. +- `chore`: catch-all type + +### Commit scopes + +Available commit scopes are module names, `status`, `pane`, and +`window`. If none of these apply, omit the scope. + +### Breaking changes + +All breaking changes should be documented in the commit footer in the format +described by Conventional Commits. Use the `!` syntax in order to distinguish +breaking commits in the log, but include the footer to provide a better description +for the changelog generator. + +``` +feat(bar)!: foo the bars + +BREAKING CHANGE: bars are now foo'ed +``` diff --git a/dotfiles/common/.tmux/plugins/tmux/LICENSE b/dotfiles/common/.tmux/plugins/tmux/LICENSE new file mode 100644 index 0000000..006383b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Catppuccin + +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. diff --git a/dotfiles/common/.tmux/plugins/tmux/README.md b/dotfiles/common/.tmux/plugins/tmux/README.md new file mode 100644 index 0000000..d279a3f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/README.md @@ -0,0 +1,625 @@ +

+ Logo
+ + Catppuccin for Tmux + +

+ +

+ + + +

+ +

+ +

+ +## Content + +1. [Themes](#themes) +1. [Installation](#installation) +1. [Overview](#overview) +1. [Configuration options](#configuration-options) + 1. [Window](#window) + 1. [Window default](#window-default) + 1. [Window current](#window-current) + 1. [Status](#status) + 1. [Pane](#pane) + 1. [Customizing modules](#customizing-modules) + 1. [Battery module](#battery-module) + 1. [CPU module](#CPU-module) + 1. [Weather modules](#weather-modules) + 1. [Load module](#load-module) +1. [Create a custom module](#create-a-custom-module) +1. [Configuration Examples](#configuration-examples) + 1. [Config 1](#config-1) + 1. [Config 2](#config-2) + 1. [Config 3](#config-3) + +## Themes + +- 🌻 [Latte](./themes/catppuccin_latte.tmuxtheme) +- 🪴 [Frappé](./themes/catppuccin_frappe.tmuxtheme) +- 🌺 [Macchiato](./themes/catppuccin_macchiato.tmuxtheme) +- 🌿 [Mocha](./themes/catppuccin_mocha.tmuxtheme) + +## Installation + +In order to have the icons displayed correctly please use/update your favorite [patched font](https://www.nerdfonts.com/font-downloads). +If you do not have a patched font installed, you can override or remove any icon. Check the documentation below on the options available. + +### TPM + +1. Install [TPM](https://github.com/tmux-plugins/tpm) +2. Add the Catppuccin plugin: + +```bash +set -g @plugin 'catppuccin/tmux#latest' # See https://github.com/catppuccin/tmux/tags for additional tags +# set -g @plugin 'catppuccin/tmux' # main branch +# ...alongside +set -g @plugin 'tmux-plugins/tpm' +``` + +3. (Optional) Set your preferred flavor, it defaults to `"mocha"`: + +```bash +set -g @catppuccin_flavour 'mocha' # latte,frappe, macchiato or mocha +``` + +### Manual + +1. Clone this repository to your desired location (e.g. + `~/.config/tmux/plugins/catppuccin`) +2. Add the following line to your `tmux.conf` file: + `run ~/.config/tmux/plugins/catppuccin/catppuccin.tmux` +3. (Optional) Set your preferred flavor and/or add configuration options as + listed in [Configuration Options](#configuration-options). +4. Reload Tmux by either restarting the session or reloading it with `tmux source-file ~/.tmux.conf` + +## Overview + +![Default](./assets/overview.png) +This is a diagram of how the theme is split between its components. + +## Configuration options + +All flavors support certain levels of customization that match our [Catppuccin +Style Guide][style-guide]. To add these customizations, add any of the following +options to your Tmux configuration. + +### Window + +### Set the window separator +```sh +set -g @catppuccin_window_separator "" +``` + +#### Set the window left separator: +```sh +set -g @catppuccin_window_left_separator "█" +``` + +#### Set the window middle separator: +```sh +set -g @catppuccin_window_middle_separator "█" +``` + +#### Set the window right separator: +```sh +set -g @catppuccin_window_right_separator "█" +``` + +#### Position the number: +```sh +set -g @catppuccin_window_number_position "left" +``` +Values: +- left - the number will be on the left part of the window +- right - the number will be on the right part of the window + +#### Enable window status: +```sh +set -g @catppuccin_window_status_enable "no" +``` +Values: +- yes - this will enable the window status part +- no - this will disable the window status part + +#### Enable window status icons instead of text: +```sh +set -g @catppuccin_window_status_icon_enable "yes" +``` +Values: +- yes - this will replace the windows status text with icons +- no - this will keep the windows status in text format + +#### Override windows status icons +```sh +set -g @catppuccin_icon_window_last "󰖰" +set -g @catppuccin_icon_window_current "󰖯" +set -g @catppuccin_icon_window_zoom "󰁌" +set -g @catppuccin_icon_window_mark "󰃀" +set -g @catppuccin_icon_window_silent "󰂛" +set -g @catppuccin_icon_window_activity "󱅫" +set -g @catppuccin_icon_window_bell "󰂞" +``` + +### Window default + +#### Set the window default color fill: +```sh +set -g @catppuccin_window_default_fill "number" +``` +Values: +- number - only the number of the window part will have color +- all - the entire window part will have the same color +- none - the entire window part will have no color + +#### Override the window default colors: +```sh +set -g @catppuccin_window_default_color "#{thm_blue}" # text color +set -g @catppuccin_window_default_background "#{thm_gray}" +``` + +Values: +- color - a theme color (`#{thm_}`) or hexadecimal color value + +#### Override the window default text: +```sh +set -g @catppuccin_window_default_text "#{b:pane_current_path}" # use "#W" for application instead of directory +``` + +### Window current + +#### Set the window current color fill: +```sh +set -g @catppuccin_window_current_fill "number" +``` +Values: +- number - only the number of the window part will have color +- all - the entire window part will have the same color +- none - the entire window part will have no color + +#### Override the window current colors: +```sh +set -g @catppuccin_window_current_color "#{thm_orange}" # text color +set -g @catppuccin_window_current_background "#{thm_bg}" +``` +Note that color and background fields are swapped when `@catppuccin_window_current_fill` is set to "all". + +Values: +- color - a theme color (`#{thm_}`) or a hexadecimal color value + +#### Override the window current text: +```sh +set -g @catppuccin_window_current_text "#{b:pane_current_path}" # use "#W" for application instead of directory +``` + +### Pane + +#### Set the pane border style: + +```sh +set -g @catppuccin_pane_border_style "fg=#{thm_gray}" # Use a value compatible with the standard tmux 'pane-border-style' +``` + +#### Set the pane active border style: + +```sh +set -g @catppuccin_pane_active_border_style "fg=#{thm_orange}" # Use a value compatible with the standard tmux 'pane-border-active-style' +``` + +### Menu + +#### Set the menu style: + +```sh +set -g @catppuccin_menu_style "default" # Use a value compatible with the standard tmux `menu-style` +``` + +#### Set the menu selected style: + +```sh +set -g @catppuccin_menu_selected_style "fg=#{thm_gray},bg=#{thm_yellow}" # Use a value compatible with the standard tmux `menu-selected-style` +``` + +### Set the menu border style: + +```sh +set -g @catppuccin_menu_border_style "default" # Use a value compatible with the standard tmux `menu-border-style` +``` + +### Status +#### Set the default status bar visibility +```sh +set -g @catppuccin_status_default "on" + +``` + +#### Override the default status background color +```sh +set -g @catppuccin_status_background "theme" +``` +This will overwrite the status bar background: +- "theme" will use the color from the selected theme +- "default" will make the status bar transparent +- use hex color codes for other colors or a theme color (`#{thm_}`) + +Note: you need to restart tmux for this to take effect: +```sh +tmux kill-server & tmux +``` + +#### Set the status module left separator: +```sh +set -g @catppuccin_status_left_separator "" +``` + +#### Set the status module right separator: +```sh +set -g @catppuccin_status_right_separator "█" +``` + +#### Set the status connect separator: +```sh +set -g @catppuccin_status_connect_separator "yes" +``` +Values: +- yes - the background color of the separator will not blend in with the background color of tmux +- no - the background color of the separator will blend in with the background color of tmux + + +#### Set the status module color fill: +```sh +set -g @catppuccin_status_fill "icon" +``` +Values: +- icon - only the icon of the module will have color +- all - the entire module will have the same color + +#### Set the status module justify value: +```sh +set -g @catppuccin_status_justify "left" +``` +Values: +- left +- centre - puts the window list in the relative centre of the available free space +- right +- absolute-centre - uses the centre of the entire horizontal space + +### Pane + +```sh +set -g @catppuccin_pane_status_enabled "no" +set -g @catppuccin_pane_border_status "off" # See `pane-border-status` +set -g @catppuccin_pane_left_separator "█" +set -g @catppuccin_pane_right_separator "█" +set -g @catppuccin_pane_middle_separator "█" +set -g @catppuccin_pane_number_position "left" +set -g @catppuccin_pane_default_fill "number" +set -g @catppuccin_pane_default_text "#{b:pane_current_path}" +set -g @catppuccin_pane_border_style "fg=#{thm_orange}" +set -g @catppuccin_pane_active_border_style "fg=#{thm_orange}" +set -g @catppuccin_pane_color "#{thm_orange}" +set -g @catppuccin_pane_background_color "#{thm_orange}" +``` + +#### Set the module list +```sh +set -g @catppuccin_status_modules_right "application session" +set -g @catppuccin_status_modules_left "null" +``` +Provide a list of modules and the order in which you want them to appear in the status. + +Available modules: +- application - display the current window running application +- directory - display the basename of the current window path +- session - display the number of tmux sessions running +- user - display the username +- host - display the hostname +- date_time - display the date and time +- uptime - display the uptime +- [battery](#battery-module) - display the battery + +### Customizing modules + +Every module (except the module "session") supports the following overrides: + +#### Override the specific module icon +```sh +set -g @catppuccin_[module_name]_icon "icon" +``` + +#### Override the specific module color +```sh +set -g @catppuccin_[module_name]_color "color" +``` + +#### Override the specific module text +```sh +set -g @catppuccin_[module_name]_text "text" +``` + +#### Removing a specific module option +```sh +set -g @catppuccin_[module_name]_[option] "null" +``` +This is for the situation where you want to remove the icon from a module. +Ex: +```sh +set -g @catppuccin_date_time_icon "null" +``` + +### Battery module + +#### Requirements +This module depends on [tmux-battery](https://github.com/tmux-plugins/tmux-battery/tree/master). + +#### Install +The preferred way to install tmux-battery is using [TPM](https://github.com/tmux-plugins/tpm). + +#### Configure +Load tmux-battery after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'tmux-plugins/tmux-battery' +``` + +Add the battery module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... battery ..." +``` + +### CPU module + +#### Requirements +This module depends on [tmux-cpu](https://github.com/tmux-plugins/tmux-cpu/tree/master). + +#### Install +The preferred way to install tmux-cpu is using [TPM](https://github.com/tmux-plugins/tpm). + +#### Configure +Load tmux-cpu after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'tmux-plugins/tmux-cpu' +``` + +Add the cpu module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... cpu ..." +``` + +### Weather modules + +#### tmux-weather + +##### Requirements +This module depends on [tmux-weather](https://github.com/xamut/tmux-weather). + +##### Install +The preferred way to install tmux-weather is using [TPM](https://github.com/tmux-plugins/tpm). + +##### Configure +Load tmux-weather after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'xamut/tmux-weather' +``` + +Add the weather module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... weather ..." +``` + +#### tmux-clima + +##### Requirements +This module depends on [tmux-clima](https://github.com/vascomfnunes/tmux-clima). + +##### Install +The preferred way to install tmux-clima is using [TPM](https://github.com/tmux-plugins/tpm). + +##### Configure +Load tmux-clima after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'vascomfnunes/tmux-clima' +``` + +Add the weather module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... clima ..." +``` + +### Load module + +#### Requirements +This module depends on [tmux-loadavg](https://github.com/jamesoff/tmux-loadavg). + +#### Install +The preferred way to install tmux-loadavg is using [TPM](https://github.com/tmux-plugins/tpm). + +#### Configure +Load tmux-loadavg after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'jamesoff/tmux-loadavg' +``` + +Add the load module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... load ..." +``` + +### Gitmux module + +#### Requirements +This module depends on [gitmux](https://github.com/arl/gitmux). + +#### Install +To install gitmux, follow the instructions in the [gitmux documentation](https://github.com/arl/gitmux/blob/main/README.md#installing). + +#### Configure +Add the gitmux module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... gitmux ..." +``` + +To customize the gitmux module, you can follow the instrucctions in the [gitmux documentation](https://github.com/arl/gitmux/blob/main/README.md#customizing) and add this line in your tmux configuration: +```sh +set -g @catppuccin_gitmux_text "#(gitmux -cfg $HOME/.gitmux.conf \"#{pane_current_path}\")" +``` + +### Pomodoro module + +#### Requirements +This module depends on [tmux-pomodoro-plus](https://github.com/olimorris/tmux-pomodoro-plus/tree/main). + +#### Install +The preferred way to install tmux-pomodoro-plus is using [TPM](https://github.com/tmux-plugins/tpm). + +#### Configure +Load tmux-pomodoro-plus after you load catppuccin. +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'olimorris/tmux-pomodoro-plus' +``` + +Add the pomodoro module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... pomodoro_plus ..." +``` + + +### Kube module + +#### Requirements +This module depends on [kube-tmux](https://github.com/jonmosco/kube-tmux). + +#### Install +The preferred way to install kube-tmux is using [TPM](https://github.com/tmux-plugins/tpm). + +#### Configure +```sh +set -g @plugin 'catppuccin/tmux' +... +set -g @plugin 'jonmosco/kube-tmux' +``` + +Add the tmux module to the status modules list. +```sh +set -g @catppuccin_status_modules_right "... kube ..." +``` + +Optionally override the kube-tmux colors +```sh +set -g @catppuccin_kube_context_color "#{thm_red}" +set -g @catppuccin_kube_namespace_color "#{thm_cyan}" +``` + + +## Create a custom module + +It is possible to add a new custom module or overwrite any of the existing modules. + +For further details, see the documentation in [custom/README.md](custom/README.md) + +Any file added to the custom folder will be preserved when updating catppuccin. + +## Configuration Examples +Below are provided a few configurations as examples or starting points. + +Note: +When switching between configurations run: +```sh +tmux kill-server +``` +To kill the tmux server and clear all global variables. + + +### Config 1 +![Default](./assets/config1.png) + +```sh +set -g @catppuccin_window_right_separator "█ " +set -g @catppuccin_window_number_position "right" +set -g @catppuccin_window_middle_separator " | " + +set -g @catppuccin_window_default_fill "none" + +set -g @catppuccin_window_current_fill "all" + +set -g @catppuccin_status_modules_right "application session user host date_time" +set -g @catppuccin_status_left_separator "█" +set -g @catppuccin_status_right_separator "█" + +set -g @catppuccin_date_time_text "%Y-%m-%d %H:%M:%S" +``` + +### Config 2 +![Default](./assets/config2.png) + +```sh +set -g @catppuccin_window_left_separator "█" +set -g @catppuccin_window_right_separator "█ " +set -g @catppuccin_window_number_position "right" +set -g @catppuccin_window_middle_separator " █" + +set -g @catppuccin_window_default_fill "number" + +set -g @catppuccin_window_current_fill "number" +set -g @catppuccin_window_current_text "#{pane_current_path}" + +set -g @catppuccin_status_modules_right "application session date_time" +set -g @catppuccin_status_left_separator "" +set -g @catppuccin_status_right_separator " " +set -g @catppuccin_status_fill "all" +set -g @catppuccin_status_connect_separator "yes" +``` + +### Config 3 +![Default](./assets/config3.png) + +```sh +set -g @catppuccin_window_left_separator "" +set -g @catppuccin_window_right_separator " " +set -g @catppuccin_window_middle_separator " █" +set -g @catppuccin_window_number_position "right" + +set -g @catppuccin_window_default_fill "number" +set -g @catppuccin_window_default_text "#W" + +set -g @catppuccin_window_current_fill "number" +set -g @catppuccin_window_current_text "#W" + +set -g @catppuccin_status_modules_right "directory user host session" +set -g @catppuccin_status_left_separator " " +set -g @catppuccin_status_right_separator "" +set -g @catppuccin_status_fill "icon" +set -g @catppuccin_status_connect_separator "no" + +set -g @catppuccin_directory_text "#{pane_current_path}" +``` + +[style-guide]: https://github.com/catppuccin/catppuccin/blob/main/docs/style-guide.md + +## 💝 Thanks to + +- [Pocco81](https://github.com/catppuccin) +- [vinnyA3](https://github.com/vinnyA3) +- [rogeruiz](https://github.com/rogeruiz) + +  + +

+

Copyright © 2021-present Catppuccin Org +

+ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/config1.png b/dotfiles/common/.tmux/plugins/tmux/assets/config1.png new file mode 100644 index 0000000..6eede6d Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/config1.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/config2.png b/dotfiles/common/.tmux/plugins/tmux/assets/config2.png new file mode 100644 index 0000000..5695890 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/config2.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/config3.png b/dotfiles/common/.tmux/plugins/tmux/assets/config3.png new file mode 100644 index 0000000..431a4ed Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/config3.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/frappe.webp b/dotfiles/common/.tmux/plugins/tmux/assets/frappe.webp new file mode 100644 index 0000000..64c14f8 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/frappe.webp differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/latte.webp b/dotfiles/common/.tmux/plugins/tmux/assets/latte.webp new file mode 100644 index 0000000..6e48ac9 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/latte.webp differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/macchiato.webp b/dotfiles/common/.tmux/plugins/tmux/assets/macchiato.webp new file mode 100644 index 0000000..df3aa04 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/macchiato.webp differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/mocha.webp b/dotfiles/common/.tmux/plugins/tmux/assets/mocha.webp new file mode 100644 index 0000000..d9ee469 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/mocha.webp differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/overview.png b/dotfiles/common/.tmux/plugins/tmux/assets/overview.png new file mode 100644 index 0000000..f26e899 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/overview.png differ diff --git a/dotfiles/common/.tmux/plugins/tmux/assets/preview.webp b/dotfiles/common/.tmux/plugins/tmux/assets/preview.webp new file mode 100644 index 0000000..3224dc1 Binary files /dev/null and b/dotfiles/common/.tmux/plugins/tmux/assets/preview.webp differ diff --git a/dotfiles/common/.tmux/plugins/tmux/builder/module_builder.sh b/dotfiles/common/.tmux/plugins/tmux/builder/module_builder.sh new file mode 100644 index 0000000..cfb9e5e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/builder/module_builder.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +build_status_module() { + local index="$1" + local icon="$2" + local color="$3" + local text="$4" + + if [ "$status_fill" = "icon" ]; then + local bg + local show_icon="#[fg=$thm_bg,bg=$color,nobold,nounderscore,noitalics]$icon " + local show_text="#[fg=$thm_fg,bg=$thm_gray] $text" + + if [ "$status_connect_separator" = "yes" ]; then + bg="$thm_gray" + else + bg="default" + fi + + local show_left_separator="#[fg=$color,bg=$bg,nobold,nounderscore,noitalics]$status_left_separator" + local show_right_separator="#[fg=$thm_gray,bg=$bg,nobold,nounderscore,noitalics]$status_right_separator" + fi + + if [ "$status_fill" = "all" ]; then + local show_icon="#[fg=$thm_bg,bg=$color,nobold,nounderscore,noitalics]$icon " + local show_text="#[fg=$thm_bg,bg=$color]$text" + + if [ "$status_connect_separator" = "yes" ]; then + local show_left_separator="#[fg=$color,nobold,nounderscore,noitalics]$status_left_separator" + local show_right_separator="#[fg=$color,bg=$color,nobold,nounderscore,noitalics]$status_right_separator" + + else + local show_left_separator="#[fg=$color,bg=default,nobold,nounderscore,noitalics]$status_left_separator" + local show_right_separator="#[fg=$color,bg=default,nobold,nounderscore,noitalics]$status_right_separator" + fi + + fi + + if [ $((index)) -eq 0 ]; then + local show_left_separator="#[fg=$color,bg=default,nobold,nounderscore,noitalics]$status_left_separator" + fi + + if [ -z "$icon" ] ; then + show_icon="" + fi + + echo "$show_left_separator$show_icon$show_text$show_right_separator" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/builder/pane_builder.sh b/dotfiles/common/.tmux/plugins/tmux/builder/pane_builder.sh new file mode 100644 index 0000000..fa97805 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/builder/pane_builder.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +build_pane_format() { + local number=$1 + local color=$2 + local background=$3 + local text=$4 + local fill=$5 + + if [ "$pane_status_enable" = "yes" ] + then + if [ "$fill" = "none" ] + then + local show_left_separator="#[fg=$thm_gray,bg=default,nobold,nounderscore,noitalics]$pane_left_separator" + local show_number="#[fg=$thm_fg,bg=$thm_gray]$number" + local show_middle_separator="#[fg=$thm_fg,bg=$thm_gray,nobold,nounderscore,noitalics]$pane_middle_separator" + local show_text="#[fg=$thm_fg,bg=$thm_gray]$text" + local show_right_separator="#[fg=$thm_gray,bg=default]$pane_right_separator" + fi + + if [ "$fill" = "all" ] + then + local show_left_separator="#[fg=$color,bg=default,nobold,nounderscore,noitalics]$pane_left_separator" + local show_number="#[fg=$background,bg=$color]$number" + local show_middle_separator="#[fg=$background,bg=$color,nobold,nounderscore,noitalics]$pane_middle_separator" + local show_text="#[fg=$background,bg=$color]$text" + local show_right_separator="#[fg=$color,bg=default]$pane_right_separator" + fi + + if [ "$fill" = "number" ] + then + local show_number="#[fg=$background,bg=$color]$number" + local show_middle_separator="#[fg=$color,bg=$background,nobold,nounderscore,noitalics]$pane_middle_separator" + local show_text="#[fg=$thm_fg,bg=$background]$text" + + if [ "$pane_number_position" = "right" ] + then + local show_left_separator="#[fg=$background,bg=default,nobold,nounderscore,noitalics]$pane_left_separator" + local show_right_separator="#[fg=$color,bg=default]$pane_right_separator" + fi + + if [ "$pane_number_position" = "left" ] + then + local show_right_separator="#[fg=$background,bg=default,nobold,nounderscore,noitalics]$pane_right_separator" + local show_left_separator="#[fg=$color,bg=default]$pane_left_separator" + fi + + fi + + local final_pane_format + + if [ "$pane_number_position" = "right" ] + then + final_pane_format="$show_left_separator$show_text$show_middle_separator$show_number$show_right_separator" + fi + + if [ "$pane_number_position" = "left" ] + then + final_pane_format="$show_left_separator$show_number$show_middle_separator$show_text$show_right_separator" + fi + + echo "$final_pane_format" + fi +} diff --git a/dotfiles/common/.tmux/plugins/tmux/builder/window_builder.sh b/dotfiles/common/.tmux/plugins/tmux/builder/window_builder.sh new file mode 100644 index 0000000..186b169 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/builder/window_builder.sh @@ -0,0 +1,127 @@ +#!/bin/sh + +build_window_format() { + local number="$1" + local color="$2" + local background="$3" + local text="$4" + local fill="$5" + + if [ "$window_status_enable" = "yes" ]; then + local icon + icon="$(build_window_icon)" + text="$text$icon" + fi + + if [ "$fill" = "none" ]; then + local show_number="#[fg=$thm_fg,bg=$thm_gray]$number" + local show_middle_separator="#[fg=$thm_fg,bg=$thm_gray,nobold,nounderscore,noitalics]$window_middle_separator" + local show_text="#[fg=$thm_fg,bg=$thm_gray]$text" + + if [ "$status_connect_separator" = "yes" ]; then + local show_left_separator="#[fg=$thm_gray,bg=$thm_bg,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$thm_gray,bg=$thm_bg]$window_right_separator" + + else + local show_left_separator="#[fg=$thm_gray,bg=default,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$thm_gray,bg=default]$window_right_separator" + + fi + + fi + + if [ "$fill" = "all" ]; then + local show_number="#[fg=$background,bg=$color]$number" + local show_middle_separator="#[fg=$background,bg=$color,nobold,nounderscore,noitalics]$window_middle_separator" + local show_text="#[fg=$background,bg=$color]$text" + + if [ "$status_connect_separator" = "yes" ]; then + local show_left_separator="#[fg=$color,bg=$thm_bg,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$color,bg=$thm_bg]$window_right_separator" + + else + local show_left_separator="#[fg=$color,bg=default,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$color,bg=default]$window_right_separator" + + fi + + fi + + if [ "$fill" = "number" ]; then + local show_number="#[fg=$background,bg=$color]$number" + local show_middle_separator="#[fg=$color,bg=$background,nobold,nounderscore,noitalics]$window_middle_separator" + local show_text="#[fg=$thm_fg,bg=$background]$text" + + if [ "$window_number_position" = "right" ]; then + if [ "$status_connect_separator" = "yes" ]; then + local show_left_separator="#[fg=$background,bg=$thm_bg,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$color,bg=$thm_bg]$window_right_separator" + + else + local show_left_separator="#[fg=$background,bg=default,nobold,nounderscore,noitalics]$window_left_separator" + local show_right_separator="#[fg=$color,bg=default]$window_right_separator" + + fi + fi + + if [ "$window_number_position" = "left" ]; then + if [ "$status_connect_separator" = "yes" ]; then + local show_right_separator="#[fg=$background,bg=$thm_bg,nobold,nounderscore,noitalics]$window_right_separator" + local show_left_separator="#[fg=$color,bg=$thm_bg]$window_left_separator" + + else + local show_right_separator="#[fg=$background,bg=default,nobold,nounderscore,noitalics]$window_right_separator" + local show_left_separator="#[fg=$color,bg=default]$window_left_separator" + + fi + + fi + + fi + + local final_window_format + + if [ "$window_number_position" = "right" ]; then + final_window_format="$show_left_separator$show_text$show_middle_separator$show_number$show_right_separator" + fi + + if [ "$window_number_position" = "left" ]; then + final_window_format="$show_left_separator$show_number$show_middle_separator$show_text$show_right_separator" + fi + + echo "$final_window_format" +} + +build_window_icon() { + local window_status_icon_enable custom_icon_window_last \ + custom_icon_window_zoom custom_icon_window_mark custom_icon_window_mark \ + custom_icon_window_silent custom_icon_window_activity custom_icon_window_bell + + window_status_icon_enable=$(get_tmux_option "@catppuccin_window_status_icon_enable" "yes") + custom_icon_window_last=$(get_tmux_option "@catppuccin_icon_window_last" "󰖰") + custom_icon_window_current=$(get_tmux_option "@catppuccin_icon_window_current" "󰖯") + custom_icon_window_zoom=$(get_tmux_option "@catppuccin_icon_window_zoom" "󰁌") + custom_icon_window_mark=$(get_tmux_option "@catppuccin_icon_window_mark" "󰃀") + custom_icon_window_silent=$(get_tmux_option "@catppuccin_icon_window_silent" "󰂛") + custom_icon_window_activity=$(get_tmux_option "@catppuccin_icon_window_activity" "󱅫") + custom_icon_window_bell=$(get_tmux_option "@catppuccin_icon_window_bell" "󰂞") + + if [ "$window_status_icon_enable" = "yes" ]; then + # #!~[*-]MZ + local show_window_status="" + show_window_status+="#{?window_activity_flag, ${custom_icon_window_activity},}" + show_window_status+="#{?window_bell_flag, ${custom_icon_window_bell},}" + show_window_status+="#{?window_silence_flag, ${custom_icon_window_silent},}" + show_window_status+="#{?window_active, ${custom_icon_window_current},}" + show_window_status+="#{?window_last_flag, ${custom_icon_window_last},}" + show_window_status+="#{?window_marked_flag, ${custom_icon_window_mark},}" + show_window_status+="#{?window_zoomed_flag, ${custom_icon_window_zoom},}" + + fi + + if [ "$window_status_icon_enable" = "no" ]; then + local show_window_status=" #F" + fi + + echo "$show_window_status" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/catppuccin.tmux b/dotfiles/common/.tmux/plugins/tmux/catppuccin.tmux new file mode 100755 index 0000000..5771b8e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/catppuccin.tmux @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +# Set path of script +PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# import +# shellcheck source=./builder/module_builder.sh +source "${PLUGIN_DIR}/builder/module_builder.sh" +# shellcheck source=./builder/window_builder.sh +source "${PLUGIN_DIR}/builder/window_builder.sh" +# shellcheck source=./builder/pane_builder.sh +source "${PLUGIN_DIR}/builder/pane_builder.sh" +# shellcheck source=./utils/tmux_utils.sh +source "${PLUGIN_DIR}/utils/tmux_utils.sh" +# shellcheck source=./utils/interpolate_utils.sh +source "${PLUGIN_DIR}/utils/interpolate_utils.sh" +# shellcheck source=./utils/module_utils.sh +source "${PLUGIN_DIR}/utils/module_utils.sh" + +main() { + # Aggregate all commands in one array + local tmux_commands=() + + # module directories + local custom_path modules_custom_path modules_status_path modules_window_path modules_pane_path + custom_path="$(get_tmux_option "@catppuccin_custom_plugin_dir" "${PLUGIN_DIR}/custom")" + modules_custom_path=$custom_path + modules_status_path=$PLUGIN_DIR/status + modules_window_path=$PLUGIN_DIR/window + modules_pane_path=$PLUGIN_DIR/pane + + # load local theme + local theme + local color_interpolation=() + local color_values=() + local temp + theme="$(get_tmux_option "@catppuccin_flavour" "mocha")" + # NOTE: Pulling in the selected theme by the theme that's being set as local + # variables. + # https://github.com/dylanaraps/pure-sh-bible#parsing-a-keyval-file + # shellcheck source=./catppuccin-frappe.tmuxtheme + while IFS='=' read -r key val; do + # Skip over lines containing comments. + # (Lines starting with '#'). + [ "${key##\#*}" ] || continue + + # '$key' stores the key. + # '$val' stores the value. + eval "local $key"="$val" + + # TODO: Find a better way to strip the quotes from `$val` + temp="${val%\"}" + temp="${temp#\"}" + color_interpolation+=("\#{$key}") + color_values+=("${temp}") + done <"${PLUGIN_DIR}/themes/catppuccin_${theme}.tmuxtheme" + + # status general + local status_default status_justify status_background message_background + status_default=$(get_tmux_option "@catppuccin_status_default" "on") + # shellcheck disable=SC2121 + set status "$status_default" + + status_justify=$(get_tmux_option "@catppuccin_status_justify" "left") + set status-justify "$status_justify" + + status_background=$(get_tmux_option "@catppuccin_status_background" "theme") + if [ "${status_background}" = "theme" ]; then + set status-bg "${thm_bg}" + message_background="${thm_gray}" + else + if [ "${status_background}" = "default" ]; then + set status-style bg=default + message_background="default" + else + message_background="$(do_color_interpolation "$status_background")" + set status-bg "${message_background}" + fi + fi + + set status-left-length "100" + set status-right-length "100" + + # messages + set message-style "fg=${thm_cyan},bg=${message_background},align=centre" + set message-command-style "fg=${thm_cyan},bg=${message_background},align=centre" + + # menu + local menu_style menu_selected_style menu_border_style + menu_style=$(get_interpolated_tmux_option "@catppuccin_menu_style" "default") + menu_selected_style=$(get_interpolated_tmux_option "@catppuccin_menu_selected_style" "fg=${thm_gray},bg=${thm_yellow}") + menu_border_style=$(get_interpolated_tmux_option "@catppuccin_menu_border_style" "default") + set menu-style "$menu_style" + set menu-selected-style "$menu_selected_style" + set menu-border-style "$menu_border_style" + + # panes + local pane_border_status pane_border_style \ + pane_active_border_style pane_left_separator pane_middle_separator \ + pane_right_separator pane_number_position pane_format + pane_status_enable=$(get_tmux_option "@catppuccin_pane_status_enabled" "no") # yes + pane_border_status=$(get_tmux_option "@catppuccin_pane_border_status" "off") # bottom + pane_border_style=$( + get_interpolated_tmux_option "@catppuccin_pane_border_style" "fg=${thm_gray}" + ) + pane_active_border_style=$( + get_interpolated_tmux_option "@catppuccin_pane_active_border_style" \ + "#{?pane_in_mode,fg=${thm_yellow},#{?pane_synchronized,fg=${thm_magenta},fg=${thm_orange}}}" + ) + pane_left_separator=$(get_tmux_option "@catppuccin_pane_left_separator" "█") + pane_middle_separator=$(get_tmux_option "@catppuccin_pane_middle_separator" "█") + pane_right_separator=$(get_tmux_option "@catppuccin_pane_right_separator" "█") + pane_number_position=$(get_tmux_option "@catppuccin_pane_number_position" "left") # right, left + pane_format=$(load_modules "pane_default_format" "$modules_custom_path" "$modules_pane_path") + + setw pane-border-status "$pane_border_status" + setw pane-active-border-style "$pane_active_border_style" + setw pane-border-style "$pane_border_style" + setw pane-border-format "$(do_color_interpolation "$pane_format")" + + # window + local window_status_separator window_left_separator window_right_separator \ + window_middle_separator window_number_position window_status_enable \ + window_format window_current_format + + window_status_separator=$(get_interpolated_tmux_option "@catppuccin_window_separator" "") + setw window-status-separator "$window_status_separator" + + window_left_separator=$(get_tmux_option "@catppuccin_window_left_separator" "█") + window_right_separator=$(get_tmux_option "@catppuccin_window_right_separator" "█") + window_middle_separator=$(get_tmux_option "@catppuccin_window_middle_separator" "█ ") + window_number_position=$(get_tmux_option "@catppuccin_window_number_position" "left") # right, left + window_status_enable=$(get_tmux_option "@catppuccin_window_status_enable" "no") # right, left + + window_format=$(load_modules "window_default_format" "$modules_custom_path" "$modules_window_path") + setw window-status-format "$(do_color_interpolation "$window_format")" + + window_current_format=$(load_modules "window_current_format" "$modules_custom_path" "$modules_window_path") + setw window-status-current-format "$(do_color_interpolation "$window_current_format")" + + # status module + local status_left_separator status_right_separator status_connect_separator \ + status_fill status_modules_left status_modules_right + status_left_separator=$(get_tmux_option "@catppuccin_status_left_separator" "") + status_right_separator=$(get_tmux_option "@catppuccin_status_right_separator" "█") + status_connect_separator=$(get_tmux_option "@catppuccin_status_connect_separator" "yes") + status_fill=$(get_tmux_option "@catppuccin_status_fill" "icon") + + status_modules_left=$(get_tmux_option "@catppuccin_status_modules_left" "") + loaded_modules_left=$(load_modules "$status_modules_left" "$modules_custom_path" "$modules_status_path") + set status-left "$(do_color_interpolation "$loaded_modules_left")" + + status_modules_right=$(get_tmux_option "@catppuccin_status_modules_right" "application session") + loaded_modules_right=$(load_modules "$status_modules_right" "$modules_custom_path" "$modules_status_path") + set status-right "$(do_color_interpolation "$loaded_modules_right")" + + # modes + setw clock-mode-colour "${thm_blue}" + setw mode-style "fg=${thm_pink} bg=${thm_black4} bold" + + tmux "${tmux_commands[@]}" +} + +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tmux/pane/pane_default_format.sh b/dotfiles/common/.tmux/plugins/tmux/pane/pane_default_format.sh new file mode 100644 index 0000000..53ef75b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/pane/pane_default_format.sh @@ -0,0 +1,13 @@ +show_pane_default_format() { + local number color background text fill + + number="#{pane_index}" + color="$(get_tmux_option "@catppuccin_pane_color" "$thm_green")" + background="$(get_tmux_option "@catppuccin_pane_background_color" "$thm_gray")" + text="$(get_tmux_option "@catppuccin_pane_default_text" "#{b:pane_current_path}")" + fill="$(get_tmux_option "@catppuccin_pane_default_fill" "number")" # number, all, none + + default_pane_format=$(build_pane_format "$number" "$color" "$background" "$text" "$fill") + + echo "$default_pane_format" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/renovate.json b/dotfiles/common/.tmux/plugins/tmux/renovate.json new file mode 100644 index 0000000..a222000 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>catppuccin/renovate-config" + ] +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/application.sh b/dotfiles/common/.tmux/plugins/tmux/status/application.sh new file mode 100644 index 0000000..fa2bbbc --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/application.sh @@ -0,0 +1,12 @@ +show_application() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_application_icon" "") + color=$(get_tmux_option "@catppuccin_application_color" "$thm_pink") + text=$(get_tmux_option "@catppuccin_application_text" "#{pane_current_command}") + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/battery.sh b/dotfiles/common/.tmux/plugins/tmux/status/battery.sh new file mode 100644 index 0000000..5a2d8f0 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/battery.sh @@ -0,0 +1,26 @@ +show_battery() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_battery_icon" "#{battery_icon}") + color=$(get_tmux_option "@catppuccin_battery_color" "$thm_yellow") + text=$(get_tmux_option "@catppuccin_battery_text" "#{battery_percentage}") + + tmux set-option -g @batt_icon_charge_tier8 '󰁹' + tmux set-option -g @batt_icon_charge_tier7 '󰂁' + tmux set-option -g @batt_icon_charge_tier6 '󰁿' + tmux set-option -g @batt_icon_charge_tier5 '󰁾' + tmux set-option -g @batt_icon_charge_tier4 '󰁽' + tmux set-option -g @batt_icon_charge_tier3 '󰁼' + tmux set-option -g @batt_icon_charge_tier2 '󰁻' + tmux set-option -g @batt_icon_charge_tier1 '󰁺' + tmux set-option -g @batt_icon_status_charged '󰚥' + tmux set-option -g @batt_icon_status_charging '󰂄' + tmux set-option -g @batt_icon_status_discharging '󰂃' + tmux set-option -g @batt_icon_status_unknown '󰂑' + tmux set-option -g @batt_icon_status_attached "󱈑" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/clima.sh b/dotfiles/common/.tmux/plugins/tmux/status/clima.sh new file mode 100644 index 0000000..7e1801b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/clima.sh @@ -0,0 +1,13 @@ +# Requires https://github.com/vascomfnunes/tmux-clima +show_clima() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_clima_icon" "")" + color="$(get_tmux_option "@catppuccin_clima_color" "$thm_yellow")" + text="$(get_tmux_option "@catppuccin_clima_text" "#{clima}")" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/cpu.sh b/dotfiles/common/.tmux/plugins/tmux/status/cpu.sh new file mode 100644 index 0000000..fd12951 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/cpu.sh @@ -0,0 +1,16 @@ +show_cpu() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_cpu_icon" "") + color="$(get_tmux_option "@catppuccin_cpu_color" "#{cpu_bg_color}")" + text="$(get_tmux_option "@catppuccin_cpu_text" "#{cpu_percentage}")" + + tmux set-option -g @cpu_low_bg_color "$thm_yellow" # background color when cpu is low + tmux set-option -g @cpu_medium_bg_color "$thm_orange" # background color when cpu is medium + tmux set-option -g @cpu_high_bg_color "$thm_red" # background color when cpu is high + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/date_time.sh b/dotfiles/common/.tmux/plugins/tmux/status/date_time.sh new file mode 100644 index 0000000..04695b9 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/date_time.sh @@ -0,0 +1,12 @@ +show_date_time() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_date_time_icon" "󰃰")" + color="$(get_tmux_option "@catppuccin_date_time_color" "$thm_blue")" + text="$(get_tmux_option "@catppuccin_date_time_text" "%Y-%m-%d %H:%M")" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/directory.sh b/dotfiles/common/.tmux/plugins/tmux/status/directory.sh new file mode 100644 index 0000000..8982dbe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/directory.sh @@ -0,0 +1,12 @@ +show_directory() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_directory_icon" "") + color=$(get_tmux_option "@catppuccin_directory_color" "$thm_pink") + text=$(get_tmux_option "@catppuccin_directory_text" "#{b:pane_current_path}") + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/gitmux.sh b/dotfiles/common/.tmux/plugins/tmux/status/gitmux.sh new file mode 100644 index 0000000..9186bc2 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/gitmux.sh @@ -0,0 +1,14 @@ +# Requires https://github.com/arl/gitmux + +show_gitmux() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_gitmux_icon" "󰊢")" + color="$(get_tmux_option "@catppuccin_gitmux_color" "$thm_green")" + text="$(get_tmux_option "@catppuccin_gitmux_text" "#(gitmux \"#{pane_current_path}\")")" + + module=$( build_status_module "$index" "$icon" "$color" "$text" ) + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/host.sh b/dotfiles/common/.tmux/plugins/tmux/status/host.sh new file mode 100644 index 0000000..8e4bd23 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/host.sh @@ -0,0 +1,12 @@ +show_host() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_host_icon" "󰒋") + color=$(get_tmux_option "@catppuccin_host_color" "$thm_magenta") + text=$(get_tmux_option "@catppuccin_host_text" "#H") + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/kube.sh b/dotfiles/common/.tmux/plugins/tmux/status/kube.sh new file mode 100644 index 0000000..4e3083c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/kube.sh @@ -0,0 +1,17 @@ +# Requires https://github.com/jonmosco/kube-tmux + +show_kube() { + local index icon color text context_color namespace_color symbol_enabled module + + index=$1 + icon=$(get_tmux_option "@catppuccin_kube_icon" "󱃾") + color=$(get_tmux_option "@catppuccin_kube_color" "$thm_blue") + context_color=$(get_tmux_option "@catppuccin_kube_context_color" "#{thm_red}") + namespace_color=$(get_tmux_option "@catppuccin_kube_namespace_color" "#{thm_cyan}") + symbol_enabled=${KUBE_TMUX_SYMBOL_ENABLE:-false} + text=$(get_tmux_option "@catppuccin_kube_text" "#(KUBE_TMUX_SYMBOL_ENABLE=$symbol_enabled ${TMUX_PLUGIN_MANAGER_PATH}kube-tmux/kube.tmux 250 '$context_color' '$namespace_color')") + + module=$( build_status_module "$index" "$icon" "$color" "$text" ) + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/load.sh b/dotfiles/common/.tmux/plugins/tmux/status/load.sh new file mode 100644 index 0000000..5809423 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/load.sh @@ -0,0 +1,12 @@ +show_load() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_load_icon" "󰊚")" + color="$(get_tmux_option "@catppuccin_load_color" "$thm_blue")" + text="$(get_tmux_option "@catppuccin_load_text" "#{load_full}")" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/pomodoro_plus.sh b/dotfiles/common/.tmux/plugins/tmux/status/pomodoro_plus.sh new file mode 100644 index 0000000..a155cd7 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/pomodoro_plus.sh @@ -0,0 +1,14 @@ +# Requires https://github.com/olimorris/tmux-pomodoro-plus + +show_pomodoro_plus() { + local index icon color text module + + index=$1 + icon="$( get_tmux_option "@catppuccin_pomodoro_plus_icon" "" )" + color="$( get_tmux_option "@catppuccin_pomodoro_plus_color" "$thm_orange" )" + text="$( get_tmux_option "@catppuccin_pomodoro_plus_text" "#{pomodoro_status}" )" + + module=$( build_status_module "$index" "$icon" "$color" "$text" ) + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/session.sh b/dotfiles/common/.tmux/plugins/tmux/status/session.sh new file mode 100644 index 0000000..6e79cfb --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/session.sh @@ -0,0 +1,12 @@ +show_session() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_session_icon" "") + color=$(get_tmux_option "@catppuccin_session_color" "#{?client_prefix,$thm_red,$thm_green}") + text=$(get_tmux_option "@catppuccin_session_text" "#S") + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/uptime.sh b/dotfiles/common/.tmux/plugins/tmux/status/uptime.sh new file mode 100644 index 0000000..10999de --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/uptime.sh @@ -0,0 +1,12 @@ +show_uptime() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_uptime_icon" "󰔟")" + color="$(get_tmux_option "@catppuccin_uptime_color" "$thm_green")" + text="$(get_tmux_option "@catppuccin_uptime_text" "#(uptime | sed 's/^[^,]*up *//; s/, *[[:digit:]]* user.*//; s/ day.*, */d /; s/:/h /; s/ min//; s/$/m/')")" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/user.sh b/dotfiles/common/.tmux/plugins/tmux/status/user.sh new file mode 100644 index 0000000..ea88e75 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/user.sh @@ -0,0 +1,12 @@ +show_user() { + local index icon color text module + + index=$1 + icon=$(get_tmux_option "@catppuccin_user_icon" "") + color=$(get_tmux_option "@catppuccin_user_color" "$thm_cyan") + text=$(get_tmux_option "@catppuccin_user_text" "#(whoami)") + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/status/weather.sh b/dotfiles/common/.tmux/plugins/tmux/status/weather.sh new file mode 100644 index 0000000..b6cb6fe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/status/weather.sh @@ -0,0 +1,14 @@ +# Requires https://github.com/xamut/tmux-weather. + +show_weather() { + local index icon color text module + + index=$1 + icon="$(get_tmux_option "@catppuccin_weather_icon" "")" + color="$(get_tmux_option "@catppuccin_weather_color" "$thm_yellow")" + text="$(get_tmux_option "@catppuccin_weather_text" "#{weather}")" + + module=$(build_status_module "$index" "$icon" "$color" "$text") + + echo "$module" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_frappe.tmuxtheme b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_frappe.tmuxtheme new file mode 100644 index 0000000..a38a73f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_frappe.tmuxtheme @@ -0,0 +1,17 @@ +# NOTE: you can use vars with $ and ${} as long as the str is double quoted: "" +# WARNING: hex colors can't contain capital letters + +# --> Catppuccin (Frappe) +thm_bg="#303446" +thm_fg="#c6d0f5" +thm_cyan="#99d1db" +thm_black="#292c3c" +thm_gray="#414559" +thm_magenta="#ca9ee6" +thm_pink="#f4b8e4" +thm_red="#e78284" +thm_green="#a6d189" +thm_yellow="#e5c890" +thm_blue="#8caaee" +thm_orange="#ef9f76" +thm_black4="#626880" diff --git a/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_latte.tmuxtheme b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_latte.tmuxtheme new file mode 100644 index 0000000..af31a8d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_latte.tmuxtheme @@ -0,0 +1,17 @@ +# NOTE: you can use vars with $ and ${} as long as the str is double quoted: "" +# WARNING: hex colors can't contain capital letters + +# --> Catppuccin (Latte) +thm_bg="#eff1f5" +thm_fg="#4c4f69" +thm_cyan="#04a5e5" +thm_black="#e6e9ef" +thm_gray="#ccd0da" +thm_magenta="#8839ef" +thm_pink="#ea76cb" +thm_red="#d20f39" +thm_green="#40a02b" +thm_yellow="#df8e1d" +thm_blue="#1e66f5" +thm_orange="#fe640b" +thm_black4="#acb0be" diff --git a/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_macchiato.tmuxtheme b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_macchiato.tmuxtheme new file mode 100644 index 0000000..c90bf76 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_macchiato.tmuxtheme @@ -0,0 +1,17 @@ +# NOTE: you can use vars with $ and ${} as long as the str is double quoted: "" +# WARNING: hex colors can't contain capital letters + +# --> Catppuccin (Macchiato) +thm_bg="#24273a" +thm_fg="#cad3f5" +thm_cyan="#91d7e3" +thm_black="#1e2030" +thm_gray="#363a4f" +thm_magenta="#c6a0f6" +thm_pink="#f5bde6" +thm_red="#ed8796" +thm_green="#a6da95" +thm_yellow="#eed49f" +thm_blue="#8aadf4" +thm_orange="#f5a97f" +thm_black4="#5b6078" diff --git a/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_mocha.tmuxtheme b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_mocha.tmuxtheme new file mode 100644 index 0000000..41e6369 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/themes/catppuccin_mocha.tmuxtheme @@ -0,0 +1,17 @@ +# NOTE: you can use vars with $ and ${} as long as the str is double quoted: "" +# WARNING: hex colors can't contain capital letters + +# --> Catppuccin (Mocha) +thm_bg="#1e1e2e" +thm_fg="#cdd6f4" +thm_cyan="#89dceb" +thm_black="#181825" +thm_gray="#313244" +thm_magenta="#cba6f7" +thm_pink="#f5c2e7" +thm_red="#f38ba8" +thm_green="#a6e3a1" +thm_yellow="#f9e2af" +thm_blue="#89b4fa" +thm_orange="#fab387" +thm_black4="#585b70" diff --git a/dotfiles/common/.tmux/plugins/tmux/tmux.tera b/dotfiles/common/.tmux/plugins/tmux/tmux.tera new file mode 100644 index 0000000..7435b79 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/tmux.tera @@ -0,0 +1,25 @@ +--- +whiskers: + version: "2.1.1" + matrix: + - flavor + filename: "themes/catppuccin_{{flavor.identifier}}.tmuxtheme" +--- +{%- set palette = flavor.colors -%} +# NOTE: you can use vars with $ and ${} as long as the str is double quoted: "" +# WARNING: hex colors can't contain capital letters + +# --> Catppuccin ({{ flavor.identifier | capitalize }}) +thm_bg="#{{ palette.base.hex | lower }}" +thm_fg="#{{ palette.text.hex | lower }}" +thm_cyan="#{{ palette.sky.hex| lower }}" +thm_black="#{{ palette.mantle.hex | lower }}" +thm_gray="#{{ palette.surface0.hex | lower }}" +thm_magenta="#{{ palette.mauve.hex | lower }}" +thm_pink="#{{ palette.pink.hex | lower }}" +thm_red="#{{ palette.red.hex | lower }}" +thm_green="#{{ palette.green.hex | lower }}" +thm_yellow="#{{ palette.yellow.hex | lower }}" +thm_blue="#{{ palette.blue.hex | lower }}" +thm_orange="#{{ palette.peach.hex | lower }}" +thm_black4="#{{ palette.surface2.hex | lower }}" diff --git a/dotfiles/common/.tmux/plugins/tmux/utils/interpolate_utils.sh b/dotfiles/common/.tmux/plugins/tmux/utils/interpolate_utils.sh new file mode 100644 index 0000000..54d2489 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/utils/interpolate_utils.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +do_color_interpolation() { + local all_interpolated="$1" + + for ((i=0; i<${#color_interpolation[@]}; i++)); do + all_interpolated=${all_interpolated//${color_interpolation[$i]}/${color_values[$i]}} + done + + echo "$all_interpolated" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/utils/module_utils.sh b/dotfiles/common/.tmux/plugins/tmux/utils/module_utils.sh new file mode 100644 index 0000000..8dd2a7e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/utils/module_utils.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +load_modules() { + local modules_list=$1 + shift + local module_directories=("$@") + + local -i module_index=0; + local module_name + local module_path + local loaded_modules + local IN=$modules_list + + # https://stackoverflow.com/questions/918886/how-do-i-split-a-string-on-a-delimiter-in-bash#15988793 + while [ "$IN" != "$iter" ] ;do + # extract the substring from start of string up to delimiter. + iter=${IN%% *} + # delete this first "element" AND next separator, from $IN. + IN="${IN#"$iter "}" + # Print (or doing anything with) the first "element". + + module_name=$iter + + for module_dir in "${module_directories[@]}" ; do + module_path="$module_dir/$module_name.sh" + + if [ -r "$module_path" ]; then + # shellcheck source=/dev/null + source "$module_path" + loaded_modules="$loaded_modules$( "show_$module_name" "$module_index" )" + module_index+=1 + continue 2 + fi + done + + if [[ -z "${module_name/ }" ]]; then + if [[ -z "${modules_list/ }" ]]; then + tmux_echo "catppuccin warning: a module list has only white space, to remove all modules set it to \"null\"" 100 + else + tmux_echo "catppuccin warning: a module list with value \"$modules_list\" has leading/trailing whitespace" 101 + fi + continue + fi + + tmux_echo "catppuccin warning: module $module_name not found" 102 + + + done + + echo "$loaded_modules" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/utils/tmux_utils.sh b/dotfiles/common/.tmux/plugins/tmux/utils/tmux_utils.sh new file mode 100644 index 0000000..e43f61f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/utils/tmux_utils.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +tmux_echo() { + local hook + hook="after-new-session[$2]" + + tmux set-hook -g "$hook" "run-shell 'echo \"$1\"'; set-hook -gu \"$hook\"" +} + +get_tmux_option() { + local option value default + option="$1" + default="$2" + value=$(tmux show-option -gqv "$option") + + if [ -n "$value" ] + then + if [ "$value" = "null" ] + then + echo "" + + else + echo "$value" + fi + + else + echo "$default" + + fi +} + +get_interpolated_tmux_option() { + local option value default + option="$1" + default="$2" + value=$(tmux show-option -gqv "$option") + + if [ -n "$value" ] + then + if [ "$value" = "null" ] + then + echo "" + + else + do_color_interpolation "$value" + fi + + else + echo "$default" + fi +} + +set() { + local option=$1 + local value=$2 + tmux_commands+=(set-option -gq "$option" "$value" ";") +} + +setw() { + local option=$1 + local value=$2 + tmux_commands+=(set-window-option -gq "$option" "$value" ";") +} diff --git a/dotfiles/common/.tmux/plugins/tmux/window/window_current_format.sh b/dotfiles/common/.tmux/plugins/tmux/window/window_current_format.sh new file mode 100644 index 0000000..0a17bcf --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/window/window_current_format.sh @@ -0,0 +1,13 @@ +show_window_current_format() { + local number color background text fill current_window_format + + number="#I" + color=$(get_tmux_option "@catppuccin_window_current_color" "$thm_orange") + background=$(get_tmux_option "@catppuccin_window_current_background" "$thm_bg") + text="$(get_tmux_option "@catppuccin_window_current_text" "#{b:pane_current_path}")" # use #W for application instead of directory + fill="$(get_tmux_option "@catppuccin_window_current_fill" "number")" # number, all, none + + current_window_format=$(build_window_format "$number" "$color" "$background" "$text" "$fill") + + echo "$current_window_format" +} diff --git a/dotfiles/common/.tmux/plugins/tmux/window/window_default_format.sh b/dotfiles/common/.tmux/plugins/tmux/window/window_default_format.sh new file mode 100644 index 0000000..2c758ba --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tmux/window/window_default_format.sh @@ -0,0 +1,13 @@ +show_window_default_format() { + local number color background text fill default_window_format + + number="#I" + color=$(get_tmux_option "@catppuccin_window_default_color" "$thm_blue") + background=$(get_tmux_option "@catppuccin_window_default_background" "$thm_gray") + text="$(get_tmux_option "@catppuccin_window_default_text" "#{b:pane_current_path}")" # use #W for application instead of directory + fill="$(get_tmux_option "@catppuccin_window_default_fill" "number")" # number, all, none + + default_window_format=$(build_window_format "$number" "$color" "$background" "$text" "$fill") + + echo "$default_window_format" +} diff --git a/dotfiles/common/.tmux/plugins/tpm/.gitattributes b/dotfiles/common/.tmux/plugins/tpm/.gitattributes new file mode 100644 index 0000000..80772e4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/.gitattributes @@ -0,0 +1,9 @@ +# Force text files to have unix eols, so Windows/Cygwin does not break them +*.* eol=lf + +# These files are unfortunately not recognized as text files so +# explicitly listing them here +tpm eol=lf +bin/* eol=lf +bindings/* eol=lf +tests/* eol=lf diff --git a/dotfiles/common/.tmux/plugins/tpm/.gitignore b/dotfiles/common/.tmux/plugins/tpm/.gitignore new file mode 100644 index 0000000..8a94156 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/.gitignore @@ -0,0 +1,4 @@ +**/.vagrant/ +run_tests +tests/run_tests_in_isolation +tests/helpers/helpers.sh diff --git a/dotfiles/common/.tmux/plugins/tpm/.gitmodules b/dotfiles/common/.tmux/plugins/tpm/.gitmodules new file mode 100644 index 0000000..5e44e3c --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/tmux-test"] + path = lib/tmux-test + url = https://github.com/tmux-plugins/tmux-test.git diff --git a/dotfiles/common/.tmux/plugins/tpm/.travis.yml b/dotfiles/common/.tmux/plugins/tpm/.travis.yml new file mode 100644 index 0000000..ac45d8b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/.travis.yml @@ -0,0 +1,19 @@ +# generic packages and tmux +before_install: + - sudo apt-get update + - sudo apt-get install -y git-core expect + - sudo apt-get install -y python-software-properties software-properties-common + - sudo apt-get install -y libevent-dev libncurses-dev + - git clone https://github.com/tmux/tmux.git + - cd tmux + - git checkout 2.0 + - sh autogen.sh + - ./configure && make && sudo make install + +install: + - git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules + # manual `git clone` required for testing `tmux-test` plugin itself + - git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test; true + - lib/tmux-test/setup + +script: ./tests/run_tests_in_isolation diff --git a/dotfiles/common/.tmux/plugins/tpm/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tpm/CHANGELOG.md new file mode 100644 index 0000000..a9ce81e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/CHANGELOG.md @@ -0,0 +1,86 @@ +# Changelog + +### master + +### v3.1.0, 2023-01-03 +- upgrade to new version of `tmux-test` +- bug: when using `emacs` copy mode, Enter does not quit screen after tpm + installation/update. Fix by making `Escape` the key for emacs mode. +- add a doc with troubleshooting instructions +- add `.gitattributes` file that forces linefeed characters (classic `\n`) as + line endings - helps with misconfigured git on windows/cygwin +- readme update: announce Cygwin support +- un-deprecate old plugin definition syntax: `set -g @tpm_plugins` +- More stuff, check `git log`. + +### v3.0.0, 2015-08-03 +- refactor `shared_set_tpm_path_constant` function +- move all instructions to `docs/` dir +- add `bin/install_plugins` cli executable script +- improved test runner function +- switch to using [tmux-test](https://github.com/tmux-plugins/tmux-test) + framework +- add `bin/update_plugins` cli executable script +- refactor test `expect` scripts, make them simpler and ensure they properly + assert expectations +- refactor code that sets 'TMUX_PLUGIN_MANAGER_PATH' global env var +- stop using global variable for 'tpm path' +- support defining plugins via `set -g @plugin` in sourced files as well + +### v2.0.0, 2015-07-07 +- enable overriding default key bindings +- start using `C-c` to clear screen +- add uninstall/clean procedure and keybinding (prefix+alt+u) (@chilicuil) +- add new `set @plugin 'repo'` plugin definition syntax (@chilicuil) +- revert back to using `-g` flag in new plugin definition syntax +- permit leading whitespace with new plugin definition syntax (thanks @chilicuil) +- make sure `TMUX_PLUGIN_MANAGER_PATH` always has trailng slash +- ensure old/deprecated plugin syntax `set -g @tpm_plugins` works alongside new + `set -g @plugin` syntax + +### v1.2.2, 2015-02-08 +- set GIT_TERMINAL_PROMPT=0 when doing `git clone`, `pull` or `submodule update` + to ensure git does not prompt for username/password in any case + +### v1.2.1, 2014-11-21 +- change the way plugin name is expanded. It now uses the http username + and password by default, like this: `https://git::@github.com/`. This prevents + username and password prompt (and subsequently tmux install hanging) with old + git versions. Fixes #7. + +### v1.2.0, 2014-11-20 +- refactor tests so they can be used on travis +- add travis.yml, add travis badge to the readme + +### v1.1.0, 2014-11-19 +- if the plugin is not downloaded do not source it +- remove `PLUGINS.md`, an obsolete list of plugins +- update readme with instructions about uninstalling plugins +- tilde char and `$HOME` in `TMUX_SHARED_MANAGER_PATH` couldn't be used because + they are just plain strings. Fixing the problem by manually expanding them. +- bugfix: fragile `*.tmux` file globbing (@majutsushi) + +### v1.0.0, 2014-08-05 +- update readme because of github organization change to + [tmux-plugins](https://github.com/tmux-plugins) +- update tests to pass +- update README to suggest different first plugin +- update list of plugins in the README +- remove README 'about' section +- move key binding to the main file. Delete `key_binding.sh`. +- rename `display_message` -> `echo_message` +- installing plugins installs just new plugins. Already installed plugins aren't + updated. +- add 'update plugin' binding and functionality +- add test for updating a plugin + +### v0.0.2, 2014-07-17 +- run all *.tmux plugin files as executables +- fix all redirects to /dev/null +- fix bug: TPM shared path is created before sync (cloning plugins from github + is done) +- add test suite running in Vagrant +- add Tmux version check. `TPM` won't run if Tmux version is less than 1.9. + +### v0.0.1, 2014-05-21 +- get TPM up and running diff --git a/dotfiles/common/.tmux/plugins/tpm/HOW_TO_PLUGIN.md b/dotfiles/common/.tmux/plugins/tpm/HOW_TO_PLUGIN.md new file mode 100644 index 0000000..9901619 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/HOW_TO_PLUGIN.md @@ -0,0 +1,2 @@ +Instructions moved to +[docs/how_to_create_plugin.md](docs/how_to_create_plugin.md). diff --git a/dotfiles/common/.tmux/plugins/tpm/LICENSE.md b/dotfiles/common/.tmux/plugins/tpm/LICENSE.md new file mode 100644 index 0000000..1222865 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/LICENSE.md @@ -0,0 +1,20 @@ +MIT license +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. diff --git a/dotfiles/common/.tmux/plugins/tpm/README.md b/dotfiles/common/.tmux/plugins/tpm/README.md new file mode 100644 index 0000000..2371863 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/README.md @@ -0,0 +1,101 @@ +# Tmux Plugin Manager + +[![Build Status](https://travis-ci.org/tmux-plugins/tpm.svg?branch=master)](https://travis-ci.org/tmux-plugins/tpm) + +Installs and loads `tmux` plugins. + +Tested and working on Linux, OSX, and Cygwin. + +See list of plugins [here](https://github.com/tmux-plugins/list). + +### Installation + +Requirements: `tmux` version 1.9 (or higher), `git`, `bash`. + +Clone TPM: + +```bash +git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm +``` + +Put this at the bottom of `~/.tmux.conf` (`$XDG_CONFIG_HOME/tmux/tmux.conf` +works too): + +```bash +# List of plugins +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-sensible' + +# Other examples: +# set -g @plugin 'github_username/plugin_name' +# set -g @plugin 'github_username/plugin_name#branch' +# set -g @plugin 'git@github.com:user/plugin' +# set -g @plugin 'git@bitbucket.com:user/plugin' + +# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) +run '~/.tmux/plugins/tpm/tpm' +``` + +Reload TMUX environment so TPM is sourced: + +```bash +# type this in terminal if tmux is already running +tmux source ~/.tmux.conf +``` + +That's it! + +### Installing plugins + +1. Add new plugin to `~/.tmux.conf` with `set -g @plugin '...'` +2. Press `prefix` + I (capital i, as in **I**nstall) to fetch the plugin. + +You're good to go! The plugin was cloned to `~/.tmux/plugins/` dir and sourced. + +### Uninstalling plugins + +1. Remove (or comment out) plugin from the list. +2. Press `prefix` + alt + u (lowercase u as in **u**ninstall) to remove the plugin. + +All the plugins are installed to `~/.tmux/plugins/` so alternatively you can +find plugin directory there and remove it. + +### Key bindings + +`prefix` + I +- Installs new plugins from GitHub or any other git repository +- Refreshes TMUX environment + +`prefix` + U +- updates plugin(s) + +`prefix` + alt + u +- remove/uninstall plugins not on the plugin list + +### Docs + +- [Help, tpm not working](docs/tpm_not_working.md) - problem solutions + +More advanced features and instructions, regular users probably do not need +this: + +- [How to create a plugin](docs/how_to_create_plugin.md). It's easy. +- [Managing plugins via the command line](docs/managing_plugins_via_cmd_line.md) +- [Changing plugins install dir](docs/changing_plugins_install_dir.md) +- [Automatic TPM installation on a new machine](docs/automatic_tpm_installation.md) + +### Tests + +Tests for this project run on [Travis CI](https://travis-ci.org/tmux-plugins/tpm). + +When run locally, [vagrant](https://www.vagrantup.com/) is required. +Run tests with: + +```bash +# within project directory +./run_tests +``` + +### License + +[MIT](LICENSE.md) diff --git a/dotfiles/common/.tmux/plugins/tpm/bin/clean_plugins b/dotfiles/common/.tmux/plugins/tpm/bin/clean_plugins new file mode 100755 index 0000000..12f8730 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bin/clean_plugins @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Script intended for use via the command line. +# +# `.tmux.conf` needs to be set for TPM. Tmux has to be installed on the system, +# but does not need to be started in order to run this script. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" + +main() { + "$SCRIPTS_DIR/clean_plugins.sh" # has correct exit code +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/bin/install_plugins b/dotfiles/common/.tmux/plugins/tpm/bin/install_plugins new file mode 100755 index 0000000..c66b15b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bin/install_plugins @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Script intended for use via the command line. +# +# `.tmux.conf` needs to be set for TPM. Tmux has to be installed on the system, +# but does not need to be started in order to run this script. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" + +main() { + "$SCRIPTS_DIR/install_plugins.sh" # has correct exit code +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/bin/update_plugins b/dotfiles/common/.tmux/plugins/tpm/bin/update_plugins new file mode 100755 index 0000000..30a5646 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bin/update_plugins @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Script intended for use via the command line. +# +# `.tmux.conf` needs to be set for TPM. Tmux has to be installed on the system, +# but does not need to be started in order to run this script. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" +PROGRAM_NAME="$0" + +if [ $# -eq 0 ]; then + echo "usage:" + echo " $PROGRAM_NAME all update all plugins" + echo " $PROGRAM_NAME tmux-foo update plugin 'tmux-foo'" + echo " $PROGRAM_NAME tmux-bar tmux-baz update multiple plugins" + exit 1 +fi + +main() { + "$SCRIPTS_DIR/update_plugin.sh" --shell-echo "$*" # has correct exit code +} +main "$*" + diff --git a/dotfiles/common/.tmux/plugins/tpm/bindings/clean_plugins b/dotfiles/common/.tmux/plugins/tpm/bindings/clean_plugins new file mode 100755 index 0000000..9a0d5d7 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bindings/clean_plugins @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Tmux key-binding script. +# Scripts intended to be used via the command line are in `bin/` directory. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" +HELPERS_DIR="$SCRIPTS_DIR/helpers" + +source "$HELPERS_DIR/tmux_echo_functions.sh" +source "$HELPERS_DIR/tmux_utils.sh" + +main() { + reload_tmux_environment + "$SCRIPTS_DIR/clean_plugins.sh" --tmux-echo >/dev/null 2>&1 + reload_tmux_environment + end_message +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/bindings/install_plugins b/dotfiles/common/.tmux/plugins/tpm/bindings/install_plugins new file mode 100755 index 0000000..3ade3c4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bindings/install_plugins @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Tmux key-binding script. +# Scripts intended to be used via the command line are in `bin/` directory. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" +HELPERS_DIR="$SCRIPTS_DIR/helpers" + +source "$HELPERS_DIR/tmux_echo_functions.sh" +source "$HELPERS_DIR/tmux_utils.sh" + +main() { + reload_tmux_environment + "$SCRIPTS_DIR/install_plugins.sh" --tmux-echo >/dev/null 2>&1 + reload_tmux_environment + end_message +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/bindings/update_plugins b/dotfiles/common/.tmux/plugins/tpm/bindings/update_plugins new file mode 100755 index 0000000..28cc281 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/bindings/update_plugins @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# Tmux key-binding script. +# Scripts intended to be used via the command line are in `bin/` directory. + +# This script: +# - shows a list of installed plugins +# - starts a prompt to enter the name of the plugin that will be updated + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTS_DIR="$CURRENT_DIR/../scripts" +HELPERS_DIR="$SCRIPTS_DIR/helpers" + +source "$HELPERS_DIR/plugin_functions.sh" +source "$HELPERS_DIR/tmux_echo_functions.sh" +source "$HELPERS_DIR/tmux_utils.sh" + +display_plugin_update_list() { + local plugins="$(tpm_plugins_list_helper)" + tmux_echo "Installed plugins:" + tmux_echo "" + + for plugin in $plugins; do + # displaying only installed plugins + if plugin_already_installed "$plugin"; then + local plugin_name="$(plugin_name_helper "$plugin")" + tmux_echo " $plugin_name" + fi + done + + tmux_echo "" + tmux_echo "Type plugin name to update it." + tmux_echo "" + tmux_echo "- \"all\" - updates all plugins" + tmux_echo "- ENTER - cancels" +} + +update_plugin_prompt() { + tmux command-prompt -p 'plugin update:' " \ + send-keys C-c; \ + run-shell '$SCRIPTS_DIR/update_plugin_prompt_handler.sh %1'" +} + +main() { + reload_tmux_environment + display_plugin_update_list + update_plugin_prompt +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/docs/automatic_tpm_installation.md b/dotfiles/common/.tmux/plugins/tpm/docs/automatic_tpm_installation.md new file mode 100644 index 0000000..630573f --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/docs/automatic_tpm_installation.md @@ -0,0 +1,12 @@ +# Automatic tpm installation + +One of the first things we do on a new machine is cloning our dotfiles. Not everything comes with them though, so for example `tpm` most likely won't be installed. + +If you want to install `tpm` and plugins automatically when tmux is started, put the following snippet in `.tmux.conf` before the final `run '~/.tmux/plugins/tpm/tpm'`: + +``` +if "test ! -d ~/.tmux/plugins/tpm" \ + "run 'git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm && ~/.tmux/plugins/tpm/bin/install_plugins'" +``` + +This useful tip was submitted by @acr4 and narfman0. diff --git a/dotfiles/common/.tmux/plugins/tpm/docs/changing_plugins_install_dir.md b/dotfiles/common/.tmux/plugins/tpm/docs/changing_plugins_install_dir.md new file mode 100644 index 0000000..27de96d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/docs/changing_plugins_install_dir.md @@ -0,0 +1,16 @@ +# Changing plugins install dir + +By default, TPM installs plugins in a subfolder named `plugins/` inside +`$XDG_CONFIG_HOME/tmux/` if a `tmux.conf` file was found at that location, or +inside `~/.tmux/` otherwise. + +You can change the install path by putting this in `.tmux.conf`: + + set-environment -g TMUX_PLUGIN_MANAGER_PATH '/some/other/path/' + +Tmux plugin manager initialization in `.tmux.conf` should also be updated: + + # initializes TMUX plugin manager in a new path + run /some/other/path/tpm/tpm + +Please make sure that the `run` line is at the very bottom of `.tmux.conf`. diff --git a/dotfiles/common/.tmux/plugins/tpm/docs/how_to_create_plugin.md b/dotfiles/common/.tmux/plugins/tpm/docs/how_to_create_plugin.md new file mode 100644 index 0000000..f7d9c13 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/docs/how_to_create_plugin.md @@ -0,0 +1,108 @@ +# How to create Tmux plugins + +Creating a new plugin is easy. + +For demonstration purposes we'll create a simple plugin that lists all +installed TPM plugins. Yes, a plugin that lists plugins :) We'll bind that to +`prefix + T`. + +The source code for this example plugin can be found +[here](https://github.com/tmux-plugins/tmux-example-plugin). + +### 1. create a new git project + +TPM depends on git for downloading and updating plugins. + +To create a new git project: + + $ mkdir tmux_my_plugin + $ cd tmux_my_plugin + $ git init + +### 2. create a `*.tmux` plugin run file + +When it sources a plugin, TPM executes all `*.tmux` files in your plugins' +directory. That's how plugins are run. + +Create a plugin run file in plugin directory: + + $ touch my_plugin.tmux + $ chmod u+x my_plugin.tmux + +You can have more than one `*.tmux` file, and all will get executed. However, usually +you'll need just one. + +### 3. create a plugin key binding + +We want the behavior of the plugin to trigger when a user hits `prefix + T`. + +Key `T` is chosen because: + - it's "kind of" a mnemonic for `TPM` + - the key is not used by Tmux natively. Tmux man page, KEY BINDINGS section + contains a list of all the bindings Tmux uses. There's plenty of unused keys + and we don't want to override any of Tmux default key bindings. + +Open the plugin run file in your favorite text editor: + + $ vim my_plugin.tmux + # or + $ subl my_plugin.tmux + +Put the following content in the file: + + #!/usr/bin/env bash + + CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + tmux bind-key T run-shell "$CURRENT_DIR/scripts/tmux_list_plugins.sh" + +As you can see, plugin run file is a simple bash script that sets up the binding. + +When pressed, `prefix + T` will execute another shell script: +`tmux_list_plugins.sh`. That script should be in `scripts/` directory - +relative to the plugin run file. + + +### 4. listing plugins + +Now that we have the binding, let's create a script that's invoked with +`prefix + T`. + + $ mkdir scripts + $ touch scripts/tmux_list_plugins.sh + $ chmod u+x scripts/tmux_list_plugins.sh + +And here's the script content: + + #!/usr/bin/env bash + + # fetching the directory where plugins are installed + plugin_path="$(tmux show-env -g TMUX_PLUGIN_MANAGER_PATH | cut -f2 -d=)" + + # listing installed plugins + ls -1 "$plugin_path" + +### 5. try it out + +To see if this works, execute the plugin run file: + + $ ./my_plugin.tmux + +That should set up the key binding. Now hit `prefix + T` and see if it works. + +### 6. publish the plugin + +When everything is ready, push the plugin to an online git repository, +preferably GitHub. + +Other users can install your plugin by just adding plugin git URL to the +`@plugin` list in their `.tmux.conf`. + +If the plugin is on GitHub, your users will be able to use the shorthand of +`github_username/repository`. + +### Conclusion + +Hopefully, that was easy. As you can see, it's mostly shell scripting. + +You can use other scripting languages (ruby, python etc) but plain old shell +is preferred because of portability. diff --git a/dotfiles/common/.tmux/plugins/tpm/docs/managing_plugins_via_cmd_line.md b/dotfiles/common/.tmux/plugins/tpm/docs/managing_plugins_via_cmd_line.md new file mode 100644 index 0000000..7aefd7d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/docs/managing_plugins_via_cmd_line.md @@ -0,0 +1,36 @@ +# Managing plugins via the command line + +Aside from tmux key bindings, TPM provides shell interface for managing plugins +via scripts located in [bin/](../bin/) directory. + +Tmux does not need to be started in order to run scripts (but it's okay if it +is). If you [changed tpm install dir](../docs/changing_plugins_install_dir.md) +in `.tmux.conf` that should work fine too. + +Prerequisites: + +- tmux installed on the system (doh) +- `.tmux.conf` set up for TPM + +### Installing plugins + +As usual, plugins need to be specified in `.tmux.conf`. Run the following +command to install plugins: + + ~/.tmux/plugins/tpm/bin/install_plugins + +### Updating plugins + +To update all installed plugins: + + ~/.tmux/plugins/tpm/bin/update_plugins all + +or update a single plugin: + + ~/.tmux/plugins/tpm/bin/update_plugins tmux-sensible + +### Removing plugins + +To remove plugins not on the plugin list: + + ~/.tmux/plugins/tpm/bin/clean_plugins diff --git a/dotfiles/common/.tmux/plugins/tpm/docs/tpm_not_working.md b/dotfiles/common/.tmux/plugins/tpm/docs/tpm_not_working.md new file mode 100644 index 0000000..6680291 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/docs/tpm_not_working.md @@ -0,0 +1,102 @@ +# Help, tpm not working! + +Here's the list of issues users had with `tpm`: + +
+ +> Nothing works. `tpm` key bindings `prefix + I`, `prefix + U` not even + defined. + +Related [issue #22](https://github.com/tmux-plugins/tpm/issues/22) + +- Do you have required `tmux` version to run `tpm`?
+ Check `tmux` version with `$ tmux -V` command and make sure it's higher or + equal to the required version for `tpm` as stated in the readme. + +- ZSH tmux plugin might be causing issues.
+ If you have it installed, try disabling it and see if `tpm` works then. + +
+ +> Help, I'm using custom config file with `tmux -f /path/to/my_tmux.conf` +to start Tmux and for some reason plugins aren't loaded!? + +Related [issue #57](https://github.com/tmux-plugins/tpm/issues/57) + +`tpm` has a known issue when using custom config file with `-f` option. +The solution is to use alternative plugin definition syntax. Here are the steps +to make it work: + +1. remove all `set -g @plugin` lines from tmux config file +2. in the config file define the plugins in the following way: + + # List of plugins + set -g @tpm_plugins ' \ + tmux-plugins/tpm \ + tmux-plugins/tmux-sensible \ + tmux-plugins/tmux-resurrect \ + ' + + # Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) + run '~/.tmux/plugins/tpm/tpm' + +3. Reload TMUX environment so TPM is sourced: `$ tmux source /path/to/my_tmux.conf` + +The plugins should now be working. + +
+ +> Weird sequence of characters show up when installing or updating plugins + +Related: [issue #25](https://github.com/tmux-plugins/tpm/issues/25) + +- This could be caused by [tmuxline.vim](https://github.com/edkolev/tmuxline.vim) + plugin. Uninstall it and see if things work. + +
+ +> "failed to connect to server" error when sourcing .tmux.conf + +Related: [issue #48](https://github.com/tmux-plugins/tpm/issues/48) + +- Make sure `tmux source ~/.tmux.conf` command is ran from inside `tmux`. + +
+ +> tpm not working: '~/.tmux/plugins/tpm/tpm' returned 2 (Windows / Cygwin) + +Related: [issue #81](https://github.com/tmux-plugins/tpm/issues/81) + +This issue is most likely caused by Windows line endings. For example, if you +have git's `core.autocrlf` option set to `true`, git will automatically convert +all the files to Windows line endings which might cause a problem. + +The solution is to convert all line ending to Unix newline characters. This +command handles that for all files under `.tmux/` dir (skips `.git` +subdirectories): + +```bash +find ~/.tmux -type d -name '.git*' -prune -o -type f -print0 | xargs -0 dos2unix +``` + +
+ +> '~/.tmux/plugins/tpm/tpm' returned 127 (on macOS, w/ tmux installed using brew) + +Related: [issue #67](https://github.com/tmux-plugins/tpm/issues/67) + +This problem is because tmux's `run-shell` command runs a shell which doesn't read from user configs, thus tmux installed in a brew prefix (e.g. `/usr/local/bin`) will not be found. + +The solution is to find your brew prefix + +```sh +> echo "$(brew --prefix)/bin" +/opt/homebrew/bin +``` + +And prepend it to the `PATH` environment variable +``` +set-environment -g PATH "/opt/homebrew/bin:/bin:/usr/bin" +``` + +before any `run-shell`/`run` commands in `~/.tmux.conf`. diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.gitignore b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.gitignore new file mode 100644 index 0000000..27281b5 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.gitignore @@ -0,0 +1,2 @@ +.vagrant/ +lib/ diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.travis.yml b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.travis.yml new file mode 100644 index 0000000..ac45d8b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/.travis.yml @@ -0,0 +1,19 @@ +# generic packages and tmux +before_install: + - sudo apt-get update + - sudo apt-get install -y git-core expect + - sudo apt-get install -y python-software-properties software-properties-common + - sudo apt-get install -y libevent-dev libncurses-dev + - git clone https://github.com/tmux/tmux.git + - cd tmux + - git checkout 2.0 + - sh autogen.sh + - ./configure && make && sudo make install + +install: + - git fetch --unshallow --recurse-submodules || git fetch --recurse-submodules + # manual `git clone` required for testing `tmux-test` plugin itself + - git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test; true + - lib/tmux-test/setup + +script: ./tests/run_tests_in_isolation diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/CHANGELOG.md b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/CHANGELOG.md new file mode 100644 index 0000000..e6b9c79 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog + +### master +- move `setup` task to `.travis.yml` for travis tests +- "merge" travis.yml and travis_for_plugins.yml files (no need to keep em + separate) +- add more useful helper functions +- remove tmux-test repo as a submodule from self, this causes issues with + `$ git submodule update --recursive --init` command that some users use for + managing other plugins +- add new helper `teardown_helper` +- add `run_tests` helper +- change CLI syntax for choosing vagrant machine to run the tests on +- enable running just a single test via `run_tests` cli interface +- add `--keep-running` cli option to continue running vagrant after the tests + are done executing +- start using tmux 2.0 for tests + +### v0.2.0, 2015-02-22 +- `setup` script gitignores `tests/helpers.sh` +- move `tests/helpers.sh` to `tests/helpers/helpers.sh` +- `setup` undo removes added lines from gitignore file + +### v0.1.0, 2015-02-22 +- changes so that 'tmux-test' can be included with tmux plugins +- do not gitignore submodules directory +- add installation and usage instructions +- copy `.travis.yml` to the project root when running `setup` script +- add a brief mention of travis CI to the readme +- add test helpers +- `setup` script symlinks helpers file to `tests/` directory +- `setup` script can undo most of its actions +- add a tmux scripting test +- `tmux-test` uses `tmux-test` to test itself +- update `tmux-test` submodule +- a different `travis.yml` for `tmux-test` and for plugins + +### v0.0.1, 2015-02-21 +- git init +- add vagrant provisioning scripts for ubuntu and debian +- add a ".travis.yml" file +- generic "run_tests" script +- "run_tests_in_isolation" script +- add "Vagrantfile" +- enable passing VM names as arguments to "run_tests" script diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/LICENSE.md b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/LICENSE.md new file mode 100644 index 0000000..e6e7350 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/LICENSE.md @@ -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. diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/README.md b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/README.md new file mode 100644 index 0000000..27dccc9 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/README.md @@ -0,0 +1,134 @@ +# tmux-test + +[![Build Status](https://travis-ci.org/tmux-plugins/tmux-test.png?branch=master)](https://travis-ci.org/tmux-plugins/tmux-test) + +A small framework for isolated testing of tmux plugins. Isolation is achieved by +running the tests in `Vagrant`. Works on [travis](travis-ci.org) too. + +Extracted from [tmux plugin manager](https://github.com/tmux-plugins/tpm) and +[tmux-copycat](https://github.com/tmux-plugins/tmux-copycat). + +Dependencies: `Vagrant` (no required when running on travis). + +### Setup + +Let's say you made tmux plugin with the following file hierarchy: + +```text +/tmux-plugin +|-- plugin.tmux +`-- scripts + `-- plugin_script.sh +``` + +From your project root directory (tmux-plugin/) execute the following shell +command to fetch `tmux-test` and add it as a submodule: + + $ git submodule add https://github.com/tmux-plugins/tmux-test.git lib/tmux-test + +Run the `setup` script: + + $ lib/tmux-test/setup + +The project directory will now look like this (additions have comments): + +```text +/tmux-plugin +|-- plugin.tmux +|-- run_tests # symlink, gitignored +|-- .gitignore # 2 lines appended to gitignore +|-- .travis.yml # added +|-- lib/tmux-test/ # git submodule +|-- scripts +| `-- plugin_script.sh +`-- tests # dir to put the tests in + `-- run_tests_in_isolation.sh # symlink, gitignored + `-- helpers + `-- helpers.sh # symlinked bash helpers, gitignored +``` + +`tmux-test` is now set up. You are ok to commit the additions to the repo. + +### Writing and running tests + +A test is any executable with a name starting with `test_` in `tests/` +directory. + +Now that you installed `tmux-test` let's create an example test. + +- create a `tests/test_example.sh` file with the following content (it's a + `bash` script but it can be any executable): + + #/usr/bin/env bash + + CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + # bash helpers provided by 'tmux-test' + source $CURRENT_DIR/helpers/helpers.sh + + # installs plugin from current repo in Vagrant (or on Travis) + install_tmux_plugin_under_test_helper + + # start tmux in background (plugin under test is sourced) + tmux new -d + + # get first session name + session_name="$(tmux list-sessions -F "#{session_name}")" + + # fail the test if first session name is not "0" + if [ "$session_name" == "0" ]; then + # fail_helper is also provided by 'tmux-test' + fail_helper "First session name is not '0' by default" + fi + + # sets the right script exit code ('tmux-test' helper) + exit_helper + +- make the test file executable with `$ chmod +x tests/test_example.sh` +- run the test by executing `./run_tests` from the project root directory +- the first invocation might take some time because Vagrant's ubuntu virtual + machine is downloading. You should see `Success, tests pass!` message when it's + done. + +Check out more example test scripts in this project's [tests/ directory](tests/). + +### Continuous integration + +The setup script (`lib/tmux-test/setup`) added a `.travis.yml` file to the +project root. To setup continuous integration, just add/enable the project on +[travis](travis-ci.org). + +### Notes + +- The `tests/` directory for tests and `lib/tmux-test/` for cloning `tmux-test` + into cannot be changed currently +- Don't run `tests/run_tests_in_isolation` script on your local development + environment. That's an internal test runner meant to be executed in an + isolated environment like `vagrant` or `travis`.
+ Use `./run_tests` script. +- You can use `KEEP_RUNNING=true ./run_tests` for faster test running cycle. + If this case `Vagrant` will keep running even after the tests are done. +- You can use `VAGRANT_CWD=lib/tmux-text/ vagrant ssh ubuntu` for ssh login to + `Vagrant`. + +### Running `tmux-test` framework tests + +`tmux-test` uses itself to test itself. To run framework tests: + +- clone this project `$ git clone git@github.com:tmux-plugins/tmux-test.git` +- `$ cd tmux-test` +- run `$ ./run_framework_tests` + +### Other goodies + +- [tmux-copycat](https://github.com/tmux-plugins/tmux-copycat) - a plugin for + regex searches in tmux and fast match selection +- [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) - automatic + restoring and continuous saving of tmux env + +You might want to follow [@brunosutic](https://twitter.com/brunosutic) on +twitter if you want to hear about new tmux plugins or feature updates. + +### License + +[MIT](LICENSE.md) diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/Vagrantfile b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/Vagrantfile new file mode 100644 index 0000000..04b3eba --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/Vagrantfile @@ -0,0 +1,17 @@ +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + + config.vm.synced_folder "../../", "/vagrant" + + config.vm.define :ubuntu do |ubuntu| + ubuntu.vm.box = "hashicorp/precise64" + ubuntu.vm.provision "shell", path: "vagrant_ubuntu_provisioning.sh" + end + + config.vm.define :centos do |centos| + centos.vm.box = "chef/centos-6.5" + centos.vm.provision "shell", path: "vagrant_centos_provisioning.sh" + end + +end diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/run_framework_tests b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/run_framework_tests new file mode 100755 index 0000000..ed7f634 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/run_framework_tests @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# This file is used to run "tmux-test" framework tests. + +# "setup" script is needed to run the tests, but it overrides some working dir +# files. To address that, "setup" is run before the tests and it's actions are +# undone after. + +main() { + git clone https://github.com/tmux-plugins/tmux-test lib/tmux-test + lib/tmux-test/setup + ./run_tests + local exit_value=$? + lib/tmux-test/setup "undo" + exit "$exit_value" +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/setup b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/setup new file mode 100755 index 0000000..575a8a3 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/setup @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# invoke this script from your projects root directory + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# pass "undo" as a script arg to undo most of the setup actions +UNDO_SETUP="$1" +undo() { + [ "$UNDO_SETUP" == "undo" ] +} + +restore() { + local file="$1" + rm -f "$file" + git checkout -- "$file" 2>/dev/null +} + +gitignore() { + local file="$1" + grep -q "^${file}$" .gitignore 2>/dev/null || echo "$file" >> .gitignore +} + +remove_from_gitignore() { + local file="$1" + local escaped_filename="$(echo "$file" | sed "s,/,\\\/,g")" + sed -i"" "/^${escaped_filename}$/d" .gitignore +} + +add_files_to_gitignore() { + if ! undo; then + gitignore "run_tests" + gitignore "tests/run_tests_in_isolation" + gitignore "tests/helpers/helpers.sh" + else + remove_from_gitignore "run_tests" + remove_from_gitignore "tests/run_tests_in_isolation" + remove_from_gitignore "tests/helpers/helpers.sh" + fi +} + +symlink_user_test_runner() { + local file="run_tests" + if ! undo; then + ln -sf "lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +create_directory_for_tests() { + if ! undo; then + mkdir -p tests/helpers/ + fi +} + +symlink_internal_test_runner() { + local file="tests/run_tests_in_isolation" + if ! undo; then + ln -sf "../lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +symlink_test_helpers() { + local file="tests/helpers/helpers.sh" + if ! undo; then + ln -sf "../../lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +copy_travis_yml() { + local file=".travis.yml" + if ! undo; then + cp "lib/tmux-test/${file}" "$file" + else + restore "$file" + fi +} + +main() { + add_files_to_gitignore + symlink_user_test_runner + create_directory_for_tests + symlink_internal_test_runner + symlink_test_helpers + copy_travis_yml +} +main + diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/helpers/helpers.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/helpers/helpers.sh new file mode 100644 index 0000000..32b1ee4 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/helpers/helpers.sh @@ -0,0 +1,68 @@ +# This file is a symlink from 'tmux-test' plugin. +# You probably don't want to edit it. + + +# Global variable that keeps the value of test status (success/fail). +# Suggested usage is via `fail_helper` and `exit_helper` functions. +TEST_STATUS="success" + +# PRIVATE FUNCTIONS + +_clone_the_plugin() { + local plugin_path="${HOME}/.tmux/plugins/tmux-plugin-under-test/" + rm -rf "$plugin_path" + git clone --recursive "${CURRENT_DIR}/../" "$plugin_path" >/dev/null 2>&1 +} + +_add_plugin_to_tmux_conf() { + set_tmux_conf_helper<<-HERE + run-shell '~/.tmux/plugins/tmux-plugin-under-test/*.tmux' + HERE +} + +# PUBLIC HELPER FUNCTIONS + +teardown_helper() { + rm -f ~/.tmux.conf + rm -rf ~/.tmux/ + tmux kill-server >/dev/null 2>&1 +} + +set_tmux_conf_helper() { + > ~/.tmux.conf # empty tmux.conf file + while read line; do + echo "$line" >> ~/.tmux.conf + done +} + +fail_helper() { + local message="$1" + echo "$message" >&2 + TEST_STATUS="fail" +} + +exit_helper() { + teardown_helper + if [ "$TEST_STATUS" == "fail" ]; then + echo "FAIL!" + echo + exit 1 + else + echo "SUCCESS" + echo + exit 0 + fi +} + +install_tmux_plugin_under_test_helper() { + _clone_the_plugin + _add_plugin_to_tmux_conf +} + +run_tests() { + # get all the functions starting with 'test_' and invoke them + for test in $(compgen -A function | grep "^test_"); do + "$test" + done + exit_helper +} diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/run_tests_in_isolation b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/run_tests_in_isolation new file mode 100755 index 0000000..fa39ebe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/run_tests_in_isolation @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# This file is a symlink from 'tmux-test' plugin. +# You probably don't want to edit it. + +# This script should be run within an isolated enviroment (Vagrant, travis). +# Depending on what the tests do, it might NOT be safe to run this script +# directly on the development machine. + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +EXIT_VALUE=0 # running a test suite is successful by default + +all_test_files() { + ls -1 "$CURRENT_DIR" | # test files are in the current dir + \grep -i "^test" | # test file names start with "test" + xargs # file names in a single line +} + +set_exit_val_to_false() { + EXIT_VALUE=1 +} + +run_tests() { + local test_file tests_files + if [ "$#" -gt 0 ]; then + test_files="${@//tests\//}" # remove 'tests/' directory prefix + else + test_files="$(all_test_files)" + fi + for test_file in $test_files; do + echo "Running test: $test_file" + "${CURRENT_DIR}/${test_file}" + + # handling exit value + local test_exit_value="$?" + if [ "$test_exit_value" -ne 0 ]; then + set_exit_val_to_false + fi + done +} + +main() { + run_tests "$@" + exit "$EXIT_VALUE" +} +main "$@" diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_basic_script_execution.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_basic_script_execution.sh new file mode 100755 index 0000000..0fdcf09 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_basic_script_execution.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +exit 0 diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_default_session_name.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_default_session_name.sh new file mode 100755 index 0000000..c761b93 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_default_session_name.sh @@ -0,0 +1,24 @@ +#/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# bash helpers provided by 'tmux-test' +source $CURRENT_DIR/helpers/helpers.sh + +# installs plugin from current repo in Vagrant (or on Travis) +install_tmux_plugin_under_test_helper + +# start tmux in background (plugin under test is sourced) +tmux new -d + +# get first session name +session_name="$(tmux list-sessions -F "#{session_name}")" + +# fail the test if first session name is not "0" +if ! [ "$session_name" == "0" ]; then + # fail_helper is also provided by 'tmux-test' + fail_helper "First session name is not '0' by default" +fi + +# sets the right script exit code ('tmux-test' helper) +exit_helper diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_tmux_scripting.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_tmux_scripting.sh new file mode 100755 index 0000000..3b4bece --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/tests/test_tmux_scripting.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +source $CURRENT_DIR/helpers/helpers.sh + +number_of_windows() { + tmux list-windows | + wc -l | + sed "s/ //g" +} + +main() { + # start tmux in the background + tmux new -d + tmux new-window + + local number_of_windows="$(number_of_windows)" + if ! [ "$number_of_windows" -eq 2 ]; then + fail_helper "Incorrect number of windows. Expected 2, got $number_of_windows" + fi + exit_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_centos_provisioning.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_centos_provisioning.sh new file mode 100644 index 0000000..20a282b --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_centos_provisioning.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# libevent2 installation instructions from here +# https://gist.github.com/rschuman/6168833 + +sudo su - + +yum -y install gcc kernel-devel make automake autoconf ncurses-devel +yum -y install git-core expect vim ruby ruby-devel ruby-irb + +# install libevent2 from source +curl http://sourceforge.net/projects/levent/files/latest/download?source=files -L -o libevent2.tar.gz -w 'Last URL was: %{url_effective}' +cd ~/downloads +tar zxvf libevent2.tar.gz +cd ./libevent-* +./configure --prefix=/usr/local +make +make install + +# compile tmux +git clone https://github.com/tmux/tmux.git ~/tmux_source +cd ~/tmux_source +git checkout 2.0 +sh autogen.sh +LDFLAGS="-L/usr/local/lib -Wl,-rpath=/usr/local/lib" ./configure --prefix=/usr/local +make && sudo make install diff --git a/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_ubuntu_provisioning.sh b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_ubuntu_provisioning.sh new file mode 100644 index 0000000..63a60ea --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/lib/tmux-test/vagrant_ubuntu_provisioning.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +sudo apt-get update +sudo apt-get install -y git-core expect vim +sudo apt-get install -y python-software-properties software-properties-common +sudo apt-get install -y build-essential libtool autotools-dev autoconf +sudo apt-get install -y pkg-config libevent-dev libncurses-dev + +# install tmux 2.0 +git clone https://github.com/tmux/tmux.git ~/tmux_source +cd ~/tmux_source +git checkout 2.0 +sh autogen.sh +./configure && make && sudo make install diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/check_tmux_version.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/check_tmux_version.sh new file mode 100755 index 0000000..b0aedec --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/check_tmux_version.sh @@ -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 diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/clean_plugins.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/clean_plugins.sh new file mode 100755 index 0000000..a025524 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/clean_plugins.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +HELPERS_DIR="$CURRENT_DIR/helpers" + +source "$HELPERS_DIR/plugin_functions.sh" +source "$HELPERS_DIR/utility.sh" + +if [ "$1" == "--tmux-echo" ]; then # tmux-specific echo functions + source "$HELPERS_DIR/tmux_echo_functions.sh" +else # shell output functions + source "$HELPERS_DIR/shell_echo_functions.sh" +fi + +clean_plugins() { + local plugins plugin plugin_directory + plugins="$(tpm_plugins_list_helper)" + + for plugin_directory in "$(tpm_path)"/*; do + [ -d "${plugin_directory}" ] || continue + plugin="$(plugin_name_helper "${plugin_directory}")" + case "${plugins}" in + *"${plugin}"*) : ;; + *) + [ "${plugin}" = "tpm" ] && continue + echo_ok "Removing \"$plugin\"" + rm -rf "${plugin_directory}" >/dev/null 2>&1 + [ -d "${plugin_directory}" ] && + echo_err " \"$plugin\" clean fail" || + echo_ok " \"$plugin\" clean success" + ;; + esac + done +} + +main() { + ensure_tpm_path_exists + clean_plugins + exit_value_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/plugin_functions.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/plugin_functions.sh new file mode 100644 index 0000000..f33d215 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/plugin_functions.sh @@ -0,0 +1,104 @@ +# using @tpm_plugins is now deprecated in favor of using @plugin syntax +tpm_plugins_variable_name="@tpm_plugins" + +# manually expanding tilde char or `$HOME` variable. +_manual_expansion() { + local path="$1" + local expanded_tilde="${path/#\~/$HOME}" + echo "${expanded_tilde/#\$HOME/$HOME}" +} + +_tpm_path() { + local string_path="$(tmux start-server\; show-environment -g TMUX_PLUGIN_MANAGER_PATH | cut -f2 -d=)/" + _manual_expansion "$string_path" +} + +_CACHED_TPM_PATH="$(_tpm_path)" + +# Get the absolute path to the users configuration file of TMux. +# This includes a prioritized search on different locations. +# +_get_user_tmux_conf() { + # Define the different possible locations. + xdg_location="${XDG_CONFIG_HOME:-$HOME/.config}/tmux/tmux.conf" + default_location="$HOME/.tmux.conf" + + # Search for the correct configuration file by priority. + if [ -f "$xdg_location" ]; then + echo "$xdg_location" + + else + echo "$default_location" + fi +} + +_tmux_conf_contents() { + user_config=$(_get_user_tmux_conf) + cat /etc/tmux.conf "$user_config" 2>/dev/null + if [ "$1" == "full" ]; then # also output content from sourced files + local file + for file in $(_sourced_files); do + cat $(_manual_expansion "$file") 2>/dev/null + done + fi +} + +# return files sourced from tmux config files +_sourced_files() { + _tmux_conf_contents | + sed -E -n -e "s/^[[:space:]]*source(-file)?[[:space:]]+(-q+[[:space:]]+)?['\"]?([^'\"]+)['\"]?/\3/p" +} + +# Want to be able to abort in certain cases +trap "exit 1" TERM +export TOP_PID=$$ + +_fatal_error_abort() { + echo >&2 "Aborting." + kill -s TERM $TOP_PID +} + +# PUBLIC FUNCTIONS BELOW + +tpm_path() { + if [ "$_CACHED_TPM_PATH" == "/" ]; then + echo >&2 "FATAL: Tmux Plugin Manager not configured in tmux.conf" + _fatal_error_abort + fi + echo "$_CACHED_TPM_PATH" +} + +tpm_plugins_list_helper() { + # lists plugins from @tpm_plugins option + echo "$(tmux start-server\; show-option -gqv "$tpm_plugins_variable_name")" + + # read set -g @plugin "tmux-plugins/tmux-example-plugin" entries + _tmux_conf_contents "full" | + awk '/^[ \t]*set(-option)? +-g +@plugin/ { gsub(/'\''/,""); gsub(/'\"'/,""); print $4 }' +} + +# Allowed plugin name formats: +# 1. "git://github.com/user/plugin_name.git" +# 2. "user/plugin_name" +plugin_name_helper() { + local plugin="$1" + # get only the part after the last slash, e.g. "plugin_name.git" + local plugin_basename="$(basename "$plugin")" + # remove ".git" extension (if it exists) to get only "plugin_name" + local plugin_name="${plugin_basename%.git}" + echo "$plugin_name" +} + +plugin_path_helper() { + local plugin="$1" + local plugin_name="$(plugin_name_helper "$plugin")" + echo "$(tpm_path)${plugin_name}/" +} + +plugin_already_installed() { + local plugin="$1" + local plugin_path="$(plugin_path_helper "$plugin")" + [ -d "$plugin_path" ] && + cd "$plugin_path" && + git remote >/dev/null 2>&1 +} diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/shell_echo_functions.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/shell_echo_functions.sh new file mode 100644 index 0000000..ecaa37e --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/shell_echo_functions.sh @@ -0,0 +1,7 @@ +echo_ok() { + echo "$*" +} + +echo_err() { + fail_helper "$*" +} diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_echo_functions.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_echo_functions.sh new file mode 100644 index 0000000..7a6ef0a --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_echo_functions.sh @@ -0,0 +1,28 @@ +_has_emacs_mode_keys() { + $(tmux show -gw mode-keys | grep -q emacs) +} + +tmux_echo() { + local message="$1" + tmux run-shell "echo '$message'" +} + +echo_ok() { + tmux_echo "$*" +} + +echo_err() { + tmux_echo "$*" +} + +end_message() { + if _has_emacs_mode_keys; then + local continue_key="ESCAPE" + else + local continue_key="ENTER" + fi + tmux_echo "" + tmux_echo "TMUX environment reloaded." + tmux_echo "" + tmux_echo "Done, press $continue_key to continue." +} diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_utils.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_utils.sh new file mode 100644 index 0000000..238952d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/tmux_utils.sh @@ -0,0 +1,6 @@ +HELPERS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$HELPERS_DIR/plugin_functions.sh" + +reload_tmux_environment() { + tmux source-file $(_get_user_tmux_conf) >/dev/null 2>&1 +} diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/utility.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/utility.sh new file mode 100644 index 0000000..de6eb35 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/helpers/utility.sh @@ -0,0 +1,17 @@ +ensure_tpm_path_exists() { + mkdir -p "$(tpm_path)" +} + +fail_helper() { + local message="$1" + echo "$message" >&2 + FAIL="true" +} + +exit_value_helper() { + if [ "$FAIL" == "true" ]; then + exit 1 + else + exit 0 + fi +} diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/install_plugins.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/install_plugins.sh new file mode 100755 index 0000000..e2450ac --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/install_plugins.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +HELPERS_DIR="$CURRENT_DIR/helpers" + +source "$HELPERS_DIR/plugin_functions.sh" +source "$HELPERS_DIR/utility.sh" + +if [ "$1" == "--tmux-echo" ]; then # tmux-specific echo functions + source "$HELPERS_DIR/tmux_echo_functions.sh" +else # shell output functions + source "$HELPERS_DIR/shell_echo_functions.sh" +fi + +clone() { + local plugin="$1" + local branch="$2" + if [ -n "$branch" ]; then + cd "$(tpm_path)" && + GIT_TERMINAL_PROMPT=0 git clone -b "$branch" --single-branch --recursive "$plugin" >/dev/null 2>&1 + else + cd "$(tpm_path)" && + GIT_TERMINAL_PROMPT=0 git clone --single-branch --recursive "$plugin" >/dev/null 2>&1 + fi +} + +# tries cloning: +# 1. plugin name directly - works if it's a valid git url +# 2. expands the plugin name to point to a GitHub repo and tries cloning again +clone_plugin() { + local plugin="$1" + local branch="$2" + clone "$plugin" "$branch" || + clone "https://git::@github.com/$plugin" "$branch" +} + +# clone plugin and produce output +install_plugin() { + local plugin="$1" + local branch="$2" + local plugin_name="$(plugin_name_helper "$plugin")" + + if plugin_already_installed "$plugin"; then + echo_ok "Already installed \"$plugin_name\"" + else + echo_ok "Installing \"$plugin_name\"" + clone_plugin "$plugin" "$branch" && + echo_ok " \"$plugin_name\" download success" || + echo_err " \"$plugin_name\" download fail" + fi +} + +install_plugins() { + local plugins="$(tpm_plugins_list_helper)" + for plugin in $plugins; do + IFS='#' read -ra plugin <<< "$plugin" + install_plugin "${plugin[0]}" "${plugin[1]}" + done +} + +verify_tpm_path_permissions() { + local path="$(tpm_path)" + # check the write permission flag for all users to ensure + # that we have proper access + [ -w "$path" ] || + echo_err "$path is not writable!" +} + +main() { + ensure_tpm_path_exists + verify_tpm_path_permissions + install_plugins + exit_value_helper +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/source_plugins.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/source_plugins.sh new file mode 100755 index 0000000..6381d54 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/source_plugins.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +HELPERS_DIR="$CURRENT_DIR/helpers" + +source "$HELPERS_DIR/plugin_functions.sh" + +plugin_dir_exists() { + [ -d "$1" ] +} + +# Runs all *.tmux files from the plugin directory. +# Files are ran as executables. +# No errors if the plugin dir does not exist. +silently_source_all_tmux_files() { + local plugin_path="$1" + local plugin_tmux_files="$plugin_path*.tmux" + if plugin_dir_exists "$plugin_path"; then + for tmux_file in $plugin_tmux_files; do + # if the glob didn't find any files this will be the + # unexpanded glob which obviously doesn't exist + [ -f "$tmux_file" ] || continue + # runs *.tmux file as an executable + $tmux_file >/dev/null 2>&1 + done + fi +} + +source_plugins() { + local plugin plugin_path + local plugins="$(tpm_plugins_list_helper)" + for plugin in $plugins; do + IFS='#' read -ra plugin <<< "$plugin" + plugin_path="$(plugin_path_helper "${plugin[0]}")" + silently_source_all_tmux_files "$plugin_path" + done +} + +main() { + source_plugins +} +main diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin.sh new file mode 100755 index 0000000..e533664 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# this script handles core logic of updating plugins + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +HELPERS_DIR="$CURRENT_DIR/helpers" + +source "$HELPERS_DIR/plugin_functions.sh" +source "$HELPERS_DIR/utility.sh" + +if [ "$1" == "--tmux-echo" ]; then # tmux-specific echo functions + source "$HELPERS_DIR/tmux_echo_functions.sh" +else # shell output functions + source "$HELPERS_DIR/shell_echo_functions.sh" +fi + +# from now on ignore first script argument +shift + +pull_changes() { + local plugin="$1" + local plugin_path="$(plugin_path_helper "$plugin")" + cd "$plugin_path" && + GIT_TERMINAL_PROMPT=0 git pull && + GIT_TERMINAL_PROMPT=0 git submodule update --init --recursive +} + +update() { + local plugin="$1" output + output=$(pull_changes "$plugin" 2>&1) + if (( $? == 0 )); then + echo_ok " \"$plugin\" update success" + echo_ok "$(echo "$output" | sed -e 's/^/ | /')" + else + echo_err " \"$plugin\" update fail" + echo_err "$(echo "$output" | sed -e 's/^/ | /')" + fi +} + +update_all() { + echo_ok "Updating all plugins!" + echo_ok "" + local plugins="$(tpm_plugins_list_helper)" + for plugin in $plugins; do + IFS='#' read -ra plugin <<< "$plugin" + local plugin_name="$(plugin_name_helper "${plugin[0]}")" + # updating only installed plugins + if plugin_already_installed "$plugin_name"; then + update "$plugin_name" & + fi + done + wait +} + +update_plugins() { + local plugins="$*" + for plugin in $plugins; do + IFS='#' read -ra plugin <<< "$plugin" + local plugin_name="$(plugin_name_helper "${plugin[0]}")" + if plugin_already_installed "$plugin_name"; then + update "$plugin_name" & + else + echo_err "$plugin_name not installed!" & + fi + done + wait +} + +main() { + ensure_tpm_path_exists + if [ "$1" == "all" ]; then + update_all + else + update_plugins "$*" + fi + exit_value_helper +} +main "$*" diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin_prompt_handler.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin_prompt_handler.sh new file mode 100755 index 0000000..5e1f7d9 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/update_plugin_prompt_handler.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +HELPERS_DIR="$CURRENT_DIR/helpers" + +if [ $# -eq 0 ]; then + exit 0 +fi + +source "$HELPERS_DIR/tmux_echo_functions.sh" +source "$HELPERS_DIR/tmux_utils.sh" + +main() { + "$CURRENT_DIR/update_plugin.sh" --tmux-echo "$*" + reload_tmux_environment + end_message +} +main "$*" diff --git a/dotfiles/common/.tmux/plugins/tpm/scripts/variables.sh b/dotfiles/common/.tmux/plugins/tpm/scripts/variables.sh new file mode 100644 index 0000000..5601a86 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/scripts/variables.sh @@ -0,0 +1,13 @@ +install_key_option="@tpm-install" +default_install_key="I" + +update_key_option="@tpm-update" +default_update_key="U" + +clean_key_option="@tpm-clean" +default_clean_key="M-u" + +SUPPORTED_TMUX_VERSION="1.9" + +DEFAULT_TPM_ENV_VAR_NAME="TMUX_PLUGIN_MANAGER_PATH" +DEFAULT_TPM_PATH="$HOME/.tmux/plugins/" diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_failed_plugin_download b/dotfiles/common/.tmux/plugins/tpm/tests/expect_failed_plugin_download new file mode 100755 index 0000000..b970477 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_failed_plugin_download @@ -0,0 +1,36 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + I +send "I" + +# cloning might take a while +set timeout 20 + +expect_after { + timeout { exit 1 } +} + +expect { + "Installing \"non-existing-plugin\"" +} + +expect { + "\"non-existing-plugin\" download fail" +} + +expect { + "Done, press ENTER to continue" { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_clean_plugins b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_clean_plugins new file mode 100755 index 0000000..987c49d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_clean_plugins @@ -0,0 +1,35 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + alt + u +send "u" + +set timeout 5 + +expect_after { + timeout { exit 1 } +} + +expect { + "Removing \"tmux-example-plugin\"" +} + +expect { + "\"tmux-example-plugin\" clean success" +} + +expect { + "Done, press ENTER to continue." { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_multiple_plugins_download b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_multiple_plugins_download new file mode 100755 index 0000000..cc87a26 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_multiple_plugins_download @@ -0,0 +1,44 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + I +send "I" + +# cloning might take a while +set timeout 15 + +expect_after { + timeout { exit 1 } +} + +expect { + "Installing \"tmux-example-plugin\"" +} + +expect { + "\"tmux-example-plugin\" download success" +} + +expect { + "Installing \"tmux-copycat\"" +} + +expect { + "\"tmux-copycat\" download success" +} + +expect { + "Done, press ENTER to continue." { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_plugin_download b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_plugin_download new file mode 100755 index 0000000..388f05d --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_plugin_download @@ -0,0 +1,50 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + I +send "I" + +# cloning might take a while +set timeout 15 + +expect_after { + timeout { exit 1 } +} + +expect { + "Installing \"tmux-example-plugin\"" +} + +expect { + "\"tmux-example-plugin\" download success" +} + +expect { + "Done, press ENTER to continue" { + send " " + } +} + +sleep 1 +# this is tmux prefix + I +send "I" + +expect { + "Already installed \"tmux-example-plugin\"" +} + +expect { + "Done, press ENTER to continue" { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_a_single_plugin b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_a_single_plugin new file mode 100755 index 0000000..bcd64fe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_a_single_plugin @@ -0,0 +1,55 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + U +send "U" + +set timeout 15 + +expect_after { + timeout { exit 1 } +} + +expect { + "Installed plugins" +} + +expect { + "tmux-example-plugin" +} + +expect { + "\"all\" - updates all plugins" +} + +expect { + "ENTER - cancels" +} + +# wait for tmux to display prompt before sending characters +sleep 1 +send "tmux-example-plugin\r" + +expect { + "Updating \"tmux-example-plugin\"" +} + +expect { + "\"tmux-example-plugin\" update success" +} + +expect { + "Done, press ENTER to continue." { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_all_plugins b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_all_plugins new file mode 100755 index 0000000..4f3a4a3 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/expect_successful_update_of_all_plugins @@ -0,0 +1,59 @@ +#!/usr/bin/env expect + +# disables script output +log_user 0 + +spawn tmux + +# Waiting for tmux to attach. If this is not done, next command, `send` will +# not work properly. +sleep 1 + +# this is tmux prefix + U +send "U" + +set timeout 5 + +expect_after { + timeout { exit 1 } +} + +expect { + "Installed plugins" +} + +expect { + "tmux-example-plugin" +} + +expect { + "\"all\" - updates all plugins" +} + +expect { + "ENTER - cancels" +} + +# wait for tmux to display prompt before sending characters +sleep 1 +send "all\r" + +expect { + "Updating all plugins!" +} + +expect { + "Updating \"tmux-example-plugin\"" +} + +expect { + "\"tmux-example-plugin\" update success" +} + +expect { + "Done, press ENTER to continue." { + exit 0 + } +} + +exit 1 diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/helpers/tpm.sh b/dotfiles/common/.tmux/plugins/tpm/tests/helpers/tpm.sh new file mode 100644 index 0000000..1594afb --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/helpers/tpm.sh @@ -0,0 +1,13 @@ +check_dir_exists_helper() { + [ -d "$1" ] +} + +# runs the scripts and asserts it has the correct output and exit code +script_run_helper() { + local script="$1" + local expected_output="$2" + local expected_exit_code="${3:-0}" + $script 2>&1 | + grep "$expected_output" >/dev/null 2>&1 && # grep -q flag quits the script early + [ "${PIPESTATUS[0]}" -eq "$expected_exit_code" ] +} diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_clean.sh b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_clean.sh new file mode 100755 index 0000000..d36c468 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_clean.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TPM_DIR="$PWD" +PLUGINS_DIR="$HOME/.tmux/plugins" + +source "$CURRENT_DIR/helpers/helpers.sh" +source "$CURRENT_DIR/helpers/tpm.sh" + +manually_install_the_plugin() { + rm -rf "$PLUGINS_DIR" + mkdir -p "$PLUGINS_DIR" + cd "$PLUGINS_DIR" + git clone --quiet https://github.com/tmux-plugins/tmux-example-plugin +} + +# TMUX KEY-BINDING TESTS + +test_plugin_uninstallation_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + run-shell "$TPM_DIR/tpm" + HERE + + manually_install_the_plugin + + "$CURRENT_DIR/expect_successful_clean_plugins" || + fail_helper "[key-binding] clean fails" + + teardown_helper +} + +# SCRIPT TESTS + +test_plugin_uninstallation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + run-shell "$TPM_DIR/tpm" + HERE + + manually_install_the_plugin + + script_run_helper "$TPM_DIR/bin/clean_plugins" '"tmux-example-plugin" clean success' || + fail_helper "[script] plugin cleaning fails" + + teardown_helper +} + +test_unsuccessful_plugin_uninstallation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + run-shell "$TPM_DIR/tpm" + HERE + + manually_install_the_plugin + chmod 000 "$PLUGINS_DIR/tmux-example-plugin" # disable directory deletion + + local expected_exit_code=1 + script_run_helper "$TPM_DIR/bin/clean_plugins" '"tmux-example-plugin" clean fail' "$expected_exit_code" || + fail_helper "[script] unsuccessful plugin cleaning doesn't fail" + + chmod 755 "$PLUGINS_DIR/tmux-example-plugin" # enable directory deletion + + teardown_helper +} + +run_tests diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation.sh b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation.sh new file mode 100755 index 0000000..94fb674 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation.sh @@ -0,0 +1,284 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PLUGINS_DIR="$HOME/.tmux/plugins" +TPM_DIR="$PWD" + +CUSTOM_PLUGINS_DIR="$HOME/foo/plugins" +ADDITIONAL_CONFIG_FILE_1="$HOME/.tmux/additional_config_file_1" +ADDITIONAL_CONFIG_FILE_2="$HOME/.tmux/additional_config_file_2" + +source "$CURRENT_DIR/helpers/helpers.sh" +source "$CURRENT_DIR/helpers/tpm.sh" + +# TMUX KEY-BINDING TESTS + +test_plugin_installation_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + "$CURRENT_DIR/expect_successful_plugin_download" || + fail_helper "[key-binding] plugin installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding] plugin download fails" + + teardown_helper +} + +test_plugin_installation_via_tmux_key_binding_set_option() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set-option -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + "$CURRENT_DIR/expect_successful_plugin_download" || + fail_helper "[key-binding][set-option] plugin installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding][set-option] plugin download fails" + + teardown_helper +} + +test_plugin_installation_custom_dir_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set-environment -g TMUX_PLUGIN_MANAGER_PATH '$CUSTOM_PLUGINS_DIR' + + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + "$CURRENT_DIR/expect_successful_plugin_download" || + fail_helper "[key-binding][custom dir] plugin installation fails" + + check_dir_exists_helper "$CUSTOM_PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding][custom dir] plugin download fails" + + teardown_helper + rm -rf "$CUSTOM_PLUGINS_DIR" +} + +test_non_existing_plugin_installation_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/non-existing-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + "$CURRENT_DIR/expect_failed_plugin_download" || + fail_helper "[key-binding] non existing plugin installation doesn't fail" + + teardown_helper +} + +test_multiple_plugins_installation_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + \ \ set -g @plugin 'tmux-plugins/tmux-copycat' + run-shell "$TPM_DIR/tpm" + HERE + + "$CURRENT_DIR/expect_successful_multiple_plugins_download" || + fail_helper "[key-binding] multiple plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[key-binding] plugin download fails (tmux-copycat)" + + teardown_helper +} + +test_plugins_installation_from_sourced_file_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + source '$ADDITIONAL_CONFIG_FILE_1' + set -g @plugin 'tmux-plugins/tmux-example-plugin' + run-shell "$TPM_DIR/tpm" + HERE + + mkdir ~/.tmux + echo "set -g @plugin 'tmux-plugins/tmux-copycat'" > "$ADDITIONAL_CONFIG_FILE_1" + + "$CURRENT_DIR/expect_successful_multiple_plugins_download" || + fail_helper "[key-binding][sourced file] plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding][sourced file] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[key-binding][sourced file] plugin download fails (tmux-copycat)" + + teardown_helper +} + +test_plugins_installation_from_multiple_sourced_files_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + \ \ source '$ADDITIONAL_CONFIG_FILE_1' + source-file '$ADDITIONAL_CONFIG_FILE_2' + run-shell "$TPM_DIR/tpm" + HERE + + mkdir ~/.tmux + echo "set -g @plugin 'tmux-plugins/tmux-example-plugin'" > "$ADDITIONAL_CONFIG_FILE_1" + echo " set -g @plugin 'tmux-plugins/tmux-copycat'" > "$ADDITIONAL_CONFIG_FILE_2" + + "$CURRENT_DIR/expect_successful_multiple_plugins_download" || + fail_helper "[key-binding][multiple sourced files] plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding][multiple sourced files] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[key-binding][multiple sourced files] plugin download fails (tmux-copycat)" + + teardown_helper +} + +# SCRIPT TESTS + +test_plugin_installation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-example-plugin" download success' || + fail_helper "[script] plugin installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script] plugin download fails" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-example-plugin"' || + fail_helper "[script] plugin already installed message fail" + + teardown_helper +} + +test_plugin_installation_custom_dir_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set-environment -g TMUX_PLUGIN_MANAGER_PATH '$CUSTOM_PLUGINS_DIR' + + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-example-plugin" download success' || + fail_helper "[script][custom dir] plugin installation fails" + + check_dir_exists_helper "$CUSTOM_PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script][custom dir] plugin download fails" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-example-plugin"' || + fail_helper "[script][custom dir] plugin already installed message fail" + + teardown_helper + rm -rf "$CUSTOM_PLUGINS_DIR" +} + +test_non_existing_plugin_installation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/non-existing-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + local expected_exit_code=1 + script_run_helper "$TPM_DIR/bin/install_plugins" '"non-existing-plugin" download fail' "$expected_exit_code" || + fail_helper "[script] non existing plugin installation doesn't fail" + + teardown_helper +} + +test_multiple_plugins_installation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + \ \ set -g @plugin 'tmux-plugins/tmux-copycat' + run-shell "$TPM_DIR/tpm" + HERE + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-example-plugin" download success' || + fail_helper "[script] multiple plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[script] plugin download fails (tmux-copycat)" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-copycat"' || + fail_helper "[script] multiple plugins already installed message fail" + + teardown_helper +} + +test_plugins_installation_from_sourced_file_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + source '$ADDITIONAL_CONFIG_FILE_1' + set -g @plugin 'tmux-plugins/tmux-example-plugin' + run-shell "$TPM_DIR/tpm" + HERE + + mkdir ~/.tmux + echo "set -g @plugin 'tmux-plugins/tmux-copycat'" > "$ADDITIONAL_CONFIG_FILE_1" + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-copycat" download success' || + fail_helper "[script][sourced file] plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script][sourced file] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[script][sourced file] plugin download fails (tmux-copycat)" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-copycat"' || + fail_helper "[script][sourced file] plugins already installed message fail" + + teardown_helper +} + +test_plugins_installation_from_multiple_sourced_files_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + \ \ source '$ADDITIONAL_CONFIG_FILE_1' + source-file '$ADDITIONAL_CONFIG_FILE_2' + set -g @plugin 'tmux-plugins/tmux-example-plugin' + run-shell "$TPM_DIR/tpm" + HERE + + mkdir ~/.tmux + echo " set -g @plugin 'tmux-plugins/tmux-copycat'" > "$ADDITIONAL_CONFIG_FILE_1" + echo "set -g @plugin 'tmux-plugins/tmux-sensible'" > "$ADDITIONAL_CONFIG_FILE_2" + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-sensible" download success' || + fail_helper "[script][multiple sourced files] plugins installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script][multiple sourced files] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[script][multiple sourced files] plugin download fails (tmux-copycat)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-sensible/" || + fail_helper "[script][multiple sourced files] plugin download fails (tmux-sensible)" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-sensible"' || + fail_helper "[script][multiple sourced files] plugins already installed message fail" + + teardown_helper +} + +run_tests diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation_legacy.sh b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation_legacy.sh new file mode 100755 index 0000000..b1d0cf6 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_installation_legacy.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PLUGINS_DIR="$HOME/.tmux/plugins" +TPM_DIR="$PWD" + +source "$CURRENT_DIR/helpers/helpers.sh" +source "$CURRENT_DIR/helpers/tpm.sh" + +# TMUX KEY-BINDING TESTS + +test_plugin_installation_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @tpm_plugins "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + # opens tmux and test it with `expect` + $CURRENT_DIR/expect_successful_plugin_download || + fail_helper "[key-binding] plugin installation fails" + + # check plugin dir exists after download + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding] plugin download fails" + + teardown_helper +} + +test_legacy_and_new_syntax_for_plugin_installation_work_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @tpm_plugins " \ + tmux-plugins/tmux-example-plugin \ + " + set -g @plugin 'tmux-plugins/tmux-copycat' + run-shell "$TPM_DIR/tpm" + HERE + + # opens tmux and test it with `expect` + "$CURRENT_DIR"/expect_successful_multiple_plugins_download || + fail_helper "[key-binding] multiple plugins installation fails" + + # check plugin dir exists after download + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[key-binding] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[key-binding] plugin download fails (tmux-copycat)" + + teardown_helper +} + +# SCRIPT TESTS + +test_plugin_installation_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @tpm_plugins "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-example-plugin" download success' || + fail_helper "[script] plugin installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script] plugin download fails" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-example-plugin"' || + fail_helper "[script] plugin already installed message fail" + + teardown_helper +} + +test_legacy_and_new_syntax_for_plugin_installation_work_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @tpm_plugins " \ + tmux-plugins/tmux-example-plugin \ + " + set -g @plugin 'tmux-plugins/tmux-copycat' + run-shell "$TPM_DIR/tpm" + HERE + + script_run_helper "$TPM_DIR/bin/install_plugins" '"tmux-example-plugin" download success' || + fail_helper "[script] multiple plugin installation fails" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-example-plugin/" || + fail_helper "[script] plugin download fails (tmux-example-plugin)" + + check_dir_exists_helper "$PLUGINS_DIR/tmux-copycat/" || + fail_helper "[script] plugin download fails (tmux-copycat)" + + script_run_helper "$TPM_DIR/bin/install_plugins" 'Already installed "tmux-copycat"' || + fail_helper "[script] multiple plugins already installed message fail" + + teardown_helper +} + +run_tests diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_sourcing.sh b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_sourcing.sh new file mode 100755 index 0000000..c06f1fe --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_sourcing.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TPM_DIR="$PWD" +PLUGINS_DIR="$HOME/.tmux/plugins" + +CUSTOM_PLUGINS_DIR="$HOME/foo/plugins" + +source "$CURRENT_DIR/helpers/helpers.sh" +source "$CURRENT_DIR/helpers/tpm.sh" + +check_binding_defined() { + local binding="$1" + tmux list-keys | grep -q "$binding" +} + +create_test_plugin_helper() { + local plugin_path="$PLUGINS_DIR/tmux_test_plugin/" + rm -rf "$plugin_path" + mkdir -p "$plugin_path" + + while read line; do + echo "$line" >> "$plugin_path/test_plugin.tmux" + done + chmod +x "$plugin_path/test_plugin.tmux" +} + +check_tpm_path() { + local correct_tpm_path="$1" + local tpm_path="$(tmux start-server\; show-environment -g TMUX_PLUGIN_MANAGER_PATH | cut -f2 -d=)" + [ "$correct_tpm_path" == "$tpm_path" ] +} + +test_plugin_sourcing() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "doesnt_matter/tmux_test_plugin" + run-shell "$TPM_DIR/tpm" + HERE + + # manually creates a local tmux plugin + create_test_plugin_helper <<- HERE + tmux bind-key R run-shell foo_command + HERE + + tmux new-session -d # tmux starts detached + check_binding_defined "R run-shell foo_command" || + fail_helper "Plugin sourcing fails" + + teardown_helper +} + +test_default_tpm_path() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + run-shell "$TPM_DIR/tpm" + HERE + + check_tpm_path "${PLUGINS_DIR}/" || + fail_helper "Default TPM path not correct" + + teardown_helper +} + +test_custom_tpm_path() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set-environment -g TMUX_PLUGIN_MANAGER_PATH '$CUSTOM_PLUGINS_DIR' + run-shell "$TPM_DIR/tpm" + HERE + + check_tpm_path "$CUSTOM_PLUGINS_DIR" || + fail_helper "Custom TPM path not correct" + + teardown_helper +} + +run_tests diff --git a/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_update.sh b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_update.sh new file mode 100755 index 0000000..4924d16 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tests/test_plugin_update.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TPM_DIR="$PWD" +PLUGINS_DIR="$HOME/.tmux/plugins" + +source "$CURRENT_DIR/helpers/helpers.sh" +source "$CURRENT_DIR/helpers/tpm.sh" + +manually_install_the_plugin() { + mkdir -p "$PLUGINS_DIR" + cd "$PLUGINS_DIR" + git clone --quiet https://github.com/tmux-plugins/tmux-example-plugin +} + +# TMUX KEY-BINDING TESTS + +test_plugin_update_via_tmux_key_binding() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + manually_install_the_plugin + + "$CURRENT_DIR/expect_successful_update_of_all_plugins" || + fail_helper "[key-binding] 'update all plugins' fails" + + "$CURRENT_DIR/expect_successful_update_of_a_single_plugin" || + fail_helper "[key-binding] 'update single plugin' fails" + + teardown_helper +} + +# SCRIPT TESTS + +test_plugin_update_via_script() { + set_tmux_conf_helper <<- HERE + set -g mode-keys vi + set -g @plugin "tmux-plugins/tmux-example-plugin" + run-shell "$TPM_DIR/tpm" + HERE + + manually_install_the_plugin + + local expected_exit_code=1 + script_run_helper "$TPM_DIR/bin/update_plugins" 'usage' "$expected_exit_code" || + fail_helper "[script] running update plugins without args should fail" + + script_run_helper "$TPM_DIR/bin/update_plugins tmux-example-plugin" '"tmux-example-plugin" update success' || + fail_helper "[script] plugin update fails" + + script_run_helper "$TPM_DIR/bin/update_plugins all" '"tmux-example-plugin" update success' || + fail_helper "[script] update all plugins fails" + + teardown_helper +} + +run_tests diff --git a/dotfiles/common/.tmux/plugins/tpm/tpm b/dotfiles/common/.tmux/plugins/tpm/tpm new file mode 100755 index 0000000..7ad4b99 --- /dev/null +++ b/dotfiles/common/.tmux/plugins/tpm/tpm @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +BINDINGS_DIR="$CURRENT_DIR/bindings" +SCRIPTS_DIR="$CURRENT_DIR/scripts" + +source "$SCRIPTS_DIR/variables.sh" + +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 +} + +tpm_path_set() { + tmux show-environment -g "$DEFAULT_TPM_ENV_VAR_NAME" >/dev/null 2>&1 +} + +# Check if configuration file exists at an XDG-compatible location, if so use +# that directory for TMUX_PLUGIN_MANAGER_PATH. Otherwise use $DEFAULT_TPM_PATH. +set_default_tpm_path() { + local xdg_tmux_path="${XDG_CONFIG_HOME:-$HOME/.config}/tmux" + local tpm_path="$DEFAULT_TPM_PATH" + + if [ -f "$xdg_tmux_path/tmux.conf" ]; then + tpm_path="$xdg_tmux_path/plugins/" + fi + + tmux set-environment -g "$DEFAULT_TPM_ENV_VAR_NAME" "$tpm_path" +} + +# Ensures TMUX_PLUGIN_MANAGER_PATH global env variable is set. +# +# Put this in `.tmux.conf` to override the default: +# `set-environment -g TMUX_PLUGIN_MANAGER_PATH "/some/other/path/"` +set_tpm_path() { + if ! tpm_path_set; then + set_default_tpm_path + fi +} + +# 1. Fetches plugin names from `@plugin` variables +# 2. Creates full plugin path +# 3. Sources all *.tmux files from each of the plugin directories +# - no errors raised if directory does not exist +# Files are sourced as tmux config files, not as shell scripts! +source_plugins() { + "$SCRIPTS_DIR/source_plugins.sh" >/dev/null 2>&1 +} + +# prefix + I - downloads TPM plugins and reloads TMUX environment +# prefix + U - updates a plugin (or all of them) and reloads TMUX environment +# prefix + alt + u - remove unused TPM plugins and reloads TMUX environment +set_tpm_key_bindings() { + local install_key="$(get_tmux_option "$install_key_option" "$default_install_key")" + tmux bind-key "$install_key" run-shell "$BINDINGS_DIR/install_plugins" + + local update_key="$(get_tmux_option "$update_key_option" "$default_update_key")" + tmux bind-key "$update_key" run-shell "$BINDINGS_DIR/update_plugins" + + local clean_key="$(get_tmux_option "$clean_key_option" "$default_clean_key")" + tmux bind-key "$clean_key" run-shell "$BINDINGS_DIR/clean_plugins" +} + +supported_tmux_version_ok() { + "$SCRIPTS_DIR/check_tmux_version.sh" "$SUPPORTED_TMUX_VERSION" +} + +main() { + if supported_tmux_version_ok; then + set_tpm_path + set_tpm_key_bindings + source_plugins + fi +} +main