1# Don't run on non-interactive shells.
2[[ $- != *i* ]] && return
3
4# -----------------------------
5# User config
6# -----------------------------
7export EDITOR="${EDITOR:-nvim}"
8export VISUAL="${VISUAL:-$EDITOR}"
9export PAGER="${PAGER:-less}"
10export LESS="-R -F -X -S"
11export CLICOLOR=1
12export LSCOLORS="${LSCOLORS:-GxFxCxDxBxegedabagaced}"
13export LANG="${LANG:-en_US.UTF-8}"
14export LC_ALL="${LC_ALL:-en_US.UTF-8}"
15export EDITOR=nvim
16export VISUAL=nvim
17
18# -----------------------------
19# Shell behavior / safety
20# -----------------------------
21shopt -s globstar nullglob dotglob 2>/dev/null || true
22shopt -s histappend 2>/dev/null || true
23shopt -s cdspell 2>/dev/null || true
24shopt -s checkwinsize 2>/dev/null || true
25set -o notify
26set -o noclobber
27
28# -----------------------------
29# History
30# -----------------------------
31export HISTSIZE=100000
32export HISTFILESIZE=200000
33export HISTCONTROL=ignoreboth:erasedups
34export HISTIGNORE="ls:ll:la:pwd:cd:cd -:exit:clear"
35export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
36PROMPT_COMMAND="history -a; history -n; ${PROMPT_COMMAND:-true}"
37
38# -----------------------------
39# Path helpers
40# -----------------------------
41path_prepend() {
42 [[ -d "$1" ]] || return 0
43 case ":$PATH:" in
44 *":$1:"*) ;;
45 *) PATH="$1:$PATH" ;;
46 esac
47}
48path_append() {
49 [[ -d "$1" ]] || return 0
50 case ":$PATH:" in
51 *":$1:"*) ;;
52 *) PATH="$PATH:$1" ;;
53 esac
54}
55
56# Common user bins
57path_prepend "$HOME/.local/bin"
58
59# -----------------------------
60# Homebrew (macOS)
61# -----------------------------
62eval "$(brew shellenv)" 2>/dev/null || true
63
64# -----------------------------
65# bash-completion@2 (macOS / Homebrew)
66# -----------------------------
67# Homebrew path varies by arch.
68if [[ -r "$(brew --prefix 2>/dev/null)/etc/profile.d/bash_completion.sh" ]]; then
69 # shellcheck source=/dev/null
70 source "$(brew --prefix)/etc/profile.d/bash_completion.sh"
71elif [[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]]; then
72 # shellcheck source=/dev/null
73 source "/usr/local/etc/profile.d/bash_completion.sh"
74elif [[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]]; then
75 # shellcheck source=/dev/null
76 source "/opt/homebrew/etc/profile.d/bash_completion.sh"
77fi
78
79# -----------------------------
80# mise (tool versions)
81# -----------------------------
82if command -v mise >/dev/null 2>&1; then
83 eval "$(mise activate bash)" 2>/dev/null || true
84fi
85
86# -----------------------------
87# direnv (per-directory envs)
88# -----------------------------
89if command -v direnv >/dev/null 2>&1; then
90 eval "$(direnv hook bash)" 2>/dev/null || true
91fi
92
93# -----------------------------
94# Git-friendly defaults
95# -----------------------------
96export GIT_PAGER="${GIT_PAGER:-less -FRX}"
97export GIT_TERMINAL_PROMPT=1
98# Better diff highlighting if installed
99if command -v delta >/dev/null 2>&1; then
100 export GIT_PAGER="delta"
101fi
102
103# -----------------------------
104# Prompt
105# -----------------------------
106if command -v starship >/dev/null 2>&1; then
107 # Initialize Starship
108 eval "$(starship init bash)"
109else
110 # Fallback: Simple native prompt if starship is missing
111 PS1='\[\033[01;34m\]\w\[\033[00m\] \$ '
112fi
113
114# -----------------------------
115# Quality-of-life aliases
116# -----------------------------
117alias ..='cd ..'
118alias ...='cd ../..'
119alias ....='cd ../../..'
120
121# Safer rm/mv/cp prompts
122alias rm='rm -i'
123alias mv='mv -i'
124alias cp='cp -i'
125
126# Better ls
127if command -v eza >/dev/null 2>&1; then
128 alias ls='eza --group-directories-first'
129 alias ll='eza -lah --git --group-directories-first'
130 alias la='eza -a --group-directories-first'
131else
132 alias ls='ls -G'
133 alias ll='ls -lahG'
134 alias la='ls -aG'
135fi
136
137command -v rg >/dev/null 2>&1 && alias grep='rg'
138command -v fd >/dev/null 2>&1 && alias find='fd'
139
140# -----------------------------
141# Functions
142# -----------------------------
143# extract: one command to unpack many formats
144extract() {
145 [[ -f "$1" ]] || { echo "extract: '$1' is not a file" >&2; return 2; }
146 case "$1" in
147 *.tar.bz2) tar xjf "$1" ;;
148 *.tar.gz) tar xzf "$1" ;;
149 *.tar.xz) tar xJf "$1" ;;
150 *.tar) tar xf "$1" ;;
151 *.zip) unzip "$1" ;;
152 *.gz) gunzip "$1" ;;
153 *.bz2) bunzip2 "$1" ;;
154 *.xz) unxz "$1" ;;
155 *) echo "extract: don't know how to extract '$1'" >&2; return 3 ;;
156 esac
157}
158
159# copy: copy to clipboard
160copy() {
161 if [[ $# -gt 0 ]]; then
162 pbcopy < "$1"
163 else
164 pbcopy
165 fi
166}