-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path.zshrc
387 lines (343 loc) · 12.5 KB
/
.zshrc
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
#!/usr/bin/env zsh
# shellcheck shell=sh
# .ZSHRC is:
# - sourced by all interactive shells
# - not sourced by scripts
# ENV
if [ -f "${HOME}/.env" ]; then
# https://stackoverflow.com/a/45971167/1923134
set -a; source "${HOME}/.env"; set +a
fi
# GEMPATH
if whence -p gem &>/dev/null; then
GEM_USER_INSTALLATION_DIRECTORY=$(cat "${HOME}/.gem/user_installation_directory" 2>/dev/null)
if [ ! -d "${GEM_USER_INSTALLATION_DIRECTORY}" ]; then
mkdir -p "${HOME}/.gem"
GEM_USER_INSTALLATION_DIRECTORY=$(gem environment | grep "USER INSTALLATION DIRECTORY" | cut -d: -f2 | sed -e 's/^ //' | tee "${HOME}/.gem/user_installation_directory")
mkdir -p "${GEM_USER_INSTALLATION_DIRECTORY}"
fi
fi
# HOMEBREW
# Hardcoding the output of the following, for performance:
# [ -f /opt/homebrew/bin/brew ] && eval "$(/opt/homebrew/bin/brew shellenv)"
export HOMEBREW_PREFIX="/opt/homebrew";
export HOMEBREW_CELLAR="/opt/homebrew/Cellar";
export HOMEBREW_REPOSITORY="/opt/homebrew";
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin${PATH+:$PATH}";
export MANPATH="/opt/homebrew/share/man${MANPATH+:$MANPATH}:";
export INFOPATH="/opt/homebrew/share/info:${INFOPATH:-}";
# PATH
whence -p go &>/dev/null && export GOPATH=$(go env GOPATH)
declare -a PATH_PREPENDA=(
"${HOMEBREW_PREFIX}/var/homebrew/linked/git/share/git-core/contrib/diff-highlight" # Add 'git'’s 'diff-highlight' script (macOS)
"/usr/share/doc/git/contrib/diff-highlight" # Add 'git'’s 'diff-highlight' script (Debian)
"${HOME}/Library/Python/2.7/bin" # Add 'pip --user'-installed package bin
"${GEM_USER_INSTALLATION_DIRECTORY}/bin" # Add 'gem install --user-install'-installed package bin
# Add tools for Chromium development
"${HOME}/Developer/depot_tools"
"${HOME}/Developer/chromium/src/buildtools/mac" # 'clang-format'
"${HOME}/Developer/chromium/src/third_party/llvm-build/Release+Asserts/bin" # 'clang'
"${HOME}/Developer/chromium/src/third_party/ninja" # 'ninja'
"${HOME}/Developer/chromium/src/out/Default/tools/clang/third_party/llvm/build/bin" # 'clangd'
# Add Edge’s tools for Chromium development
"${HOME}/Developer/chromium/chromium.depot_tools.cr-contrib"
"${HOME}/Developer/chromium/chromium.depot_tools.cr-contrib/scripts"
)
declare -a PATH_ADDENDA=(
"${HOMEBREW_PREFIX}/opt/node/bin" # Add brew-installed node, but let npm-installed npm take precedence
"${HOMEBREW_PREFIX}/opt/node@20/bin" # Add brew-installed node, but let npm-installed npm take precedence
)
for p in $PATH_PREPENDA; do
if [ -d "${p}" ] && [[ "${PATH}" != *${p}* ]]; then
PATH="${p}:$PATH"
fi
done
for p in $PATH_ADDENDA; do
if [ -d "${p}" ] && [[ "${PATH}" != *${p}* ]]; then
PATH="$PATH:${p}"
fi
done
unset p
unset GEM_USER_INSTALLATION_DIRECTORY
unset PATH_PREPENDA
unset PATH_ADDENDA
export PATH
# HISTORY
# Increase the maximum number of lines contained in the history file
export HISTSIZE=10000
export HISTFILESIZE=$HISTSIZE
export SAVEHIST=$HISTSIZE
export HISTFILE=~/.zhistory
# Skip repeated commands in history.
setopt HIST_FIND_NO_DUPS
setopt HIST_IGNORE_ALL_DUPS
# Write history immediately and share it between sessions.
setopt INC_APPEND_HISTORY
setopt SHARE_HISTORY
# Share working directory between sessions.
# https://superuser.com/a/328148
if [[ "$TERM_PROGRAM" == "Apple_Terminal" ]] && [[ -z "$INSIDE_EMACS" ]]; then
update_terminal_cwd() {
# Identify the directory using a "file:" scheme URL, including
# the host name to disambiguate local vs. remote paths.
# Percent-encode the pathname.
local url_path=''
{
# Use LC_CTYPE=C to process text byte-by-byte. Ensure that
# LC_ALL isn't set, so it doesn't interfere.
local i ch hexch LC_CTYPE=C LC_ALL=
for ((i = 1; i <= ${#PWD}; ++i)); do
ch="$PWD[i]"
if [[ "$ch" =~ [/._~A-Za-z0-9-] ]]; then
url_path+="$ch"
else
printf -v hexch "%02X" "'$ch"
url_path+="%$hexch"
fi
done
}
printf '\e]7;%s\a' "file://$HOST$url_path"
}
# Register the function so it is called at each prompt.
autoload add-zsh-hook
add-zsh-hook precmd update_terminal_cwd
fi
# Set default editor to Vi
export EDITOR=vi
# PAGING
# Don’t clear screen when using 'less' or 'man'
export LESS=-RXE
export MANPAGER="less"
# COLORS
alias grep='grep --color=auto'
alias ls="command ls -G"
# Use git diff instead of diff
alias diff="command git diff"
# Configure Edge’s tools for Chromium development
export FORCE_MAC_TOOLCHAIN=1
# COMPLETIONS
# Init zsh completions
autoload -Uz compinit
# https://gist.github.com/ctechols/ca1035271ad134841284
if [ ! -f "$HOME/.zcompdump" ] || [ "$(find $HOME/.zcompdump -mtime +1)" ] ; then
echo "Updating zsh completions"
rm -f "$HOME/.zcompdump"
compinit -i
else
compinit -C
fi
# Case-insensitive completion
# https://superuser.com/a/1092328
zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}'
# Menu selection
zstyle ':completion:*' menu yes select
zmodload zsh/complist
# Finish completion and execute with (return)
bindkey -M menuselect '^M' .accept-line
# Cancel selection with (esc)
bindkey -M menuselect '\e' send-break
# INPUT
# Use the string that has already been typed as the prefix for searching
# through commands (i.e. more intelligent Up/Down-arrow behavior)
bindkey "^[[A" history-beginning-search-backward
bindkey "^[OA" history-beginning-search-backward # SSH
bindkey "^[[B" history-beginning-search-forward
bindkey "^[OB" history-beginning-search-forward # SSH
# Reverse through the completions menu using shift-tab
bindkey "^[[Z" reverse-menu-complete
# Move cursor forward/backward by word (⌥→/⌥←)
backward-word-dir () {
local WORDCHARS=${WORDCHARS//[\/\-]}
zle backward-word
}
zle -N backward-word-dir
bindkey "^[b" backward-word-dir
forward-word-dir () {
local WORDCHARS=${WORDCHARS//[\/\-]}
zle forward-word
}
zle -N forward-word-dir
bindkey "^[f" forward-word-dir
# Move cursor to beginning/end of line (⌘←/⌘→)
bindkey "^[[1~" beginning-of-line
bindkey "^A" beginning-of-line # VS Code
bindkey "^[[4~" end-of-line
bindkey "^E" end-of-line # VS Code
# GIT
git() {
command=$1
shift 1
# Make `git push` track an upstream branch, similar to
# `git config --global push.default current`, with added support
# for back-to-back `git switch -c new-branch && git push && git pull`
BRANCH_NAME=$(command git symbolic-ref --quiet --short HEAD 2>/dev/null || \
command git rev-parse --short HEAD 2>/dev/null)
if [[ "${command}" == "push" ]] && \
[ -z "$(command "git" config "branch.${BRANCH_NAME}.merge")" ]
then
command "git" push --set-upstream origin "${BRANCH_NAME}"
unset BRANCH_NAME
return $?
fi
unset BRANCH_NAME
# Remove merged & squash-merged branches
if [[ "${command}" == "branch" ]] && [[ "${1}" == "prune" ]]; then
command "git" branch --merged | grep -E -v "(^\*|main|default|master|develop)" | xargs command "git" branch -D
command "git" fetch -a && command "git" branch -v | grep '\[gone\]' | cut -f3 -d' ' | xargs command "git" branch -D
return $?
fi
# Stash with untracked changes by default
if [[ "${command}" == "stash" ]] && [[ "$@" == "" ]]; then
command "git" stash --include-untracked
return $?
fi
command "git" "${command}" "$@"
}
gitp() {
command=$1
shift 1
if [[ "${command}" == "ush" ]]; then
git push "$@"
fi
if [[ "${command}" == "ull" ]]; then
git pull "$@"
fi
}
alias gti="git"
# KILLPORT
killport() {
port=$1
if [ -z "${port}" ]; then
echo "usage: killport port_number"
return
fi
kill -9 $(lsof -i ":${port}" 2>/dev/null | tail -n +2 | tr -s ' ' | cut -f2 -d' ')
}
# RANDOM
random() {
if [[ "$1" == "mac" ]]; then
# https://superuser.com/a/218650/257969
printf '02:%02X:%02X:%02X:%02X:%02X\n' $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256]
fi
if [[ "$1" == "pin" ]]; then
printf '%03d-%02d-%03d\n' $[RANDOM%1000] $[RANDOM%100] $[RANDOM%1000]
fi
if [[ "$1" == "uuid" ]]; then
printf '%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n' $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] \
$[RANDOM%256] $[RANDOM%256] \
$[RANDOM%256] $[RANDOM%256] \
$[RANDOM%256] $[RANDOM%256] \
$[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256] $[RANDOM%256]
fi
}
# Case-insensitive globbing (used in pathname expansion)
unsetopt CASE_GLOB
# Enable parameter expansion, command substitution and arithmetic expansion in prompt string
setopt PROMPT_SUBST
# PROMPT
# Inspired by https://github.com/necolas/dotfiles/blob/master/shell/bash_prompt
# Print this shell process’ git branch
prompt_git_branch_name() {
if [ -f "/tmp/zsh_prompt_git_branch_name_$$" ]; then
local branchName=$(cat "/tmp/zsh_prompt_git_branch_name_$$")
echo -e "${1}${branchName}"
fi
}
# Update this shell process’ git branch
prompt_git_branch_name_async() {
! command git rev-parse --is-inside-work-tree &>/dev/null && return
local branchName
branchName="$(command git symbolic-ref --quiet --short HEAD 2> /dev/null || \
command git rev-parse --short HEAD 2> /dev/null || \
echo '(unknown)')"
echo "${branchName}" > "/tmp/zsh_prompt_git_branch_name_$$"
kill -s USR1 $$
}
# Re-draw the prompt when git branch name updates are available
TRAPUSR1() {
PROMPT_GIT_BRANCH_NAME_ASYNC_PROC=0
zle && zle reset-prompt
}
# https://www.anishathalye.com/2015/02/07/an-asynchronous-shell-prompt/
# Print this shell process’ git status
prompt_git_status() {
if [ -f "/tmp/zsh_prompt_git_status_$$" ]; then
local s=$(cat "/tmp/zsh_prompt_git_status_$$")
[ -n "${s}" ] && s=" [${s}]"
echo -e "${1}${s}"
fi
}
# Update this shell process’ git status
prompt_git_status_async() {
! command git rev-parse --is-inside-work-tree &>/dev/null && return
local s='';
if [[ "$(command git rev-parse --is-inside-git-dir 2> /dev/null)" == "false" ]]; then
command git update-index --really-refresh -q &>/dev/null;
# [+] staged, uncommitted
! command git diff --quiet --ignore-submodules --cached && s+='+'
# [!] unstaged
! command git diff-files --quiet --ignore-submodules -- && s+='!'
# [?] untracked - this is the most expensive
[ -n "$(command git ls-files --others --exclude-standard)" ] && s+='?'
# [$] stashed
command git rev-parse --verify refs/stash &>/dev/null && s+='$'
fi
echo "${s}" > "/tmp/zsh_prompt_git_status_$$"
kill -s USR2 $$
}
# Re-draw the prompt when git status updates are available
TRAPUSR2() {
PROMPT_GIT_STATUS_ASYNC_PROC=0
zle && zle reset-prompt
}
set_prompts() {
if [[ -n "${SSH_CLIENT}" ]]; then
PROMPT="%4F%n%8F on %2F%m"
else
PROMPT="%4F%n"
fi
PROMPT+="%8F in ";
PROMPT+="%2F%(5~|%-1~/.../%3~|%4~)"; # working directory, https://unix.stackexchange.com/a/273567
PROMPT+='$(prompt_git_branch_name "%8F on %5F")'; # git branch name
PROMPT+='$(prompt_git_status "%8F")'; # git status
PROMPT+=$'\n';
PROMPT+="%f\$ "; # `$` (and reset color)
export PROMPT;
}
set_prompts
unset set_prompts
PPWD="$PWD" # Remember previous working directory
PSHELL_SESSION_FILE="$SHELL_SESSION_FILE" # Remember previous shell session file
precmd() {
if [ -z "${PROMPT_CTR}" ]; then
PROMPT_CTR=1
elif [ "${PROMPT_CTR}" -eq 1 ]; then
echo ""
# osascript -e 'if app "Terminal" is frontmost then tell app "System Events" to keystroke "u" using command down'
fi
# Clear shell process’ git branch name when working directory changes
# Clear shell process’ git status when working directory changes
if [[ "${PPWD}" != "${PWD}" ]]; then
rm -f "/tmp/zsh_prompt_git_branch_name_$$"
rm -f "/tmp/zsh_prompt_git_status_$$"
fi
# Clear shell session file so disowned background process
# doesn’t output “Saving session...completed.”
SHELL_SESSION_FILE=""
# Kill unfinished git branch name update requests
if [[ "${PROMPT_GIT_BRANCH_NAME_ASYNC_PROC}" != 0 ]]; then
kill -s HUP $PROMPT_GIT_BRANCH_NAME_ASYNC_PROC &>/dev/null || :
fi
# Request updated git branch name in the background
prompt_git_branch_name_async &!
PROMPT_GIT_BRANCH_NAME_ASYNC_PROC=$!
# Kill unfinished git status update requests
if [[ "${PROMPT_GIT_STATUS_ASYNC_PROC}" != 0 ]]; then
kill -s HUP $PROMPT_GIT_STATUS_ASYNC_PROC &>/dev/null || :
fi
# Request updated git status in the background
prompt_git_status_async &!
PROMPT_GIT_STATUS_ASYNC_PROC=$!
PPWD="$PWD" # Update previous working directory
SHELL_SESSION_FILE="$PSHELL_SESSION_FILE" # Update previous shell session file
}