Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/auto-assign.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
pull-requests: write
steps:
- name: 'Auto-assign issue'
uses: pozil/auto-assign-issue@v1
uses: pozil/auto-assign-issue@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
assignees: mathjoha
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,19 @@ brew install neovim tmux starship ripgrep fd
- `tmux.lua` — `vim-tmux-navigator` for Ctrl-h/j/k/l pane navigation (paired with `tmux/.tmux.conf`).
- `himalaya.lua` — `himalaya-vim` email client.
- `fzf.lua` — `junegunn/fzf` + `fzf.vim`.
- `tmux/.tmux.conf` — terminal defaults, mouse on, and the tmux side of seamless Vim/tmux Ctrl-h/j/k/l navigation.
- `tmux/.tmux.conf` — terminal defaults (mouse off), the tmux side of seamless Vim/tmux Ctrl-h/j/k/l navigation, and TPM-managed session persistence (tmux-resurrect + tmux-continuum).
- `starship/starship.toml` — Starship prompt config. Reloads automatically on the next prompt render — no command needed.
- `scripts/` — source-or-standalone helpers used by `setup.sh`: `starship.sh` (binary install + shell wiring) and `tmux.sh` (TPM clone, headless plugin install, persistent-history wiring).
- `git/gitignore_global` — global gitignore (symlinked to `~/.gitignore_global`, wired via `core.excludesfile`); keeps `Session.vim` out of `git status` everywhere.

## Session persistence

