mirror of
https://github.com/Tarrasch/zsh-autoenv.git
synced 2024-11-05 01:10:59 +02:00
409 lines
15 KiB
Plaintext
409 lines
15 KiB
Plaintext
|
################################################################################
|
||
|
# Stash/unstash support for per-directory variables
|
||
|
#
|
||
|
# Copyright (c) 2009,2012 Dave Olszewski <cxreg@pobox.com>
|
||
|
# http://github.com/cxreg/smartcd
|
||
|
#
|
||
|
# This code is released under GPL v2 and the Artistic License, and
|
||
|
# may be redistributed under the terms of either.
|
||
|
#
|
||
|
#
|
||
|
# This library allows you to save the current value of a given environment
|
||
|
# variable in a temporary location, so that you can modify it, and then
|
||
|
# later restore its original value.
|
||
|
#
|
||
|
# Note that you will need to be in the same directory you were in when you
|
||
|
# stashed in order to successfully unstash. This is because the temporary
|
||
|
# variable is derived from your current working directory's path.
|
||
|
#
|
||
|
# Usage:
|
||
|
# stash PATH
|
||
|
# export PATH=/something/else
|
||
|
# [...]
|
||
|
# unstash PATH
|
||
|
#
|
||
|
# Note that this was written for use with, and works very well with,
|
||
|
# smartcd. See the documentation there for examples.
|
||
|
#
|
||
|
# An alternate usage is `autostash' which will trigger autounstash when
|
||
|
# leaving the directory, if combined with smartcd. This reduces the amount
|
||
|
# of explicit configuration you need to provide:
|
||
|
#
|
||
|
# autostash PATH
|
||
|
# export PATH=/something/else
|
||
|
#
|
||
|
# You may also do both operations on line line, leaving only the very succinct
|
||
|
#
|
||
|
# autostash PATH=/something/else
|
||
|
#
|
||
|
# If you run stash, unstash, or varstash interactively, they will instruct
|
||
|
# you on how to create smartcd scripts for performing those actions
|
||
|
# automatically. You can affect this behavior with several variables:
|
||
|
#
|
||
|
# VARSTASH_QUIET - Set if you'd rather not see these notices
|
||
|
#
|
||
|
# VARSTASH_AUTOCONFIG - Set if you want the suggested actions to be
|
||
|
# performed automatically
|
||
|
#
|
||
|
# VARSTASH_AUTOEDIT - Set if you want it to set the values, but also
|
||
|
# give you an opportunity to edit the file
|
||
|
#
|
||
|
# If you attempt to stash the same value twice, a warning will be displayed
|
||
|
# and the second stash will not occur. To make it happen anyway, pass -f
|
||
|
# as the first argument to stash.
|
||
|
#
|
||
|
# $ stash FOO
|
||
|
# $ stash FOO
|
||
|
# You have already stashed FOO, please specify "-f" if you want to overwrite another stashed value
|
||
|
# $ stash -f FOO
|
||
|
# $
|
||
|
#
|
||
|
# This rule is a bit different if you are assigning a value and the variable
|
||
|
# has already been stashed. In that case, the new value will be assigned, but
|
||
|
# the stash will not be overwritten. This allows for non-conflicting chained
|
||
|
# stash-assign rules.
|
||
|
#
|
||
|
################################################################################
|
||
|
|
||
|
function stash() {
|
||
|
if [[ $1 == "-f" ]]; then
|
||
|
local force=1; shift
|
||
|
fi
|
||
|
|
||
|
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]] && [[ -z $run_from_autostash ]]; then
|
||
|
local working_dir="${varstash_dir:-$(pwd)}"
|
||
|
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
|
||
|
local help_action="stashing a variable"
|
||
|
local help_dir=$smartcd_dir
|
||
|
local help_cmd="echo stash $@ >> \"$smartcd_dir/bash_enter\""
|
||
|
local help_which="bash_enter"
|
||
|
_manual_stash_help
|
||
|
fi
|
||
|
|
||
|
|
||
|
while [[ -n $1 ]]; do
|
||
|
if [[ $1 == "alias" && $2 =~ "=" ]]; then
|
||
|
shift
|
||
|
local _stashing_alias_assign=1
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
local stash_expression=$1
|
||
|
local stash_which=${stash_expression%%'='*}
|
||
|
local stash_name=$(_mangle_var $stash_which)
|
||
|
|
||
|
# Extract the value and make it double-quote safe
|
||
|
local stash_value=${stash_expression#*'='}
|
||
|
stash_value=${stash_value//\\/\\\\}
|
||
|
stash_value=${stash_value//\"/\\\"}
|
||
|
stash_value=${stash_value//\`/\\\`}
|
||
|
stash_value=${stash_value//\$/\\\$}
|
||
|
|
||
|
if [[ ( -n "$(eval echo '$__varstash_alias__'$stash_name)" ||
|
||
|
-n "$(eval echo '$__varstash_function__'$stash_name)" ||
|
||
|
-n "$(eval echo '$__varstash_array__'$stash_name)" ||
|
||
|
-n "$(eval echo '$__varstash_export__'$stash_name)" ||
|
||
|
-n "$(eval echo '$__varstash_variable__'$stash_name)" ||
|
||
|
-n "$(eval echo '$__varstash_nostash__'$stash_name)" )
|
||
|
&& -z $force ]]; then
|
||
|
|
||
|
if [[ -z $already_stashed && ${already_stashed-_} == "_" ]]; then
|
||
|
local already_stashed=1
|
||
|
else
|
||
|
already_stashed=1
|
||
|
fi
|
||
|
|
||
|
if [[ $stash_which == $stash_expression ]]; then
|
||
|
if [[ -z $run_from_smartcd ]]; then
|
||
|
echo "You have already stashed $stash_which, please specify \"-f\" if you want to overwrite another stashed value"
|
||
|
fi
|
||
|
|
||
|
# Skip remaining work if we're not doing an assignment
|
||
|
shift
|
||
|
continue
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# Handle any alias that may exist under this name
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
local alias_def="$(eval alias $stash_which 2>/dev/null)"
|
||
|
if [[ -n $alias_def ]]; then
|
||
|
alias_def=${alias_def#alias }
|
||
|
eval "__varstash_alias__$stash_name=\"$alias_def\""
|
||
|
local stashed=1
|
||
|
fi
|
||
|
fi
|
||
|
if [[ $stash_which != $stash_expression && -n $_stashing_alias_assign ]]; then
|
||
|
eval "alias $stash_which=\"$stash_value\""
|
||
|
fi
|
||
|
|
||
|
# Handle any function that may exist under this name
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
local function_def="$(declare -f $stash_which)"
|
||
|
if [[ -n $function_def ]]; then
|
||
|
# make function definition quote-safe. because we are going to evaluate the
|
||
|
# source with "echo -e", we need to double-escape the backslashes (so 1 -> 4)
|
||
|
function_def=${function_def//\\/\\\\\\\\}
|
||
|
function_def=${function_def//\"/\\\"}
|
||
|
function_def=${function_def//\`/\\\`}
|
||
|
function_def=${function_def//\$/\\\$}
|
||
|
eval "__varstash_function__$stash_name=\"$function_def\""
|
||
|
local stashed=1
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
# Handle any variable that may exist under this name
|
||
|
local vartype="$(declare -p $stash_which 2>/dev/null)"
|
||
|
if [[ -n $vartype ]]; then
|
||
|
if [[ -n $ZSH_VERSION ]]; then
|
||
|
local pattern="^typeset"
|
||
|
else
|
||
|
local pattern="^declare"
|
||
|
fi
|
||
|
if [[ $vartype =~ $pattern" -a" ]]; then
|
||
|
# varible is an array
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
eval "__varstash_array__$stash_name=(\"\${$stash_which""[@]}\")"
|
||
|
fi
|
||
|
|
||
|
elif [[ $vartype =~ $pattern" -x" ]]; then
|
||
|
# variable is exported
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
eval "__varstash_export__$stash_name=\"\$$stash_which\""
|
||
|
fi
|
||
|
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
|
||
|
eval "export $stash_which=\"$stash_value\""
|
||
|
fi
|
||
|
else
|
||
|
# regular variable
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
eval "__varstash_variable__$stash_name=\"\$$stash_which\""
|
||
|
fi
|
||
|
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
|
||
|
eval "$stash_which=\"$stash_value\""
|
||
|
fi
|
||
|
|
||
|
fi
|
||
|
local stashed=1
|
||
|
fi
|
||
|
|
||
|
if [[ -z $stashed ]]; then
|
||
|
# Nothing in the variable we're stashing, but make a note that we stashed so we
|
||
|
# do the right thing when unstashing. Without this, we take no action on unstash
|
||
|
|
||
|
# Zsh bug sometimes caues
|
||
|
# (eval):1: command not found: __varstash_nostash___tmp__home_dolszewski_src_smartcd_RANDOM_VARIABLE=1
|
||
|
# fixed in zsh commit 724fd07a67f, version 4.3.14
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
eval "__varstash_nostash__$stash_name=1"
|
||
|
fi
|
||
|
|
||
|
# In the case of a previously unset variable that we're assigning too, export it
|
||
|
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
|
||
|
eval "export $stash_which=\"$stash_value\""
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
shift
|
||
|
unset -v _stashing_alias_assign
|
||
|
done
|
||
|
}
|
||
|
|
||
|
function autostash() {
|
||
|
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]]; then
|
||
|
local working_dir="${varstash_dir:-$(pwd)}"
|
||
|
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
|
||
|
local help_action="autostashing a variable"
|
||
|
local help_dir=$smartcd_dir
|
||
|
local help_cmd="echo autostash $@ >> \"$smartcd_dir/bash_enter\""
|
||
|
local help_which="bash_enter"
|
||
|
_manual_stash_help
|
||
|
fi
|
||
|
|
||
|
local run_from_autostash=1
|
||
|
while [[ -n $1 ]]; do
|
||
|
if [[ $1 == "alias" && $2 =~ "=" ]]; then
|
||
|
shift
|
||
|
local _stashing_alias_assign=1
|
||
|
fi
|
||
|
|
||
|
local already_stashed=
|
||
|
stash "$1"
|
||
|
if [[ -z $already_stashed ]]; then
|
||
|
local autostash_name=$(_mangle_var AUTOSTASH)
|
||
|
local varname=${1%%'='*}
|
||
|
apush $autostash_name "$varname"
|
||
|
fi
|
||
|
shift
|
||
|
unset -v _stashing_alias_assign
|
||
|
done
|
||
|
}
|
||
|
|
||
|
function unstash() {
|
||
|
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]] && [[ -z $run_from_autounstash ]]; then
|
||
|
local working_dir=${varstash_dir:-$(pwd)}
|
||
|
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
|
||
|
local help_action="unstashing a variable"
|
||
|
local help_dir=$smartcd_dir
|
||
|
local help_cmd="echo unstash $@ >> \"$smartcd_dir/bash_leave\""
|
||
|
local help_which="bash_leave"
|
||
|
_manual_stash_help
|
||
|
fi
|
||
|
|
||
|
while [[ -n $1 ]]; do
|
||
|
local unstash_which=$1
|
||
|
if [[ -z $unstash_which ]]; then
|
||
|
continue
|
||
|
fi
|
||
|
|
||
|
local unstash_name=$(_mangle_var $unstash_which)
|
||
|
|
||
|
# This bit is a little tricky. Here are the rules:
|
||
|
# 1) unstash any alias, function, or variable which matches
|
||
|
# 2) if one or more matches, but not all, delete any that did not
|
||
|
# 3) if none match but nostash is found, delete all
|
||
|
# 4) if none match and nostash not found, do nothing
|
||
|
|
||
|
# Unstash any alias
|
||
|
if [[ -n "$(eval echo \$__varstash_alias__$unstash_name)" ]]; then
|
||
|
eval "alias $(eval echo \$__varstash_alias__$unstash_name)"
|
||
|
unset __varstash_alias__$unstash_name
|
||
|
local unstashed=1
|
||
|
local unstashed_alias=1
|
||
|
fi
|
||
|
|
||
|
# Unstash any function
|
||
|
if [[ -n "$(eval echo \$__varstash_function__$unstash_name)" ]]; then
|
||
|
eval "function $(eval echo -e \"\$__varstash_function__$unstash_name\")"
|
||
|
unset __varstash_function__$unstash_name
|
||
|
local unstashed=1
|
||
|
local unstashed_function=1
|
||
|
fi
|
||
|
|
||
|
# Unstash any variable
|
||
|
if [[ -n "$(declare -p __varstash_array__$unstash_name 2>/dev/null)" ]]; then
|
||
|
eval "$unstash_which=(\"\${__varstash_array__$unstash_name""[@]}\")"
|
||
|
unset __varstash_array__$unstash_name
|
||
|
local unstashed=1
|
||
|
local unstashed_variable=1
|
||
|
elif [[ -n "$(declare -p __varstash_export__$unstash_name 2>/dev/null)" ]]; then
|
||
|
eval "export $unstash_which=\"\$__varstash_export__$unstash_name\""
|
||
|
unset __varstash_export__$unstash_name
|
||
|
local unstashed=1
|
||
|
local unstashed_variable=1
|
||
|
elif [[ -n "$(declare -p __varstash_variable__$unstash_name 2>/dev/null)" ]]; then
|
||
|
# Unset variable first to reset export
|
||
|
unset -v $unstash_which
|
||
|
eval "$unstash_which=\"\$__varstash_variable__$unstash_name\""
|
||
|
unset __varstash_variable__$unstash_name
|
||
|
local unstashed=1
|
||
|
local unstashed_variable=1
|
||
|
fi
|
||
|
|
||
|
# Unset any values which did not exist at time of stash
|
||
|
local nostash="$(eval echo \$__varstash_nostash__$unstash_name)"
|
||
|
unset __varstash_nostash__$unstash_name
|
||
|
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_alias" ) ]]; then
|
||
|
unalias $unstash_which 2>/dev/null
|
||
|
fi
|
||
|
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_function" ) ]]; then
|
||
|
unset -f $unstash_which 2>/dev/null
|
||
|
fi
|
||
|
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_variable" ) ]]; then
|
||
|
# Don't try to unset illegal variable names
|
||
|
if ! [[ $unstash_which =~ [^a-zA-Z0-9_] || $unstash_which =~ ^[0-9] ]]; then
|
||
|
unset -v $unstash_which
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
shift
|
||
|
done
|
||
|
}
|
||
|
|
||
|
function autounstash() {
|
||
|
# If there is anything in (mangled) variable AUTOSTASH, then unstash it
|
||
|
local autounstash_name=$(_mangle_var AUTOSTASH)
|
||
|
if (( $(alen $autounstash_name) > 0 )); then
|
||
|
local run_from_autounstash=1
|
||
|
while (( $(alen $autounstash_name) > 0 )); do
|
||
|
local autounstash_var=$(afirst $autounstash_name)
|
||
|
ashift $autounstash_name >/dev/null
|
||
|
unstash $autounstash_var
|
||
|
done
|
||
|
unset $autounstash_name
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
function _mangle_var() {
|
||
|
local mangle_var_where="${varstash_dir:-$(pwd)}"
|
||
|
mangle_var_where=${mangle_var_where//[^A-Za-z0-9]/_}
|
||
|
local mangled_name=${1//[^A-Za-z0-9]/_}
|
||
|
echo "_tmp_${mangle_var_where}_${mangled_name}"
|
||
|
}
|
||
|
|
||
|
function _manual_stash_help() {
|
||
|
# instruct user how to create bash_enter or bash_leave
|
||
|
if [[ -n $VARSTASH_AUTOEDIT || -n $VARSTASH_AUTOCONFIG ]]; then
|
||
|
if [[ -z $VARSTASH_QUIET ]]; then
|
||
|
echo "varstash: Automatically running $help_cmd"
|
||
|
fi
|
||
|
|
||
|
if [[ ! -d $help_dir ]]; then
|
||
|
mkdir -p "$help_dir"
|
||
|
fi
|
||
|
eval $help_cmd
|
||
|
|
||
|
if [[ -n $VARSTASH_AUTOEDIT ]]; then
|
||
|
varstash_edit $help_which
|
||
|
fi
|
||
|
elif [[ -z $VARSTASH_QUIET ]]; then
|
||
|
echo "############################################################################"
|
||
|
echo "# You are manually $help_action. To automatically perform this"
|
||
|
echo "# whenever you enter this directory, paste the following command(s):"
|
||
|
|
||
|
if [[ ! -d $help_dir ]]; then
|
||
|
echo "mkdir -p \"$help_dir\""
|
||
|
fi
|
||
|
echo "$help_cmd"
|
||
|
echo "############################################################################"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# A couple convenient aliases for smartcd_edit
|
||
|
function autostash_edit() {
|
||
|
varstash_edit "$@"
|
||
|
}
|
||
|
|
||
|
function varstash_edit() {
|
||
|
local file="$1"
|
||
|
local dir="$2"
|
||
|
|
||
|
if [[ -n $ZSH_VERSION ]]; then
|
||
|
if [[ $(type smartcd_edit) == "smartcd_edit is a shell function" ]]; then
|
||
|
local can_run=1
|
||
|
fi
|
||
|
else
|
||
|
if [[ $(type -t smartcd_edit) == "function" ]]; then
|
||
|
local can_run=1
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
if [[ -n "$can_run" ]]; then
|
||
|
# XXX - no support for "--host" or "--system" with this (yet?)
|
||
|
_smartcd_file_check "$file" "global" "" edit "$dir"
|
||
|
else
|
||
|
echo "smartcd not loaded, cannot run smartcd_edit"
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# Run deferred smartcd if we're waiting for it, and arrays is also loaded
|
||
|
if [[ -n "$smartcd_initially_deferred" && -n "$(fn_exists apush)" && -z "$SMARTCD_NOINITIAL" ]]; then
|
||
|
smartcd_skip_action=1
|
||
|
smartcd_run_mainline=1
|
||
|
smartcd cd
|
||
|
unset smartcd_skip_action
|
||
|
unset smartcd_initially_deferred
|
||
|
fi
|
||
|
|
||
|
# vim: filetype=sh autoindent expandtab shiftwidth=4 softtabstop=4
|