]> Sergey Matveev's repositories - zsh-autoenv.git/commitdiff
Add varstash from smartcd
authorDaniel Hahler <git@thequod.de>
Sat, 15 Nov 2014 15:24:26 +0000 (16:24 +0100)
committerDaniel Hahler <git@thequod.de>
Mon, 24 Nov 2014 19:13:18 +0000 (20:13 +0100)
Source: https://github.com/cxreg/smartcd/blob/4fef3d3610873d9e8044cb5be1922fb056f4228d/lib/core/varstash

lib/varstash [new file with mode: 0644]

diff --git a/lib/varstash b/lib/varstash
new file mode 100644 (file)
index 0000000..c2e26c2
--- /dev/null
@@ -0,0 +1,408 @@
+################################################################################
+# 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