tmux sessions survive reboots. `setup.sh` clones [TPM](https://github.com/tmux-plugins/tpm) and installs [tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) + [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) headlessly (no `prefix + I` needed, though it still works).

- **Automatic**: continuum autosaves every 15 minutes and restores the last saved state when the tmux server starts after a reboot — windows, panes, working directories, and visible pane text included.
- **Manual**: save with `prefix + Ctrl-s`, restore with `prefix + Ctrl-r`.
- **Neovim**: panes that were running nvim reopen it from a `Session.vim` written automatically on exit (only when inside tmux). `Session.vim` files are globally gitignored via `git/gitignore_global`.
- **Shell history**: restored panes keep up-arrow history. `setup.sh` wires persistent history into `~/.bashrc` (`histappend` + `history -a` on every prompt) or `~/.zshrc` (`INC_APPEND_HISTORY`), depending on `$SHELL` — tmux-resurrect itself no longer supports history saving. Re-runnable standalone for other shells: `./scripts/tmux.sh --no-config`.

## Updating plugins

Expand Down
1 change: 1 addition & 0 deletions git/.gitconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

[core]
editor = nvim
excludesfile = ~/.gitignore_global

[init]
defaultBranch = main
Expand Down
3 changes: 3 additions & 0 deletions git/gitignore_global
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Ignored in every repo (core.excludesfile)
# Session files written by nvim for tmux-resurrect restore
Session.vim
13 changes: 13 additions & 0 deletions nvim/lua/config/autocmds.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Autocmds are automatically loaded on the VeryLazy event
-- Default autocmds that are always set: https://www.lazyvim.org/configuration/general#auto-commands

-- Write Session.vim on exit (only inside tmux) so tmux-resurrect's
-- nvim 'session' strategy can restore the editor after a reboot.
vim.api.nvim_create_autocmd("VimLeavePre", {
group = vim.api.nvim_create_augroup("tmux_resurrect_session", { clear = true }),
callback = function()
if vim.env.TMUX then
pcall(vim.cmd, "mksession! Session.vim")
end
end,
})
171 changes: 171 additions & 0 deletions scripts/tmux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#!/usr/bin/env bash
# tmux session persistence: install TPM, install plugins headlessly,
# wire persistent shell history (what tmux-resurrect no longer does upstream).
#
# Can be sourced by setup.sh (uses caller's helpers) or run standalone:
# ./scripts/tmux.sh # link config + TPM + plugins + history wiring
# ./scripts/tmux.sh --no-config # skip the config symlink
set -euo pipefail

_TMUX_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# --- Helpers (only defined when running standalone) ---
if ! declare -F log >/dev/null 2>&1; then
if [[ -t 1 ]]; then
C_OK=$'\033[1;32m'; C_WARN=$'\033[1;33m'; C_INFO=$'\033[1;34m'; C_RST=$'\033[0m'
else
C_OK=''; C_WARN=''; C_INFO=''; C_RST=''
fi
log() { printf '%s==>%s %s\n' "$C_INFO" "$C_RST" "$*"; }
ok() { printf '%s ok%s %s\n' "$C_OK" "$C_RST" "$*"; }
warn() { printf '%s !!%s %s\n' "$C_WARN" "$C_RST" "$*" >&2; }
dep_warn() { warn "$*"; }
fi

# --- Install TPM (tmux plugin manager) ---
install_tpm() {
local tpm_dir="$HOME/.tmux/plugins/tpm"

if [[ -d "$tpm_dir/.git" ]]; then
ok "TPM already installed: $tpm_dir"
return 0
fi

log "Installing TPM"
if ! git clone --depth 1 https://github.com/tmux-plugins/tpm "$tpm_dir" 2>/dev/null; then
dep_warn "TPM clone failed — install manually:"
printf ' git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm\n'
return 0
fi
ok "TPM installed: $tpm_dir"
}

# --- Install declared plugins without needing prefix+I ---
install_tmux_plugins() {
local installer="$HOME/.tmux/plugins/tpm/bin/install_plugins"

if [[ ! -f "$HOME/.tmux.conf" ]]; then
dep_warn "~/.tmux.conf not found — link it first, then re-run plugin install."
return 0
fi
if [[ ! -x "$installer" ]]; then
dep_warn "TPM not present — plugins not installed (prefix+I inside tmux also works)."
return 0
fi

export TMUX_PLUGIN_MANAGER_PATH="$HOME/.tmux/plugins"
if ! "$installer" >/dev/null 2>&1; then
dep_warn "TPM headless plugin install failed — press prefix+I inside tmux instead."
return 0
fi

local plugin
for plugin in tmux-resurrect tmux-continuum; do
if [[ -d "$HOME/.tmux/plugins/$plugin" ]]; then
ok "tmux plugin installed: $plugin"
else
dep_warn "tmux plugin missing after install: $plugin"
fi
done
}

# --- Wire persistent shell history into the shell rc ---
# Restored panes get up-arrow recall: every command is flushed to the history
# file immediately, so history survives reboots alongside resurrect's layout.
wire_history_persistence() {
local shell_name="${SHELL##*/}"
local rcfile

case "$shell_name" in
zsh) rcfile="$HOME/.zshrc" ;;
bash) rcfile="$HOME/.bashrc" ;;
fish)
ok "fish persists history by default — nothing to wire"
return 0
;;
*)
dep_warn "Unknown shell ($SHELL) — wire up persistent history manually."
return 0
;;
esac

if [[ -f "$rcfile" ]] && grep -q '# Persistent shell history' "$rcfile" 2>/dev/null; then
ok "Persistent history already in $rcfile"
return 0
fi

mkdir -p "$(dirname "$rcfile")"
if [[ "$shell_name" == "bash" ]]; then
cat >> "$rcfile" <<'EOF'

# Persistent shell history
shopt -s histappend
HISTSIZE=100000
HISTFILESIZE=200000
PROMPT_COMMAND="history -a${PROMPT_COMMAND:+; $PROMPT_COMMAND}"
EOF
else
cat >> "$rcfile" <<'EOF'

# Persistent shell history
HISTFILE="$HOME/.zsh_history"
HISTSIZE=100000
SAVEHIST=200000
setopt INC_APPEND_HISTORY
EOF
fi
ok "Persistent history added to $rcfile"
}

# --- Symlink tmux config ---
link_tmux_config() {
local src="$_TMUX_SCRIPT_DIR/../tmux/.tmux.conf"
local dst="$HOME/.tmux.conf"

# Resolve to absolute path
src="$(cd "$(dirname "$src")" && pwd)/$(basename "$src")"

if [[ ! -f "$src" ]]; then
warn "tmux config not found: $src"
return 1
fi

# If setup.sh's link_path is available, use it (handles conflicts properly)
if declare -F link_path >/dev/null 2>&1; then
link_path "$src" "$dst" "tmux config"
return
fi

# Standalone: simple symlink logic
if [[ -L "$dst" && "$(readlink "$dst")" == "$src" ]]; then
ok "tmux config already linked: $dst -> $src"
return
fi

if [[ -e "$dst" ]]; then
warn "tmux config: $dst already exists. Back it up, then re-run."
return 1
fi

ln -s "$src" "$dst"
ok "tmux config linked: $dst -> $src"
}

# --- Main (only when run directly, not sourced) ---
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
link_config=1
for arg in "$@"; do
case "$arg" in
--no-config) link_config=0 ;;
esac
done

log "Setting up tmux session persistence"
if (( link_config )); then
link_tmux_config
fi
install_tpm
install_tmux_plugins
wire_history_persistence
log "Done."
fi
10 changes: 10 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ STARSHIP_SRC="$SCRIPT_DIR/starship/starship.toml"
STARSHIP_DST="$HOME/.config/starship.toml"
GIT_SRC="$SCRIPT_DIR/git/.gitconfig"
GIT_DST="$HOME/.gitconfig"
GITIGNORE_SRC="$SCRIPT_DIR/git/gitignore_global"
GITIGNORE_DST="$HOME/.gitignore_global"

if [[ -t 1 ]]; then
C_OK=$'\033[1;32m'; C_WARN=$'\033[1;33m'; C_INFO=$'\033[1;34m'; C_RST=$'\033[0m'
Expand Down Expand Up @@ -125,6 +127,10 @@ check_nerd_font() {
# shellcheck source=scripts/starship.sh
source "$SCRIPT_DIR/scripts/starship.sh"

# tmux persistence: TPM, plugins, history wiring (shared with scripts/tmux.sh)
# shellcheck source=scripts/tmux.sh
source "$SCRIPT_DIR/scripts/tmux.sh"

macos_terminal_reminder() {
[[ "$OSTYPE" == darwin* ]] || return 0
printf ' macOS reminder: installing a Nerd Font does NOT change your terminal'\''s font.\n'
Expand Down Expand Up @@ -280,6 +286,7 @@ check_deps() {
macos_terminal_reminder
check_login_sources_bashrc
wire_starship_shell_init
wire_history_persistence
printf ' Optional: install the himalaya CLI if you use the himalaya-vim plugin.\n'
}

Expand All @@ -290,6 +297,7 @@ main() {
link_path "$STARSHIP_SRC" "$STARSHIP_DST" "starship config"

link_path "$GIT_SRC" "$GIT_DST" "git config"
link_path "$GITIGNORE_SRC" "$GITIGNORE_DST" "global gitignore"

# Remind about per-machine identity if not yet configured
if [[ ! -f "$HOME/.gitconfig.local" ]]; then
Expand All @@ -298,6 +306,8 @@ main() {
fi

install_starship
install_tpm
install_tmux_plugins
check_deps

if (( LINK_CONFLICTS > 0 )); then
Expand Down
16 changes: 15 additions & 1 deletion tmux/.tmux.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,18 @@ bind L resize-pane -R 5
bind r source-file ~/.tmux.conf \; display "Tmux configuration reloaded!"

# --- Status Bar ---
set -g status-right '#h [#W] (#S) %Y-%m-%d %H:%M'
set -g status-right '#h [#W] (#S) %Y-%m-%d %H:%M'

# --- Session Persistence (TPM + resurrect + continuum) ---
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'

# Restore visible pane text and relaunch nvim from Session.vim
set -g @resurrect-capture-pane-contents 'on'
set -g @resurrect-strategy-nvim 'session'
# Autosave every 15 min (continuum default) and restore on server start
set -g @continuum-restore 'on'

# Initialize TPM — keep this line at the very bottom of .tmux.conf
run '~/.tmux/plugins/tpm/tpm'
Loading