#compdef module

#
# Zsh command-line completion for module
# Copyright (C) 2017 Xavier Delaruelle <xavier.delaruelle@cea.fr>
#

_module_avail() {
   local cur="${1:-}";
   # skip avail call if word currently being completed is an option keyword
   if [ -z "$cur" -o "${cur:0:1}" != '-' ]; then
      module avail --color=never -s -t -S --no-indepth -o '' $cur 2>&1
   fi
}

_module_savelist() {
   module savelist --color=never -s -t 2>&1 | sed '
      /No named collection\.$/d;
      /Named collection list$/d;
      /:$/d;'
}

_module_not_yet_loaded() {
   _module_avail ${1:-} | sort | @SED_ERE@ "\%^(${LOADEDMODULES//:/|})$%d"
}


_module_avail_mods() {
   local -a avail_mods;
   local suffix=' ';
   avail_mods=(${$(_module_avail $cur ${1:-})})

   # do not append space to word completed if it is a directory (ends with /)
   for val in $avail_mods; do
      if [ "${val: -1:1}" = '/' ]; then
         suffix=''
         break
      fi
   done

   compadd -S "$suffix" -a avail_mods && ret=0
}

_module_saved_colls() {
   local -a saved_colls;
   saved_colls=(${$(_module_savelist)})

   _describe -t saved-colls 'saved collections' saved_colls && ret=0
}

_module_notloaded_mods() {
   local -a not_yet_loaded_mods;
   local suffix=' ';
   not_yet_loaded_mods=(${$(_module_not_yet_loaded ${1:-})})

   # do not append space to word completed if it is a directory (ends with /)
   for val in $not_yet_loaded_mods; do
      if [ "${val: -1:1}" = '/' ]; then
         suffix=''
         break
      fi
   done

   compadd -S "$suffix" -a not_yet_loaded_mods && ret=0
}

_module_loaded_mods() {
   local -a loaded_mods;
   loaded_mods=(${=LOADEDMODULES//:/ })

   _describe -t loaded-mods 'loaded modulefiles' loaded_mods && ret=0
}

_module_used_paths() {
   local -a used_paths;
   used_paths=(${=MODULEPATH//:/ })

   _describe -t used-paths 'enabled modulepaths' used_paths && ret=0
}


_module() {
   typeset -A opt_args

   _arguments -C \
      '(-T --trace)'{-T,--trace}'[Enable trace and debug messages]' \
      '(-D --debug)'{-D,--debug}'[Enable debug messages]' \
      '(-v --verbose)'{-v,--verbose}'[Enable verbose messages]' \
      '(-s --silent)'{-s,--silent}'[Turn off error, warning and informational messages]' \
      '(-h --help)'{-h,--help}'[Usage info]' \
      '(-V --version)'{-V,--version}'[Module version]' \
      '(-w --width=)'{-w,--width=}'[Set output width]' \
      '--paginate[Pipe mesg output into a pager if stream attached to terminal]' \
      '--no-pager[Do not pipe message output into a pager]' \
      '(--color --color=)'{--color,--color=}'[Colorize the output]' \
      '(-): :->cmd' \
      '(-)*:: :->arg' && ret=0

   case $state in
      (cmd)
         local -a cmds; cmds=(
            'add:Load modulefile(s)'
            'load:Load modulefile(s)'
            'try-add:Attempt to load modulefile(s), no complain'
            'try-load:Attempt to load modulefile(s), no complain'
            'rm:Remove modulefile(s)'
            'remove:Remove modulefile(s)'
            'del:Remove modulefile(s)'
            'unload:Remove modulefile(s)'
            'purge:Unload all loaded modulefiles'
            'reload:Unload then load all loaded modulefiles'
            'refresh:Unload then load all loaded modulefiles'
            'switch:Unload mod1 and load mod2'
            'swap:Unload mod1 and load mod2'
            'list:List loaded modules'
            'avail:List all or matching available modules'
            'is-avail:Is any of the modulefile(s) available'
            'is-loaded:Test if any of the modulefile(s) are loaded'
            'info-loaded:Get full name of matching loaded module(s)'
            'aliases:List all module aliases'
            'whatis:Print whatis information of modulefile(s)'
            'apropos:Search all name and whatis containing str'
            'keyword:Search all name and whatis containing str'
            'search:Search all name and whatis containing str'
            'save:Save current module list to collection'
            'restore:Restore module list from collection or file'
            'saverm:Remove saved collection'
            'saveshow:Display information about collection'
            'savelist:List all saved collections'
            'is-saved:Test if any of the collection(s) exists'
            'initlist:List all modules loaded from init file'
            'initadd:Add modulefile to shell init file'
            'initrm:Remove modulefile from shell init file'
            'initprepend:Add to beginning of list in init file'
            'initswitch:Switch mod1 with mod2 from init file'
            'initclear:Clear all modulefiles from init file'
            'help:Print this or modulefile(s) help info'
            'display:Display information about modulefile(s)'
            'show:Display information about modulefile(s)'
            'test:Test modulefile(s)'
            'use:Add dir(s) to MODULEPATH variable'
            'unuse:Remove dir(s) from MODULEPATH variable'
            'is-used:Is any of the dir(s) enabled in MODULEPATH'
            'path:Print modulefile path'
            'paths:Print path of matching available modules'
            'source:Execute scriptfile(s)'
            'append-path:Append value to environment variable'
            'prepend-path:Prepend value to environment variable'
            'remove-path:Remove value from environment variable'
            'clear:Reset Modules-specific runtime information'
            'config:Display or set Modules configuration'
            'sh-to-mod:Make modulefile from script env changes'
            'edit:Open modulefile in editor'
         )
         # show commands only with compatible options
         if (( !$+opt_args[-h] && !$+opt_args[--help] \
            && !$+opt_args[-V] && !$+opt_args[--version] )); then
            _describe -t cmds 'Module Sub-Commands' cmds && ret=0
         fi
         ;;
      (arg)
         local cmd="${words[1]}"
         local cur="${words[CURRENT]}"
         case $cmd in
            (load|add|try-load|try-add)
               _arguments \
                  '--auto[Enable automated module handling mode]' \
                  '--no-auto[Disable automated module handling mode]' \
                  '(-f --force)'{-f,--force}'[By-pass dependency consistency]' \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  "*::modulefile:{_module_notloaded_mods $cur}" && ret=0
               ;;
            (avail)
               _arguments \
                  '(-l --long)'{-l,--long}'[Display output in long format]' \
                  '(-t --terse)'{-t,--terse}'[Display output in terse format]' \
                  '(-j --json)'{-j,--json}'[Display output in JSON format]' \
                  '(-o --output=)'{-o,--output=}'[Define elements to output in addition to module names]' \
                  '(-a --all)'{-a,--all}'[Include hidden modules in search]' \
                  '(-d --default)'{-d,--default}'[Only show default versions available]' \
                  '(-L --latest)'{-L,--latest}'[Only show latest versions available]' \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  '(-S --starts-with)'{-S,--starts-with}'[Search modules whose name begins with query string]' \
                  '(-C --contains)'{-C,--contains}'[Search modules whose name contains query string]' \
                  '--indepth[Perform recursive avail search]' \
                  '--no-indepth[Perform non-recursive avail search]' \
                  "*::modulefile:{_module_avail_mods $cur}" && ret=0
               ;;
            (aliases)
               _arguments \
                  '(-a --all)'{-a,--all}'[Include hidden modules in search]' && ret=0
               ;;
            (list)
               _arguments \
                  '(-l --long)'{-l,--long}'[Display output in long format]' \
                  '(-t --terse)'{-t,--terse}'[Display output in terse format]' \
                  '(-j --json)'{-j,--json}'[Display output in JSON format]' \
                  '(-o --output=)'{-o,--output=}'[Define elements to output in addition to module names]' \
                  '(-a --all)'{-a,--all}'[Include hidden modules in list]' && ret=0
               ;;
            (savelist)
               local -a opts; opts=(
                  '-l:Display output in long format'
                  '--long:Display output in long format'
                  '-t:Display output in terse format'
                  '--terse:Display output in terse format'
                  '-j:Display output in JSON format'
                  '--json:Display output in JSON format'
                  '-o:Define elements to output in addition to module names'
                  '--output=:Define elements to output in addition to module names'
               )
               _describe -t opts 'Switches' opts && ret=0
               ;;
            (clear)
               _arguments \
                  '(-f --force)'{-f,--force}'[Skip confirmation dialog]' && ret=0
               ;;
            (restore|save|saveshow|saverm|is-saved)
               _alternative 'avail-colls:collections:{_module_saved_colls}' \
                  && ret=0
               ;;
            (rm|del|remove|unload)
               _arguments \
                  '--auto[Enable automated module handling mode]' \
                  '--no-auto[Disable automated module handling mode]' \
                  '(-f --force)'{-f,--force}'[By-pass dependency consistency]' \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  '*::modulefile:_module_loaded_mods' && ret=0
               ;;
            (switch|swap)
               _arguments \
                  '--auto[Enable automated module handling mode]' \
                  '--no-auto[Disable automated module handling mode]' \
                  '(-f --force)'{-f,--force}'[By-pass dependency consistency]' \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  '1:loaded modulefile:_module_loaded_mods' \
                  "2:modulefile:{_module_notloaded_mods $cur}" && ret=0
               ;;
            (unuse|is-used)
               _alternative 'used-paths:modulepaths:{_module_used_paths}' \
                  && ret=0
               ;;
            (use)
               _arguments \
                  '(-a --append)'{-a,--append}'[Append directory to MODULEPATH]' \
                  '(-p --prepend)'{-p,--prepend}'[Prepend directory to MODULEPATH]' \
                  '*:modulepath:_files -/' && ret=0
               ;;
            (display|help|show|test|path|paths|is-loaded|info-loaded)
               _arguments \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  "*::modulefile:{_module_avail_mods $cur}" && ret=0
               ;;
            (is-avail)
               _arguments \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  '(-a --all)'{-a,--all}'[Include hidden modules in search]' \
                  "*::modulefile:{_module_avail_mods $cur}" && ret=0
               ;;
            (whatis)
               _arguments \
                  '(-i --icase)'{-i,--icase}'[Case insensitive match]' \
                  '(-j --json)'{-j,--json}'[Display output in JSON format]' \
                  '(-a --all)'{-a,--all}'[Include hidden modules in search]' \
                  "*::modulefile:{_module_avail_mods $cur}" && ret=0
               ;;
            (apropos|keyword|search)
               _arguments \
                  '(-j --json)'{-j,--json}'[Display output in JSON format]' \
                  '(-a --all)'{-a,--all}'[Include hidden modules in search]' \
                  && ret=0
               ;;
            (append-path|prepend-path)
               _arguments \
                  '(-d --delim)'{-d,--delim}'[Path element separator]' \
                  '--duplicates[Duplicate existing element]' \
                  && ret=0
               ;;
            (remove-path)
               _arguments \
                  '(-d --delim)'{-d,--delim}'[Path element separator]' \
                  '--index[Remove path element with index]' \
                  && ret=0
               ;;
            (config)
               _arguments \
                  '--dump-state[Report each state value of current Modules execution]' \
                  '--reset[Unset environment variable relative to configuration key]' \
                  '1:configuration key:(advanced_version_spec auto_handling avail_indepth avail_output avail_terse_output collection_pin_version collection_target color colors contact editor extended_default extra_siteconfig home icase implicit_default implicit_requirement list_output list_terse_output locked_configs mcookie_version_check ml nearly_forbidden_days pager rcfile run_quarantine search_match set_shell_startup shells_with_ksh_fpath silent_shell_debug tag_abbrev tag_color_name term_background term_width unload_match_order variant_shortcut verbosity wa_277)' \
                  && ret=0
               ;;
            (edit)
               _arguments \
                  "*::modulefile:{_module_avail_mods $cur}" && ret=0
               ;;
         esac
         ;;
   esac
}

_module "$@"

# vim:set tabstop=3 shiftwidth=3 expandtab autoindent:
