From b85ae9b9238f93a26c68144a615870ee1041274f Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" Date: Wed, 25 Apr 2018 20:04:00 +0100 Subject: [PATCH] Add autoenv command to manage autoenv's usage: autoenv [-h] {init,edit,deinit} options: -h, --help show this help message and exit commands: init add .enter and .exit scripts in current directory edit edit .enter and .exit scripts in current directory deinit remove .enter and .exit scripts in current directory Fixes #9. --- autoenv/_autoenv | 24 +++++ autoenv/autoenv.plugin.zsh | 112 +---------------------- autoenv/autoenv.zsh | 179 +++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 108 deletions(-) create mode 100644 autoenv/_autoenv create mode 100644 autoenv/autoenv.zsh diff --git a/autoenv/_autoenv b/autoenv/_autoenv new file mode 100644 index 0000000..9e3ce83 --- /dev/null +++ b/autoenv/_autoenv @@ -0,0 +1,24 @@ +#compdef autoenv + +# Completion for the autoenv command. +_autoenv() { + local ret=1 context curcontext="$curcontext" state line + typeset -A opt_args + + _arguments -C -w -s \ + '(-h --help)'{-h,--help}'[show this help message and exit]' \ + '1: :->command' + + case $state in + (command) + declare -a commands + local commands=( + init:'add .enter and .exit scripts in current directory' + edit:'edit .enter and .exit scripts in current directory' + deinit:'remove .enter and .exit scripts in current directory' + ) + _describe -t commands command commands && ret=0 ;; + esac + + return ret +} diff --git a/autoenv/autoenv.plugin.zsh b/autoenv/autoenv.plugin.zsh index d048e31..29500fe 100644 --- a/autoenv/autoenv.plugin.zsh +++ b/autoenv/autoenv.plugin.zsh @@ -1,108 +1,4 @@ -# Automatically update the environment when the current working directory -# changes, this is a reimplementation of the ideas found in the repository -# https://github.com/Tarrasch/zsh-autoenv stripped down to bare essentials. -# -# The secret sauce can be found at the bottom of this file, where the `chpwd` -# hook function `_autoenv_chpwd` is added. - -# Global entered directories array. -_autoenv_entered=() - -# Check if the given file is authorized, if not prompt the user to authorize, -# ignore, or view the file. Authorized files and their modified times are -# stored in the ~/.cache/autoenv/authorized file to make authorization -# persistent. -_autoenv_authorized() { - local file=$1 - # If autoenv cache directory does not exist, create it. - local cache_dir=~/.cache/autoenv - ! [ -d $cache_dir ] && mkdir -p $cache_dir - local authorized_file=$cache_dir/authorized - # Define a map to hold authorized key value pairs. - typeset -A authorized=() - # If the authorized file exists, load it into the map. - [ -f $authorized_file ] && typeset -A authorized=(`cat $authorized_file`) - # If the given file has been authorized, i.e. the modified time matches that - # held in the authorized file, return. - local modified_time=`zstat +mtime $file` - [ "$authorized[$file]" = "$modified_time" ] && return - # Prompt to authorize file. - while true; do - read "answer?Authorize $file [Y/n/v]? " - case "$answer" in - y|Y|yes|'') # Authorize the file. - authorized[$file]=$modified_time; break ;; - n|N|no) return 1 ;; # Do not authorize the file. - v|V|view) cat $file ;; # View the file. - esac - done - # Store authorized associative array in authorized file. - echo ${(kv)authorized} > $authorized_file -} - -# Source an enter script and add its directory to the global entered -# directories array. -_autoenv_enter() { - local entered=$1 - # If entered exists in the entered directories array, return. - (( ${+_autoenv_entered[${_autoenv_entered[(i)$entered]}]} )) && return - # If the enter script is not authorized, return. - _autoenv_authorized $entered/.enter || return - # Source the enter script. - source $entered/.enter - # Add the entered directory to the global entered array. - _autoenv_entered+=$entered -} - -# Source an exit script and remove its directory from the global entered -# directories array. -_autoenv_exit() { - local entered=$1 - # If the exit script is not authorized, return. - _autoenv_authorized $entered/.exit || return - # Source the exit script. - source $entered/.exit - # Remove the entered directory from the global entered array. - _autoenv_entered[${_autoenv_entered[(i)$entered]}]=() -} - -# Find all directories containing a .enter file by searching up the directory -# tree starting in the current directory. -_autoenv_find_enter_directories() { - local current=$PWD - # If an enter script is found in the current directory, return it. - [ -f $current/.enter ] && echo $current - # Loop until an enter script or the root directory is found. - while true; do - # Go up one directory and make the path absolute. - local next=$current/..; local next=${next:A} - # If an enter script is found in the current directory, return it. - [ -f $next/.enter ] && echo $next - # If the current directory equals the next directory we are done, otherwise - # update the current directory. - [[ $current == $next ]] && return || local current=$next - done -} - -# A chpwd hook function which automatically sources enter and exit scripts to -# setup local environments for directory and its subdirectories. -_autoenv_chpwd() { - local entered - # Loop over the reversed entered directory array. - for entered in ${(aO)_autoenv_entered}; do - # If the the current directory was previously entered then exit. - ! [[ $PWD/ == $entered/* ]] && _autoenv_exit $entered - done - # Find all enter script directories, store them in an array. - local enter_dirs=(`_autoenv_find_enter_directories`) - # Loop over reversed enter script directories array, so enter scripts found - # last are sourced first, then source all enter scripts. - for enter in ${(aO)enter_dirs}; do _autoenv_enter $enter; done -} - -# Register the autoenv chpwd hook. -autoload -U add-zsh-hook -add-zsh-hook chpwd _autoenv_chpwd - -# Ensure autoenv is activated in the current directory on first load. -_autoenv_chpwd +# Source the autoenv implementation script. +source ${0:a:h}/autoenv.zsh +# Add the autoenv directory to fpath for completion. +fpath+=${0:a:h} diff --git a/autoenv/autoenv.zsh b/autoenv/autoenv.zsh new file mode 100644 index 0000000..a7bba61 --- /dev/null +++ b/autoenv/autoenv.zsh @@ -0,0 +1,179 @@ +# Automatically update the environment when the current working directory +# changes, this is a reimplementation of the ideas found in the repository +# https://github.com/Tarrasch/zsh-autoenv stripped down to bare essentials. +# +# The secret sauce can be found at the bottom of this file, where the chpwd +# hook function _autoenv_chpwd is added. + +# The autoenv command provides a convenient way to create, edit, and remove +# enter and exit scripts in the current directory. +autoenv() { + local cmd=$1 + case "$cmd" in + -h|--help) # Display help. + echo "\ +usage: autoenv [-h] {init,edit,deinit} + +options: + -h, --help show this help message and exit + +commands: + init add .enter and .exit scripts in current directory + edit edit .enter and .exit scripts in current directory + deinit remove .enter and .exit scripts in current directory" + ;; + + init) # Create enter and exit scripts in current directory. + if [ -f $PWD/.enter ] || [ -f $PWD/.exit ]; then + echo '.enter or .exit already exists'; return 1 + fi + # Create then edit enter and exit scripts. + touch .enter .exit && autoenv edit + # If enter script exists, authorize it. + [ -f $PWD/.enter ] && _autoenv_authorized $PWD/.enter yes + # If exit script exists, authorize it. + [ -f $PWD/.exit ] && _autoenv_authorized $PWD/.exit yes + # Enter the new autoenv. + _autoenv_enter $PWD ;; + + edit) # Edit enter and exit scripts in current directory. + if ! [ -f $PWD/.enter ] || ! [ -f $PWD/.exit ]; then + echo '.enter or .exit not found'; return 1 + fi + # If vim exists, edit enter and exit scripts. + if which vim &> /dev/null; then + vim -p .enter .exit + else + echo 'vim not found'; return 1 + fi ;; + + deinit) # Remove enter and exit scripts in current directory. + if [ -f $PWD/.enter ] || [ -f $PWD/.exit ]; then + # Prompt user to confirm removal of enter and exit scripts. + while true; do + read "answer?Are you sure [y/N]? " + case "$answer" in + y|Y|yes) + # Remove enter and exit scripts if they exist. + [ -f $PWD/.enter ] && rm $PWD/.enter + [ -f $PWD/.exit ] && rm $PWD/.exit + break ;; + *) break ;; + esac + done + else + echo '.enter and .exit not found'; return 1 + fi ;; + + *) # Invalid arguments, show help then error. + echo "invalid arguments: $@" + autoenv --help + return 1 ;; + esac +} + +# Global entered directories array. +_autoenv_entered=() + +# Check if the given file is authorized, if not prompt the user to authorize, +# ignore, or view the file. Authorized files and their modified times are +# stored in the ~/.cache/autoenv/authorized file to make authorization +# persistent. +_autoenv_authorized() { + local file=$1 yes=$2 + # If autoenv cache directory does not exist, create it. + ! [ -d ~/.cache/autoenv ] && mkdir -p ~/.cache/autoenv + # If the authorized file does not exist, create it. + ! [ -f ~/.cache/autoenv/authorized ] && touch ~/.cache/autoenv/authorized + # Load the authorized file into a map of authorized key value pairs. + typeset -A authorized=(`cat ~/.cache/autoenv/authorized`) + # If the file has been removed, return. + ! [ -f $file ] && return 1 + # If the given file has been authorized, i.e. the modified time matches that + # held in the authorized file, return. + local modified_time=`zstat +mtime $file` + [ "$authorized[$file]" = "$modified_time" ] && return + # If yes, don't prompt for user confirmation. + if [ "$yes" != "yes" ]; then + # Prompt to authorize file. + while true; do + read "answer?Authorize $file [Y/n/v]? " + case "$answer" in + y|Y|yes|'') break ;; # Authorize the file. + n|N|no) return 1 ;; # Do not authorize the file. + v|V|view) cat $file ;; # View the file. + esac + done + fi + # Add file to the authorized map. + authorized[$file]=$modified_time + # Store authorized map in authorized file. + echo ${(kv)authorized} > ~/.cache/autoenv/authorized +} + +# Source an enter script and add its directory to the global entered +# directories array. +_autoenv_enter() { + local entered=$1 + # If entered exists in the entered directories array, return. + (( ${+_autoenv_entered[${_autoenv_entered[(i)$entered]}]} )) && return + # If the enter script is not authorized, return. + _autoenv_authorized $entered/.enter || return + # Source the enter script. + source $entered/.enter + # Add the entered directory to the global entered array. + _autoenv_entered+=$entered +} + +# Source an exit script and remove its directory from the global entered +# directories array. +_autoenv_exit() { + local entered=$1 + # If the exit script is not authorized, return. + _autoenv_authorized $entered/.exit || return + # Source the exit script. + source $entered/.exit + # Remove the entered directory from the global entered array. + _autoenv_entered[${_autoenv_entered[(i)$entered]}]=() +} + +# Find all directories containing a .enter file by searching up the directory +# tree starting in the current directory. +_autoenv_find_enter_directories() { + local current=$PWD + # If an enter script is found in the current directory, return it. + [ -f $current/.enter ] && echo $current + # Loop until an enter script or the root directory is found. + while true; do + # Go up one directory and make the path absolute. + local next=$current/..; local next=${next:A} + # If an enter script is found in the current directory, return it. + [ -f $next/.enter ] && echo $next + # If the current directory equals the next directory we are done, otherwise + # update the current directory. + [[ $current == $next ]] && return || local current=$next + done +} + +# A chpwd hook function which automatically sources enter and exit scripts to +# setup local environments for directory and its subdirectories. +_autoenv_chpwd() { + local entered + # Loop over the reversed entered directory array. + for entered in ${(aO)_autoenv_entered}; do + # If the the current directory was previously entered then exit. + ! [[ $PWD/ == $entered/* ]] && _autoenv_exit $entered + done + # Find all enter script directories, store them in an array. + local enter_dirs=(`_autoenv_find_enter_directories`) + # Loop over reversed enter script directories array, so enter scripts found + # last are sourced first, then source all enter scripts. + for enter in ${(aO)enter_dirs}; do _autoenv_enter $enter; done +} + +# Register the autoenv chpwd hook. +autoload -U add-zsh-hook +add-zsh-hook chpwd _autoenv_chpwd + +# Ensure autoenv is activated in the current directory on first load. +_autoenv_chpwd