dotfiles/roles/common/files/bashrc

456 lines
16 KiB
Bash

# shellcheck shell=bash
# Return immediately if non-interactive (makes FTP clients happy)
[[ "$-" == *i* ]] || return
##############################################################################
# Customize environment
##############################################################################
export XDG_CACHE_HOME="$HOME/.cache"
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_DATA_HOME="$HOME/.local/share"
export EDITOR="vim"
export INPUTRC="$XDG_CONFIG_HOME/readline/inputrc"
export LANG="en_US.UTF-8"
export LANGUAGE="en_US"
export LC_ALL="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
export LESS="-i -j.49 -M -R -z-2"
export LESSHISTFILE="$XDG_DATA_HOME/less/history"
export LESSHISTSIZE=1000
export LOCAL_CONFIG="$HOME/.local/etc"
export LOCAL_PREFIX="/usr/local"
if command -v brew &>/dev/null; then
LOCAL_PREFIX="$(brew --prefix)"
export HOMEBREW_NO_ANALYTICS=1
export HOMEBREW_NO_AUTO_UPDATE=1
fi
MANPATH="$(unset MANPATH; manpath)"
export MANPATH
export PAGER=less
export PYTHONSTARTUP="$XDG_CONFIG_HOME/python/startup.py"
# shellcheck disable=SC2016 # This expression is to be interpreted by vim, not bash.
export VIMINIT='let $MYVIMRC="$XDG_CONFIG_HOME/vim/vimrc" | source $MYVIMRC'
##############################################################################
# Customize PATH (and MANPATH)
##############################################################################
# Prevent path_helper from messing with the PATH when starting tmux.
# See: https://superuser.com/a/583502
# shellcheck disable=SC2123 # PATH is being intentionally manipulated here.
# shellcheck disable=SC1091 # /etc/profile is provided by macOS.
[ "$(uname)" == "Darwin" ] && { PATH=""; source /etc/profile; }
_prepend_path() { # 1: dir to add, 2: path variable to manipulate
if [ -d "$1" ] && [ -n "$2" ]; then
local _path="${!2}" # get path variable value
case ":$_path:" in
*":$1:"*) :;; # dir already in path, noop (:)
*) _path="$1${_path:+:}$_path";; # prepend (adding : if not empty)
esac
printf -v "$2" "%s" "$_path" # write back to path variable
fi
}
# Add custom bin dirs to PATH if they exist and are not already in PATH.
while read -r dir; do _prepend_path "$dir" PATH; done <<EOL
$LOCAL_PREFIX/bin
$LOCAL_PREFIX/opt/man-db/libexec/bin
$LOCAL_PREFIX/opt/coreutils/libexec/gnubin
$LOCAL_PREFIX/opt/gnu-sed/libexec/gnubin
$HOME/.local/bin
EOL
# Prepend custom man directories to MANPATH if they exist, so that we get
# correct man page entries when multiple versions of a command are
# available.
while read -r dir; do _prepend_path "$dir" MANPATH; done <<EOL
$LOCAL_PREFIX/share/man
$LOCAL_PREFIX/opt/man-db/libexec/man
$LOCAL_PREFIX/opt/coreutils/libexec/gnuman
$LOCAL_PREFIX/opt/gnu-sed/libexec/gnuman
$HOME/.local/share/man
EOL
unset dir
##############################################################################
# Customize shell options & variables
##############################################################################
shopt -s cdspell checkwinsize globstar histappend nocaseglob
set -o noclobber # Prevent overwriting files with output redirection.
# Eternal bash history (from https://stackoverflow.com/a/19533853)
HISTCONTROL=erasedups
HISTFILESIZE=
HISTSIZE=
HISTTIMEFORMAT="[%F %T] "
HISTFILE="$XDG_DATA_HOME/bash/history"
_tput_wrapper() {
local fmt="$1"; shift
printf "$fmt" "$@" | tput -S | sed -E "s/\x1B\[/ \x1B\[/g"
}
# shellcheck disable=SC2034 # these variable are meant for use in shell only
{
Reset="$(tput sgr0)"
read -a _FG <<<$(_tput_wrapper 'setaf %d\n' {0..15})
read -a _BG <<<$(_tput_wrapper 'setab %d\n' {0..15})
# Color definitions (from http://ethanschoonover.com/solarized)
# CSI foreground CSI background RGB HEX ANSI Index ANSI Codes
# ---------------- --------------------- -------------------- ----------- ------------
Base03=${_FG[ 8]} Base03_BG=${_BG[ 8]} Base03_RGB="002B36" Base03_N=8 # 1;30 brblack
Base02=${_FG[ 0]} Base02_BG=${_BG[ 0]} Base02_RGB="073642" Base02_N=0 # 0;30 black
Base01=${_FG[10]} Base01_BG=${_BG[10]} Base01_RGB="586E75" Base01_N=10 # 1;32 brgreen
Base00=${_FG[11]} Base00_BG=${_BG[11]} Base00_RGB="657B83" Base00_N=11 # 1;33 bryellow
Base0=${_FG[12]} Base0_BG=${_BG[12]} Base0_RGB="839496" Base0_N=12 # 1;34 brblue
Base1=${_FG[14]} Base1_BG=${_BG[14]} Base1_RGB="93A1A1" Base1_N=14 # 1;36 brcyan
Base2=${_FG[ 7]} Base2_BG=${_BG[ 7]} Base2_RGB="EEE8D5" Base2_N=7 # 0;37 white
Base3=${_FG[15]} Base3_BG=${_BG[15]} Base3_RGB="FDF6E3" Base3_N=15 # 1;37 brwhite
Red=${_FG[ 1]} Red_BG=${_BG[ 1]} Red_RGB="DC322F" Red_N=1 # 0;31 red
Orange=${_FG[ 9]} Orange_BG=${_BG[ 9]} Orange_RGB="CB4B16" Orange_N=9 # 1;31 brred
Yellow=${_FG[ 3]} Yellow_BG=${_BG[ 3]} Yellow_RGB="B58900" Yellow_N=3 # 0;33 yellow
Green=${_FG[ 2]} Green_BG=${_BG[ 2]} Green_RGB="859900" Green_N=2 # 0;32 green
Cyan=${_FG[ 6]} Cyan_BG=${_BG[ 6]} Cyan_RGB="2AA198" Cyan_N=6 # 0;36 cyan
Blue=${_FG[ 4]} Blue_BG=${_BG[ 4]} Blue_RGB="268BD2" Blue_N=4 # 0;34 blue
Violet=${_FG[13]} Violet_BG=${_BG[13]} Violet_RGB="6C71C4" Violet_N=13 # 1;35 brmagenta
Magenta=${_FG[ 5]} Magenta_BG=${_BG[ 5]} Magenta_RGB="D33682" Magenta_N=5 # 0;35 magenta
PS1_EXIT="$Red" # color for last exit code if non-zero
PS1_ROOT="$Orange" # logged in as root
PS1_SSH="$Yellow" # hostname for SSH sessions
PS1_PWD="$Cyan" # PWD color
PS1_GIT="$Blue" # color for git branch
PS1_VENV="$Violet" # color for python virtual env
PS1_JOBS="$Magenta" # color for background jobs
PS1_SEP=" > " # separator between prompt parts
PS1_SEP_LIGHT="$Base1"
PS1_SEP_DARK="$Base01"
GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWSTASHSTATE=1
GIT_PS1_SHOWUNTRACKEDFILES=1
GIT_PS1_SHOWUPSTREAM=verbose
}
PROMPT_COMMAND=__ps1_set
PS2="... "
__ps1_set() {
local exit=$?
local sep="$PS1_SEP_DARK$PS1_SEP"
local prompt=">>>>>>>>>>"
[ "$BACKGROUND" = "light" ] && sep="$PS1_SEP_LIGHT$PS1_SEP"
local ps=()
[ $exit -ne 0 ] && ps+=("$PS1_EXIT$exit")
[ $EUID -eq 0 ] && { ps+=("$PS1_ROOT\u"); prompt="##########"; }
[ -n "$SSH_CONNECTION" ] && ps+=("$PS1_SSH\h")
ps+=("$PS1_PWD\w")
type __git_ps1 && __git_ps1 '' '' "$PS1_GIT%s" && [ -n "$PS1" ] && ps+=("$PS1")
[ -n "$VIRTUAL_ENV" ] && ps+=("$PS1_VENV${VIRTUAL_ENV##*/}")
local j="\j" && [ "${j@P}" -gt 0 ] && ps+=("$PS1_JOBS${j@P} bg")
local extra=""
[ ${#ps[@]} -gt 1 ] && printf -v extra "$sep%s" "${ps[@]:1}"
PS1="\n${ps[0]}$extra$Reset\n${prompt:0:$SHLVL} "
} &>/dev/null
##############################################################################
# Customize shell aliases
##############################################################################
# ls: make `ls` group directories first if supported.
# lsc: force `ls` to use color output (e.g. for piping into `less`).
if command -v exa >/dev/null; then
alias ls="exa -F --git --group-directories-first --group --links -I'.git'"
alias la="ls -a"
alias lt="ls -lT"
alias lta="ls -lTa"
alias lsc="ls --color=always"
elif ls --group-directories-first --color=auto &>/dev/null 2>&1; then
# GNU ls
alias ls="ls -hF --group-directories-first --color=auto"
alias la="ls -A"
alias lsc="ls --color=always"
else
# BSD ls (e.g. macOS)
alias ls="ls -hF -G"
alias la="ls -A"
alias lsc="/usr/bin/env CLICOLOR_FORCE=1 ls"
fi
alias ll="ls -l"
alias lla="la -l"
alias g='git'
alias ga='git a'
alias gc='git commit'
alias gd='git d'
alias gcm='git cm'
alias gl='git l'
alias gla='git la'
alias gln='git ln'
alias gs='git s'
alias gss='git ss'
alias v='vim'
alias grep="grep --color=auto"
alias egrep="egrep --color=auto"
alias fgrep="fgrep --color=auto"
alias path='echo $PATH | tr -s ":" "\n"'
alias mpath='echo $MANPATH | tr -s ":" "\n"'
alias timer='echo "Timer started. Stop with Ctrl-D." && date && time cat && date'
alias tmux='tmux -f "$XDG_CONFIG_HOME/tmux/tmux.conf"'
# Head and tail as much as fits on screen
alias head='head -n $((${LINES:-15}-5))'
alias tail='tail -n $((${LINES:-15}-5))'
# A few options to get public IP address on command line. The dig solution
# below using the OpenDNS resolver doesn't work when connected to
# ExpressVPN because all DNS requests are handled by the ExpressVPN DNS
# servers and the OpenDNS DNS resolver is blocked.
alias ipinfo="curl -s ipinfo.io"
alias myip="curl -s https://ifconfig.co"
#alias myip="curl -s https://ifconfig.me"
#alias myip="dig +short myip.opendns.com @resolver1.opendns.com"
alias light='_update_colors light'
alias dark='_update_colors dark'
##############################################################################
# Terminal color management
##############################################################################
# Useful terminal control sequences:
#
# Sequence Abbrev Description
# ----- ----- -----------------------------
# ESC P DCS Device Control String
# ESC [ CSI Control Sequence Introducer
# ESC \ ST String Terminator
# ESC ] OSC Operating System Control
#
#
# More info at:
# * https://www.xfree86.org/4.8.0/ctlseqs.html
# * https://vt100.net/
# * https://iterm2.com/utilities/imgcat
# In tmux, OSC sequences can be forwarded to the underlying terminal by:
# * Wrapping the sequence with `DCS tmux; <sequence> ST`
# * Replacing ESC in <sequence> with ESC ESC.
# Either ST or BEL (\a) can be used to end an OSC sequence. However, tmux
# requires ST to end its wrapping DCS.
if [ -n "$TMUX" ]; then
_OSC=$(printf "\ePtmux;\e\e]")
_ST=$(printf "\a\e\\")
else
_OSC=$(printf "\e]")
_ST=$(printf "\a")
fi
_update_colors() {
export BACKGROUND="$1"
if [ "$BACKGROUND" = "light" ]; then
_set_terminal_colors $Base01_RGB $Base3_RGB $Red_RGB # fg bg cursor
else
_set_terminal_colors $Base1_RGB $Base03_RGB $Red_RGB # fg bg cursor
fi
if [ -n "$TMUX" ] && [ -f "$XDG_CONFIG_HOME/tmux/tmux-colors.conf" ]; then
tmux set-environment -g BACKGROUND "$BACKGROUND"
tmux source-file "$XDG_CONFIG_HOME/tmux/tmux-colors.conf"
fi
local ls_colors="$HOME/.config/dircolors/solarized-$BACKGROUND"
if type dircolors &>/dev/null && [ -f "$ls_colors" ]; then
eval "$(dircolors "$ls_colors")"
fi
}
_set_terminal_colors() { # 1: foreground, 2: background, 3: cursor
if [ -n "$ITERM_SESSION_ID" ]; then # iTerm2
_set_iterm2_colors "$1" "$2" "$3"
else # assume xterm
_set_xterm_colors "$1" "$2" "$3"
fi
}
# Documentation for iTerm2 at
# https://iterm2.com/documentation-escape-codes.html
# under the heading "Change the color palette".
_set_iterm2_colors() { # 1: foreground, 2: background, 3: cursor
local fg="$1" bg="$2" cursor="$3"
local key rgb
while read -r key rgb; do
printf "%s1337;SetColors=%s=%s%s" "$_OSC" "$key" "${rgb}" "$_ST"
done <<EOL
black $Base02_RGB
red $Red_RGB
green $Green_RGB
yellow $Yellow_RGB
blue $Blue_RGB
magenta $Magenta_RGB
cyan $Cyan_RGB
white $Base2_RGB
br_black $Base03_RGB
br_red $Orange_RGB
br_green $Base01_RGB
br_yellow $Base00_RGB
br_blue $Base0_RGB
br_magenta $Violet_RGB
br_cyan $Base1_RGB
br_white $Base3_RGB
fg $fg
bg $bg
bold $fg
selfg $Base2_RGB
selbg $Base01_RGB
curfg $cursor
curbg $cursor
link $Cyan_RGB
EOL
printf "%s1337;CursorShape=2%s" "$_OSC" "$_ST" # set cursor shape to underline
}
# Documentation for xterm at https://www.xfree86.org/4.8.0/ctlseqs.html
# under the heading "Operating System Controls".
_set_xterm_colors() { # $1 foreground, $2 background, $3 cursor
local fg="$1" bg="$2" cursor="$3"
local c rgb Ps
while read -r c rgb; do
# Send sequence OSC Ps BEL
# where Ps -> 4;c;spec
# c -> index of the ANSI color to change [0..15]
# spec -> RGB value of color as rgb:RR/GG/BB
printf "%s4;%s;rgb:%s/%s/%s%s" "$_OSC" "$c" "${rgb:0:2}" "${rgb:2:2}" "${rgb:4:2}" "$_ST"
done <<EOL
0 $Base02_RGB
1 $Red_RGB
2 $Green_RGB
3 $Yellow_RGB
4 $Blue_RGB
5 $Magenta_RGB
6 $Cyan_RGB
7 $Base2_RGB
8 $Base03_RGB
9 $Orange_RGB
10 $Base01_RGB
11 $Base00_RGB
12 $Base0_RGB
13 $Violet_RGB
14 $Base1_RGB
15 $Base3_RGB
EOL
while read -r Ps rgb; do
# Send sequence OSC Ps ; Pt BEL
# where Ps -> foreground (10), background (11), or cursor (12)
# Pt -> RGB value of color as rgb:RR/GG/BB
printf "%s%s;rgb:%s/%s/%s%s" "$_OSC" "$Ps" "${rgb:0:2}" "${rgb:2:2}" "${rgb:4:2}" "$_ST"
done <<EOL
10 $fg
11 $bg
12 $cursor
EOL
}
##############################################################################
# Add shell functions
##############################################################################
tree() { command tree --dirsfirst -FI '.git|Spotlight-V100|.fseventsd' "$@"; }
ltree() { tree -C "$@" | less -R; }
# Combined mkdir and cd
mkcd() { mkdir -p -- "$1" && cd -P -- "$1" || return; }
# Colorized `man`
man() {
local standout bold underline
if [ "$BACKGROUND" = "light" ]; then
standout="$Base3$Cyan_BG"
bold="$Blue"
underline="$Base02$(tput smul)"
else
standout="$Base03$Cyan_BG"
bold="$Yellow"
underline="$Base3$(tput smul)"
fi
LESS_TERMCAP_so="$standout" \
LESS_TERMCAP_md="$bold" \
LESS_TERMCAP_us="$underline" \
LESS_TERMCAP_se="$Reset" \
LESS_TERMCAP_me="$Reset" \
LESS_TERMCAP_ue="$Reset" \
GROFF_NO_SGR=1 \
command man "$@"
}
solarized() {
local name rgb _bg n
for name in \
Red Orange Yellow Green Cyan Blue Violet Magenta \
Base{0{3..0},{0..3}}
do
rgb=${name}_RGB _bg=${name}_BG n=${name}_N
printf "${!_bg}%6s$Reset ${!name}#${!rgb} %2s $name$Reset\n" '' "${!n}"
done
}
styletest() {
printf "$(tput bold)This text has the bold attribute.${Reset}\n"
printf "$(tput dim)This text has the dim attribute.${Reset}\n"
printf "$(tput smso)This text has the standout (smso) attribute.${Reset}\n"
printf "$(tput smul)This text has the underline (smul) attribute.${Reset}\n"
printf "$(tput blink)This text has the blink attribute.${Reset}\n"
printf "$(tput rev)This text has the reverse attribute.${Reset}\n"
printf "$(tput invis)This text has the invisible attribute.${Reset}\n"
}
# Print all 256 colors
colortest() {
local i j
for i in $(seq 0 15); do
for j in $(seq 0 15); do
local n=$(( 16 * i + j ))
printf "$(tput setab $n) %3d " $n
done
echo "$Reset"
done
}
##############################################################################
# Run external customizations
##############################################################################
stty -ixon # disable ctrl-s and ctrl-q
_update_colors "${BACKGROUND:-dark}"
_source_extra_configs() {
local configs=(
/usr/local/etc/profile.d/bash_completion.sh
/usr/share/bash-completion/bash_completion
$LOCAL_CONFIG/bash/*
)
local f
for f in ${configs[@]}; do [ -f "$f" ] && source "$f"; done
}
_source_extra_configs
true