Speed up shell startup with evalcache
This commit is contained in:
parent
cf01289beb
commit
9ef8e38aec
3 changed files with 60 additions and 166 deletions
50
zsh/functions/_evalcache
Executable file
50
zsh/functions/_evalcache
Executable file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Caches the output of a binary initialization command, to avoid the time to
|
||||||
|
# execute it in the future.
|
||||||
|
#
|
||||||
|
# Usage: _evalcache [NAME=VALUE]... COMMAND [ARG]...
|
||||||
|
|
||||||
|
# default cache directory
|
||||||
|
export ZSH_EVALCACHE_DIR=${ZSH_EVALCACHE_DIR:-"$HOME/.zsh-evalcache"}
|
||||||
|
|
||||||
|
function _evalcache () {
|
||||||
|
local cmdHash="nohash" data="$*" name
|
||||||
|
|
||||||
|
# use the first non-variable argument as the name
|
||||||
|
for name in $@; do
|
||||||
|
if [ "${name}" = "${name#[A-Za-z_][A-Za-z0-9_]*=}" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# if command is a function, include its definition in data
|
||||||
|
if typeset -f "${name}" > /dev/null; then
|
||||||
|
data=${data}$(typeset -f "${name}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if builtin command -v md5 > /dev/null; then
|
||||||
|
cmdHash=$(echo -n "${data}" | md5)
|
||||||
|
elif builtin command -v md5sum > /dev/null; then
|
||||||
|
cmdHash=$(echo -n "${data}" | md5sum | cut -d' ' -f1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
local cacheFile="$ZSH_EVALCACHE_DIR/init-${name##*/}-${cmdHash}.sh"
|
||||||
|
|
||||||
|
if [ "$ZSH_EVALCACHE_DISABLE" = "true" ]; then
|
||||||
|
eval ${(q)@}
|
||||||
|
elif [ -s "$cacheFile" ]; then
|
||||||
|
source "$cacheFile"
|
||||||
|
else
|
||||||
|
if type "${name}" > /dev/null; then
|
||||||
|
echo "evalcache: ${name} initialization not cached, caching output of: $*" >&2
|
||||||
|
mkdir -p "$ZSH_EVALCACHE_DIR"
|
||||||
|
eval ${(q)@} > "$cacheFile"
|
||||||
|
source "$cacheFile"
|
||||||
|
else
|
||||||
|
echo "evalcache: ERROR: ${name} is not installed or in PATH" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function _evalcache_clear () {
|
||||||
|
rm -i "$ZSH_EVALCACHE_DIR"/init-*.sh
|
||||||
|
}
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
#compdef hub
|
|
||||||
|
|
||||||
# Zsh will source this file when attempting to autoload the "_hub" function,
|
|
||||||
# typically on the first attempt to complete the hub command. We define two new
|
|
||||||
# setup helper routines (one for the zsh-distributed version, one for the
|
|
||||||
# git-distributed, bash-based version). Then we redefine the "_hub" function to
|
|
||||||
# call "_git" after some other interception.
|
|
||||||
#
|
|
||||||
# This is pretty fragile, if you think about it. Any number of implementation
|
|
||||||
# changes in the "_git" scripts could cause problems down the road. It would be
|
|
||||||
# better if the stock git completions were just a bit more permissive about how
|
|
||||||
# it allowed third-party commands to be added.
|
|
||||||
|
|
||||||
(( $+functions[__hub_setup_zsh_fns] )) ||
|
|
||||||
__hub_setup_zsh_fns () {
|
|
||||||
(( $+functions[_git-alias] )) ||
|
|
||||||
_git-alias () {
|
|
||||||
_arguments \
|
|
||||||
'-s[output shell script suitable for eval]' \
|
|
||||||
'1::shell:(zsh bash csh)'
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_git-browse] )) ||
|
|
||||||
_git-browse () {
|
|
||||||
_arguments \
|
|
||||||
'-u[output the URL]' \
|
|
||||||
'2::subpage:(wiki commits issues)'
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_git-compare] )) ||
|
|
||||||
_git-compare () {
|
|
||||||
_arguments \
|
|
||||||
'-u[output the URL]' \
|
|
||||||
':[start...]end range:'
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_git-create] )) ||
|
|
||||||
_git-create () {
|
|
||||||
_arguments \
|
|
||||||
'::name (REPOSITORY or ORGANIZATION/REPOSITORY):' \
|
|
||||||
'-p[make repository private]' \
|
|
||||||
'-d[description]:description' \
|
|
||||||
'-h[home page]:repository home page URL:_urls'
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_git-fork] )) ||
|
|
||||||
_git-fork () {
|
|
||||||
_arguments \
|
|
||||||
'--no-remote[do not add a remote for the new fork]'
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[_git-pull-request] )) ||
|
|
||||||
_git-pull-request () {
|
|
||||||
_arguments \
|
|
||||||
'-f[force (skip check for local commits)]' \
|
|
||||||
'-b[base]:base ("branch", "owner\:branch", "owner/repo\:branch"):' \
|
|
||||||
'-h[head]:head ("branch", "owner\:branch", "owner/repo\:branch"):' \
|
|
||||||
- set1 \
|
|
||||||
'-m[message]' \
|
|
||||||
'-F[file]' \
|
|
||||||
- set2 \
|
|
||||||
'-i[issue]:issue number:' \
|
|
||||||
- set3 \
|
|
||||||
'::issue-url:_urls'
|
|
||||||
}
|
|
||||||
|
|
||||||
# stash the "real" command for later
|
|
||||||
functions[_hub_orig_git_commands]=$functions[_git_commands]
|
|
||||||
|
|
||||||
# Replace it with our own wrapper.
|
|
||||||
declare -f _git_commands >& /dev/null && unfunction _git_commands
|
|
||||||
_git_commands () {
|
|
||||||
local ret=1
|
|
||||||
# call the original routine
|
|
||||||
_call_function ret _hub_orig_git_commands
|
|
||||||
|
|
||||||
# Effectively "append" our hub commands to the behavior of the original
|
|
||||||
# _git_commands function. Using this wrapper function approach ensures
|
|
||||||
# that we only offer the user the hub subcommands when the user is
|
|
||||||
# actually trying to complete subcommands.
|
|
||||||
hub_commands=(
|
|
||||||
alias:'show shell instructions for wrapping git'
|
|
||||||
pull-request:'open a pull request on GitHub'
|
|
||||||
fork:'fork origin repo on GitHub'
|
|
||||||
create:'create new repo on GitHub for the current project'
|
|
||||||
browse:'browse the project on GitHub'
|
|
||||||
compare:'open GitHub compare view'
|
|
||||||
ci-status:'lookup commit in GitHub Status API'
|
|
||||||
)
|
|
||||||
_describe -t hub-commands 'hub command' hub_commands && ret=0
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(( $+functions[__hub_setup_bash_fns] )) ||
|
|
||||||
__hub_setup_bash_fns () {
|
|
||||||
# TODO more bash-style fns needed here to complete subcommand args. They take
|
|
||||||
# the form "_git_CMD" where "CMD" is something like "pull-request".
|
|
||||||
|
|
||||||
# Duplicate and rename the 'list_all_commands' function
|
|
||||||
eval "$(declare -f __git_list_all_commands | \
|
|
||||||
sed 's/__git_list_all_commands/__git_list_all_commands_without_hub/')"
|
|
||||||
|
|
||||||
# Wrap the 'list_all_commands' function with extra hub commands
|
|
||||||
__git_list_all_commands() {
|
|
||||||
cat <<-EOF
|
|
||||||
alias
|
|
||||||
pull-request
|
|
||||||
fork
|
|
||||||
create
|
|
||||||
browse
|
|
||||||
compare
|
|
||||||
ci-status
|
|
||||||
EOF
|
|
||||||
__git_list_all_commands_without_hub
|
|
||||||
}
|
|
||||||
|
|
||||||
# Ensure cached commands are cleared
|
|
||||||
__git_all_commands=""
|
|
||||||
}
|
|
||||||
|
|
||||||
# redefine _hub to a much smaller function in the steady state
|
|
||||||
_hub () {
|
|
||||||
# only attempt to intercept the normal "_git" helper functions once
|
|
||||||
(( $+__hub_func_replacement_done )) ||
|
|
||||||
() {
|
|
||||||
# At this stage in the shell's execution the "_git" function has not yet
|
|
||||||
# been autoloaded, so the "_git_commands" or "__git_list_all_commands"
|
|
||||||
# functions will not be defined. Call it now (with a bogus no-op service
|
|
||||||
# to prevent premature completion) so that we can wrap them.
|
|
||||||
if declare -f _git >& /dev/null ; then
|
|
||||||
_hub_noop () { __hub_zsh_provided=1 } # zsh-provided will call this one
|
|
||||||
__hub_noop_main () { __hub_git_provided=1 } # git-provided will call this one
|
|
||||||
local service=hub_noop
|
|
||||||
_git
|
|
||||||
unfunction _hub_noop
|
|
||||||
unfunction __hub_noop_main
|
|
||||||
service=git
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (( $__hub_zsh_provided )) ; then
|
|
||||||
__hub_setup_zsh_fns
|
|
||||||
elif (( $__hub_git_provided )) ; then
|
|
||||||
__hub_setup_bash_fns
|
|
||||||
fi
|
|
||||||
|
|
||||||
__hub_func_replacement_done=1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now perform the actual completion, allowing the "_git" function to call our
|
|
||||||
# replacement "_git_commands" function as needed. Both versions expect
|
|
||||||
# service=git or they will call nonexistent routines or end up in an infinite
|
|
||||||
# loop.
|
|
||||||
service=git
|
|
||||||
declare -f _git >& /dev/null && _git
|
|
||||||
}
|
|
||||||
|
|
||||||
# make sure we actually attempt to complete on the first "tab" from the user
|
|
||||||
_hub
|
|
||||||
16
zshrc
16
zshrc
|
|
@ -13,6 +13,13 @@ function is_interactive() { [ -t 1 ] }
|
||||||
|
|
||||||
[[ -d "$HOME/config/zsh" ]] && ZDOTDIR="$HOME/config/zsh"
|
[[ -d "$HOME/config/zsh" ]] && ZDOTDIR="$HOME/config/zsh"
|
||||||
|
|
||||||
|
fpath=($fpath $ZDOTDIR/functions $ZDOTDIR/completions)
|
||||||
|
typeset -U fpath
|
||||||
|
|
||||||
|
# Cache the output of commands that take a long time to run even
|
||||||
|
# though they almost always return the same output.
|
||||||
|
autoload _evalcache
|
||||||
|
|
||||||
if [[ -r "$ZDOTDIR/zlocal" ]]; then
|
if [[ -r "$ZDOTDIR/zlocal" ]]; then
|
||||||
source "$ZDOTDIR/zlocal"
|
source "$ZDOTDIR/zlocal"
|
||||||
fi
|
fi
|
||||||
|
|
@ -22,13 +29,13 @@ fi
|
||||||
# Do this before setting up PATH so ~/bin and similar still have the highest precedence.
|
# Do this before setting up PATH so ~/bin and similar still have the highest precedence.
|
||||||
|
|
||||||
if command_exists rbenv; then
|
if command_exists rbenv; then
|
||||||
eval "$(rbenv init -)"
|
_evalcache rbenv init -
|
||||||
fi
|
fi
|
||||||
if command_exists pyenv; then
|
if command_exists pyenv; then
|
||||||
eval "$(pyenv init -)"
|
_evalcache pyenv init -
|
||||||
fi
|
fi
|
||||||
if command_exists direnv; then
|
if command_exists direnv; then
|
||||||
eval "$(direnv hook zsh)"
|
_evalcache direnv hook zsh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
### SSH keys
|
### SSH keys
|
||||||
|
|
@ -58,9 +65,6 @@ typeset -U path
|
||||||
# remove / from word chars
|
# remove / from word chars
|
||||||
export WORDCHARS='*?_-.[]~=&;!#$%^(){}<>'
|
export WORDCHARS='*?_-.[]~=&;!#$%^(){}<>'
|
||||||
|
|
||||||
fpath=($fpath $ZDOTDIR/functions $ZDOTDIR/completions)
|
|
||||||
typeset -U fpath
|
|
||||||
|
|
||||||
cdpath=(~)
|
cdpath=(~)
|
||||||
|
|
||||||
HOSTNAME=`hostname -s`
|
HOSTNAME=`hostname -s`
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue