all repos — dot @ master

Shell, editor and terminal configuration, and the tools that go with them

config/profile (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
# shellcheck shell=bash
# profiling helpers
__profile_enabled=false
__timestamp() {
  # date +%s
  perl -MTime::HiRes=time -e 'printf "%d\n", time * 1000'
}
 if [ $__profile_enabled = true ]; then
 __previous_timestamp=$(__timestamp)
 fi
_profile() {
 if [ $__profile_enabled = true ]; then
   _new_timestamp=$(__timestamp)
   _diff=$((_new_timestamp - __previous_timestamp))
  echo "$_diff $1"
   __previous_timestamp=$_new_timestamp
 fi
}

_profile entry

# The INTERACTIVE layer.
#
# Plain environment (PATH, XDG, locale, tool homes) lives in config/env, which
# is POSIX sh and is loaded by *every* shell — see its header. This file holds
# only what must NOT run in every shell: aliases, history, terminal setup, and
# anything expensive (theme, jenv, and whatever profile.d brings in).
#
# Reached via ~/.zshrc (interactive zsh) and ~/.profile (login bash). Sourcing
# config/env here too, because a login bash reaches ~/.profile without ever
# passing through ~/.zshenv. It is idempotent, so the double load is harmless.
if [ -r "$HOME/.env" ]; then
  # shellcheck source=/dev/null
  . "$HOME/.env"
else
  # No ~/.env yet. A pull that does not run link.sh (by hand, or a sync without
  # --link) can leave this file updated and the ~/.env symlink still absent
  # - which would silently cost it PATH, XDG, locale and EDITOR. Fall back to
  # the copy next to this file, found through the ~/.profile symlink that got
  # us here. Harmless once link.sh has run.
  __dotfiles_self=$(readlink "$HOME/.profile" 2>/dev/null)
  if [ -n "$__dotfiles_self" ] && [ -r "${__dotfiles_self%/*}/env" ]; then
    # shellcheck source=/dev/null
    . "${__dotfiles_self%/*}/env"
  fi
  unset __dotfiles_self
fi
_profile env

# Everything below this point is bash/zsh syntax, and this file is not read
# only by bash and zsh: Ubuntu's /bin/sh is dash, and `sh -l` sources
# ~/.profile. dash cannot parse `[[ ]]` (noisy but survivable) and refuses a
# function named `exit`, which is a POSIX special builtin - that one is a fatal
# parse error, so dash dies here and never runs the command it was given. That
# silently broke every `sh -lc '...'` on every Linux host; it surfaced as a
# background daemon spawned that way failing before it could even create the
# log file its error message pointed at. macOS is unaffected: its /bin/sh is
# bash, so BASH_VERSION is set and the rest of the file still runs there.
#
# config/env above is POSIX and has already been sourced, so a plain sh keeps
# PATH, XDG, locale and EDITOR. It just stops before the interactive layer,
# which it had no use for anyway.
if [ -z "${BASH_VERSION-}" ] && [ -z "${ZSH_VERSION-}" ]; then
  return 0 2>/dev/null || exit 0
fi

# Marks that this file is already running, so ~/.bashrc (which we may source at
# the very end, for login bash) doesn't source us straight back. See the header
# of config/bashrc for the full two-way handshake.
__dotfiles_profile=1

if [[ "$(uname)" == "Darwin" ]]
then
  # Sometimes, when invoked via Karabiner, the HOME variable
  # will not be defined, even with a login shell
  : "${HOME:="/Users/$(whoami)"}"
elif [[ "$(uname)" == "Linux" ]]
then
  if command -v wl-copy >/dev/null && command -v wl-paste >/dev/null; then
    alias pbcopy='wl-copy'
    alias pbpaste='wl-paste'
  else
    alias pbcopy='xclip -selection clipboard'
    alias pbpaste='xclip -selection clipboard -o'
  fi
fi
_profile clipboard_tools

# ripgrep
# Remind to use rg instead of ag
# ag() {
#   >&2 echo "ERROR: You decided to switch to 'rg' (ripgrep)"
#   return 1
#   # command ag "$@"
# }
# if [[ -r "$HOME/.config/ripgrep/config" ]]; then
#   export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/config"
# fi

# XDG_{CONFIG,CACHE,DATA}_HOME are set in config/env.

# Set history file location and size.
SHELL_NAME="$(basename "$SHELL")"
SHELL_CACHE="$XDG_CACHE_HOME/$SHELL_NAME"
mkdir -p "$SHELL_CACHE"
HISTFILE="$SHELL_CACHE/history"
HISTSIZE=1000000
# SAVEHIST is read by zsh itself, which shellcheck can't lint for.
# shellcheck disable=SC2034
SAVEHIST=1000000

exists() { command -v "$1" >/dev/null 2>&1; } # Check if command exists (is in path).
lspath() { echo "$PATH" | tr ':' '\n'; } # list PATH entries
pathadd() { [[ -d "$1" ]] && PATH="$1"$(echo ":$PATH:" | sed "s|:$1:|:|" | sed 's|:$||'); }
pathapp() { [[ -d "$1" ]] && PATH=$(echo ":$PATH:" | sed "s|:$1:|:|" | sed 's|^:||')"$1"; }

# GPG
GPG_TTY=$(tty)
export GPG_TTY
pbsign() { pbpaste | gpg --clearsign | pbcopy; }
pbverify() { pbpaste | gpg --verify; }

# Show processes listening on TCP sockets
listening() {
  lsof -n -P -i4TCP -sTCP:LISTEN \
    | awk '{ print $9,$1,$2 }' \
    | awk -F '\:' '{ print $2 }' \
    | grep -E -v '^$' \
    | column -t \
    | sort -n
}

# https://sites.google.com/site/jimmyxu101/testing/use-tcpdump-to-monitor-http-traffic
httpdump() {
  sudo tcpdump -A -i "${2:-lo0}" -s 0 -q "tcp port $1 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)"
}

alias clc="fc -ln -1 | sed -e 's/\\\\n/\\n/g' -e 's/\\\\t/\\t/g' | pbcopy"

# bash

# DISABLE_AUTO_TITLE, the core PATH entries and the less/PAGER settings are all
# in config/env.
# use bash like vi !
# set -o vi
_profile bash

# when using sudo, use alias expansion (otherwise sudo ignores your aliases)
alias sudo='sudo '

# color
export CLICOLOR=1
THEME_COLORS="$(theme --colors)"
export THEME_COLORS
if [ "$(theme --mode)" = "light" ]; then
  export LSCOLORS=GxFxCxDxBxegedabagaced
else
  export LSCOLORS=GxFxBxDxCxegedabagacad
fi
if exists bat ; then
  # best for light: Coldark-Cold, Github, Monokai Extended Light, OneHalfLight, gruvbox-light
  # best for dark: 1337, Coldark-Dark, Monokai Extended, OneHalfDark, gruvbox-dark
  # config file in $XDG_CONFIG_HOME/bat/config takes precedence to this env var
  export BAT_THEME='ansi' # works well with both light and dark themes
  export BAT_STYLE='plain'
  export MANPAGER="sh -c 'col -bx | bat -l man -p'"
  export FZF_COMPLETION_OPTS='--preview '\''bat --color=always --style=numbers --line-range=:500 {}'\'
fi
if exists lsd
then
  alias ls='lsd'
fi
_profile color

# VISUAL/EDITOR and the node environment are set in config/env.

# # ruby
# _profile ruby-start
# export RBENV_ROOT="${RBENV_ROOT:-"$XDG_CACHE_HOME/rbenv"}" # Set rbenv location.
# activate-rbenv() {
#   if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
# }
# activate-rbenv
# if [ -d "$HOME/.rbenv/bin" ] ; then
#   pathapp "$HOME/.rbenv/bin"
#   eval "$(rbenv init -)"
# fi

# # add user installs bin to path
# if which ruby >/dev/null && which gem >/dev/null; then
#   pathapp "$(ruby -rubygems -e 'puts Gem.user_dir')/bin"
# fi
# _profile ruby-done

# python
# export VIRTUAL_ENV_DISABLE_PROMPT=1 # Add the virtualenv prompt myself.
# LANG/LC_* are set in config/env — mosh needs a UTF-8 locale in shells that
# never reach this file.
# If you want to use pyenv
# if [ -x "$(command -v pyenv)" ] ; then
#   export PYENV_ROOT="$XDG_DATA_HOME/pyenv"
#   pathadd "$PYENV_ROOT/bin"
#   eval "$(pyenv init --path)"
# fi
# Instead of virtualenvwrapper's:
#   mkvirtualenv -p <path/to/python> <name>
# I prefer to use:
#   virtualenv --python=<path/to/python> .venv
# If you want to use virtualenvwrapper, uncomment this:
# if [ -r "$HOME/.local/pipx/venvs" ] ; then
#   export WORKON_HOME="$XDG_DATA_HOME/virtualenvs"
#   export VIRTUALENVWRAPPER_PYTHON="$HOME/.local/pipx/venvs/virtualenvwrapper/bin/python3.9"
#   source "$HOME/.local/bin/virtualenvwrapper.sh"
# fi
export PYENV_VIRTUALENV_DISABLE_PROMPT=1
# The Python user bin is on PATH via config/env.
_profile python


# go — GOPATH and the go bin dirs are in config/env.
godocserver() { godoc "-http=localhost:6060"; }
_profile go

# rust — CARGO_HOME/RUSTUP_HOME and the cargo bin dir are in config/env, which
# also replaces what rustup's own $CARGO_HOME/env does (it only prepends PATH).

# GNU Arm Embedded Toolchain, used to build joeycastillo/Sensor-Watch, is on
# PATH via config/env.

# java
if exists jenv > /dev/null && [[ -z "$JENV_LOADED" ]]; then
  eval "$(jenv init - --no-rehash)";
  # export JAVA_HOME="${JAVA_HOME:-"$(jenv javahome)"}"
  # export IDEA_JDK=$JAVA_HOME
  # to make sure JAVA_HOME is set, enable this plugin - just need to run once:
  # jenv enable-plugin export
fi
_profile jenv-init

# A gradlew / gradle wrapper.
# https://github.com/gdubw/gng
# This would be nice but it breaks existing IntelliJ projects
# export GRADLE_USER_HOME="$XDG_CACHE_HOME/gradle" # Also contains gradle.properties (symlink from XDG_CONFIG_HOME).
if exists gw > /dev/null; then
  alias gradle=gw
fi

_profile java

# ANDROID_HOME and the colima/testcontainers docker vars are in config/env.

# Interactive setup linked in from elsewhere, one file per concern, in name
# order. This repo ships none: it is where a machine's own layer plugs in, with
# whatever needs a secret or names a host -- a token wrapper, API keys.
# ~/.localprofile, below, is for hand-written per-host setup and still has the
# last word.
#
# A function only to scope nullglob: zsh aborts on a glob matching nothing, and
# bash would hand the loop the literal pattern. The files are sourced inside it,
# so their plain assignments, exports and functions are global, but `local` or
# `typeset` in them would not be -- and under zsh neither would a `setopt`,
# which local_options reverts when the function returns.
__dotfiles_source_dir() {
  if [ -n "${ZSH_VERSION-}" ]; then
    setopt local_options null_glob
  else
    # Not `$(shopt -p nullglob)`: that exits 1 when the option is off, and a
    # caller that sources this under `set -e` would die here without a word.
    local __dotfiles_nullglob=''
    shopt -q nullglob || __dotfiles_nullglob='shopt -u nullglob'
    shopt -s nullglob
  fi
  local __dotfiles_f
  for __dotfiles_f in "$1"/*.sh; do
    # shellcheck source=/dev/null
    . "$__dotfiles_f"
  done
  if [ -z "${ZSH_VERSION-}" ] && [ -n "$__dotfiles_nullglob" ]; then
    eval "$__dotfiles_nullglob"
  fi
}
__dotfiles_source_dir "${XDG_CONFIG_HOME:-$HOME/.config}/shell/profile.d"
unset -f __dotfiles_source_dir
_profile profile.d

macbook_chassis_regulatory_model() {
  ioreg -l \
    | grep '"regulatory-model-number"' \
    | sed -E 's/.*<([^>]+).*/\1/' \
    | cut -c1-10 \
    | xxd -r -p \
    | tr -d '\000'
}
_profile misc

if [[ -r /proc/cpuinfo ]] && \
  grep -qi 'raspberry pi' /proc/cpuinfo
then
  system_temperature() {
    paste \
      <(cat /sys/class/thermal/thermal_zone*/type) \
      <(cat /sys/class/thermal/thermal_zone*/temp | sed 's/\(.\)..$/.\1°C/' ) \
      | column -s $'\t' -t
    if [[ -f /sys/class/thermal/cooling_device0/cur_state ]]
    then
      echo "Fan state: $(cat /sys/class/thermal/cooling_device0/cur_state)"
    else
      echo "No fan"
    fi
  }
fi
_profile raspberry_pi

# The kimi / cursor-agent / claude env vars are all in config/env.

if [ -n "$ZSH_VERSION" ]; then
  [[ -d /opt/homebrew/share/zsh/site-functions/ ]] && fpath+=(/opt/homebrew/share/zsh/site-functions/)
fi
_profile zsh_specific

# Disable XON/XOFF flow control so Ctrl-Q/Ctrl-S work in apps like nvim
stty -ixon 2>/dev/null
# The Ghostty bin dir is on PATH via config/env.
_profile misc

# Interactive host-local setup. Plain host-local *environment* goes in
# ~/.localenv, which config/env sources for every shell.
# shellcheck source=/dev/null
[ -r "$HOME/.localprofile" ] && source "$HOME/.localprofile"
_profile local

# A *login* bash reads this file but never ~/.bashrc, so login and non-login
# interactive bash would otherwise diverge. Source it here to converge them.
# Skipped when ~/.bashrc is what sourced us, and for zsh, which has no such gap
# (a login interactive zsh reads both ~/.zprofile and ~/.zshrc).
if [ -n "${BASH_VERSION:-}" ] && [ -z "${__dotfiles_bashrc:-}" ] && [ -r "$HOME/.bashrc" ]; then
  # shellcheck source=/dev/null
  . "$HOME/.bashrc"
fi
_profile bashrc

_profile exit

# ~/bin is prepended to PATH in config/env.

# vim: foldmethod=marker filetype=sh tw=100