summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Completion/Base/.distfiles6
-rw-r--r--Completion/Base/_cache_invalid21
-rw-r--r--Completion/Base/_retrieve_cache31
-rw-r--r--Completion/Base/_store_cache36
-rw-r--r--Completion/Builtins/_zstyle2
-rw-r--r--Completion/Linux/_rpm435
-rw-r--r--Completion/User/_perl_modules108
-rw-r--r--Doc/Zsh/compsys.yo72
9 files changed, 474 insertions, 244 deletions
diff --git a/ChangeLog b/ChangeLog
index 231256fc5..f8311a2ad 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2000-08-02 Adam Spiers <adam@spiers.net>
+
+ * 12486: Completion/Base/{.distfiles,_cache_invalid,_retrieve_cache,
+ _store_cache}, Completion/Builtins/_zstyle, Completion/Linux/_rpm,
+ Completion/User/_perl_modules, Doc/Zsh/compsys.yo: new completion
+ caching layer
+
2000-08-02 Sven Wischnowsky <wischnow@zsh.org>
* 12483: Completion/Base/_arguments, Completion/Core/_complete,
diff --git a/Completion/Base/.distfiles b/Completion/Base/.distfiles
index a906bfc76..0cbe1f97f 100644
--- a/Completion/Base/.distfiles
+++ b/Completion/Base/.distfiles
@@ -1,7 +1,7 @@
DISTFILES_SRC='
.distfiles
- _arg_compile _arguments _brace_parameter _combination
+ _arg_compile _arguments _brace_parameter _cache_invalid _combination
_command_names _condition _default _describe _equal _first _in_vared
- _jobs _math _parameter _precommand _redirect _regex_arguments _subscript
- _tilde _value _values
+ _jobs _math _parameter _precommand _redirect _regex_arguments
+ _retrieve_cache _store_cache _subscript _tilde _value _values
'
diff --git a/Completion/Base/_cache_invalid b/Completion/Base/_cache_invalid
new file mode 100644
index 000000000..e55381439
--- /dev/null
+++ b/Completion/Base/_cache_invalid
@@ -0,0 +1,21 @@
+#autoload
+#
+# Function to decide whether a completions cache needs rebuilding
+
+local _cache_ident _cache_dir _cache_path _cache_policy
+_cache_ident="$1"
+
+# If the cache is disabled, we never want to rebuild it, so pretend
+# it's valid.
+zstyle -t ":completion:${curcontext}:" use-cache || return 1
+
+zstyle -s ":completion:${curcontext}:" cache-path _cache_dir
+: ${_cache_dir:=${ZDOTDIR:-$HOME}/.zcompcache}
+_cache_path="$_cache_dir/$_cache_ident"
+
+# See whether the caching policy says that the cache needs rebuilding
+# (the policy will return 0 if it does).
+zstyle -s ":completion:${curcontext}:" cache-policy _cache_policy
+[[ -n "$_cache_policy" ]] && "$_cache_policy" "$_cache_path" && return 0
+
+return 1
diff --git a/Completion/Base/_retrieve_cache b/Completion/Base/_retrieve_cache
new file mode 100644
index 000000000..6a82cd48b
--- /dev/null
+++ b/Completion/Base/_retrieve_cache
@@ -0,0 +1,31 @@
+#autoload
+#
+# Retrieval component of completions caching layer
+
+local _cache_ident _cache_dir _cache_path _cache_policy
+_cache_ident="$1"
+
+if zstyle -t ":completion:${curcontext}:" use-cache; then
+ # Decide which directory to retrieve cache from, and ensure it exists
+ zstyle -s ":completion:${curcontext}:" cache-path _cache_dir
+ : ${_cache_dir:=${ZDOTDIR:-HOME}/.zcompcache}
+ if [[ ! -d "$_cache_dir" ]]; then
+ [[ -e "$_cache_dir" ]] &&
+ _message "cache-dir ($_cache_dir) isn't a directory\!"
+ return 1
+ fi
+
+ _cache_path="$_cache_dir/$_cache_ident"
+
+ if [[ -e "$_cache_path" ]]; then
+ _cache_invalid "$_cache_ident" && return 1
+
+ . "$_cache_path"
+ return 0
+ else
+ return 1
+ fi
+else
+ return 1
+fi
+
diff --git a/Completion/Base/_store_cache b/Completion/Base/_store_cache
new file mode 100644
index 000000000..2fe7dfcb6
--- /dev/null
+++ b/Completion/Base/_store_cache
@@ -0,0 +1,36 @@
+#autoload
+#
+# Storage component of completions caching layer
+
+local _cache_ident
+_cache_ident="$1"
+
+if zstyle -t ":completion:${curcontext}:" use-cache; then
+ # Decide which directory to cache to, and ensure it exists
+ zstyle -s ":completion:${curcontext}:" cache-path _cache_dir
+ : ${_cache_dir:=${ZDOTDIR:-$HOME}/.zcompcache}
+ if [[ ! -d "$_cache_dir" ]]; then
+ if [[ -e "$_cache_dir" ]]; then
+ _message "cache-dir style points to a non-directory\!"
+ else
+ mkdir -p "$_cache_dir"
+ if [[ ! -d "$_cache_dir" ]]; then
+ _message "Couldn't create cache-dir $_cache_dir"
+ return 1
+ fi
+ fi
+ fi
+
+ shift
+ for var; do
+ case ${(Pt)var} in
+ (*readonly*) ;;
+ (*(association|array)*) print -r "$var=( ${(kv@Pqq)^^var} )";;
+ (*) print -r "$var=${(Pqq)^^var}";;
+ esac
+ done >! "$_cache_dir/$_cache_ident"
+else
+ return 1
+fi
+
+return 0
diff --git a/Completion/Builtins/_zstyle b/Completion/Builtins/_zstyle
index fefa8af51..abfec06d3 100644
--- a/Completion/Builtins/_zstyle
+++ b/Completion/Builtins/_zstyle
@@ -15,6 +15,7 @@ styles=(
assign-list c:
auto-description c:
break-keys c:
+ cache-path 'c:_wanted directories expl directory _path_files -/'
command c:command
completer c:completer
completions c:bool
@@ -80,6 +81,7 @@ styles=(
suffix c:bool
tag-order c:tag
try-to-use-pminst c:bool
+ use-cache c:bool
use-compctl c:urgh
users c:_users
users-hosts c:user-host
diff --git a/Completion/Linux/_rpm b/Completion/Linux/_rpm
index e047af6d6..11bb370b8 100644
--- a/Completion/Linux/_rpm
+++ b/Completion/Linux/_rpm
@@ -41,213 +41,238 @@
# Used by `_arguments', made local here.
-local curcontext="$curcontext" state lstate line nm="$compstate[nmatches]"
-typeset -A opt_args
-
-state=''
-
-local ret=1
-local -a tmp expl commonopts packageopts
-commonopts=(
- '*-v[verbose mode]'
- '--rcfile:resource file:_files'
- '--ftpproxy:FTP proxy server:_hosts'
- '--ftpport:FTP port number:'
- '--httpproxy:HTTP proxy server:_hosts'
- '--httpport:HTTP port number:'
-)
-packageopts=(
- '-a[query all packages]'
- '-p+[query uninstalled package file]:*:RPM package file:->package_file'
- '-f[specify file to query owner of]:file:_files'
- '--triggeredby:RPM package:->package'
- '--whatprovides:RPM capability:->capability'
- '--whatrequires:RPM capability:->capability'
-)
-pathopts=(
- '--root:RPM root directory:_files -/'
- '--dbpath:RPM database path:_files -/'
-)
-
-# Do simple completions or get the first state.
-
-_arguments -C -s \
- '--help[print help message]' \
- '--version[print version number]' \
- "${commonopts[@]}" \
- '-q+[query mode]:*:query:->query' \
- --{querytags,initdb,showrc} \
- '--pipe:pipe command:_command_names -e' \
- -{V,y}'[verify mode]:*:verify:->verify' \
- '--verify[verify mode]:*:verify:->verify' \
- '--setperms[set file permissions]:*:package:->setattrs' \
- '--setugids[set file owner/group]:*:package:->setattrs' \
- '(--install)-i+[install mode]:*:install:->install' \
- '(-i)--install:*:install:->install' \
- '(--upgrade)-U+[upgrade mode]:*:upgrade:->upgrade' \
- '(-U)--upgrade:*:upgrade:->upgrade' \
- '(--freshen)-F+[freshen mode]:*:upgrade:->upgrade' \
- '(-F)--freshen:*:upgrade:->upgrade' \
- '(--erase)-e+[uninstall mode]:*:uninstall:->uninstall' \
- '(-e)--erase:*:uninstall:->uninstall' \
- '-b+[build mode (spec file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_b' \
- '(-b)-t+[build mode (tar file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_t' \
- --{resign,addsign}':*:RPM package:->package_file' \
- '--rmsource:*:spec file:->spec_file' \
- --{rebuild,recompile}':*:Src RPM files:->package_src' \
- '(--checksig)-K+[signature check mode]:*:sigcheck:->sigcheck' \
- '(-K)--checksig:*:sigcheck:->sigcheck' \
- '--rebuilddb:*:rebuild:->rebuild' && ret=0
-
-# As long as we have a state name...
-
-while [[ -n "$state" ]]; do
-
- # First try to call a user-defined function.
-
- _funcall ret _rpm_$state && return ret
-
- # Copy the state and reset `state', to simplify the test above.
-
- lstate="$state"
+_rpm () {
+ local curcontext="$curcontext" state lstate line nm="$compstate[nmatches]"
+ typeset -A opt_args
+
state=''
- tmp=()
-
- # Dispatch...
-
- case "$lstate" in
- query)
- # --dump requires on of -{l,c,d}
- # --triggers requires --script
- _arguments -s \
- -q "${commonopts[@]}" "${packageopts[@]}" "${pathopts[@]}" \
- '--queryformat:RPM query format:->tags' \
- '-i[display package information]' \
- '--changelog[display change log]' \
- '-l[display package file list]' \
- '-s[show file states]' \
- '-d[documentation files only]' \
- '-c[configuration files only]' \
- '--dump[show all information]' \
- --provides \
- -{R,-requires}'[list dependencies]' \
- '--scripts[show (un)install scripts]' \
- '--triggers[show trigger scripts]' \
- '*:RPM package:->package_or_file' && ret=0
- ;;
- setattrs)
- _arguments -s --set{perm,ugids} "${packageopts[@]}" && ret = 0
- ;;
- verify)
- _arguments -s \
- '(-y --verify)-V' '(-V --verify)-y' '(-y -V)--verify' \
- "${commonopts[@]}" "${pathopts[@]}" \
- --no{deps,md5,files} \
- '*:RPM package:->package' && ret=0
- ;;
- upgrade)
- tmp=( '(--upgrade)-U' '(-U)--upgrade' '(--force)--oldpackage' )
- ;&
- install)
- (( $#tmp )) || tmp=( '(--install)-i' '(-i)--install' )
- _arguments -s "$tmp[@]" \
- "${commonopts[@]}" "${pathopts[@]}" \
- '--excludepath:exclude files in following path:_files -/' \
- '--relocate:relocate:->relocate' \
- '--prefix:package prefix directory:_files -/' \
- '(-h)--hash' '(--hash)-h' \
- '(--replacepkgs --replacefiles --oldpackage)--force' \
- '(--force)--'{replacefiles,replacepkgs} \
- --{badreloc,excludedocs,allfiles,ignorearch,ignoreos,includedocs,justdb,nodeps,noorder,noscripts,notriggers,percent,test} \
- '*:pkg file:->package_file' && ret=0
- ;;
- uninstall)
- _arguments -s \
- '(-e)--erase' '(--erase)-e' \
- "${commonopts[@]}" "${pathopts[@]}" \
- --{allmatches,justdb,nodeps,noorder,noscripts,notriggers} \
- '*:RPM package:->package' && ret=0
- ;;
- build_b)
- tmp=( '*:spec file:_files -g \*.spec' )
- ;&
- build_t)
- (( $#tmp )) || tmp=( '*:tar file:_files -g \*.\(\#i\)tar\(.\*\|\)' )
+
+ local ret=1
+ local -a tmp expl commonopts packageopts
+ commonopts=(
+ '*-v[verbose mode]'
+ '--rcfile:resource file:_files'
+ '--ftpproxy:FTP proxy server:_hosts'
+ '--ftpport:FTP port number:'
+ '--httpproxy:HTTP proxy server:_hosts'
+ '--httpport:HTTP port number:'
+ )
+ packageopts=(
+ '-a[query all packages]'
+ '-p+[query uninstalled package file]:*:RPM package file:->package_file'
+ '-f[specify file to query owner of]:file:_files'
+ '--triggeredby:RPM package:->package'
+ '--whatprovides:RPM capability:->capability'
+ '--whatrequires:RPM capability:->capability'
+ )
+ pathopts=(
+ '--root:RPM root directory:_files -/'
+ '--dbpath:RPM database path:_files -/'
+ )
+
+ # Do simple completions or get the first state.
+
+ _arguments -C -s \
+ '--help[print help message]' \
+ '--version[print version number]' \
+ "${commonopts[@]}" \
+ '-q+[query mode]:*:query:->query' \
+ --{querytags,initdb,showrc} \
+ '--pipe:pipe command:_command_names -e' \
+ -{V,y}'[verify mode]:*:verify:->verify' \
+ '--verify[verify mode]:*:verify:->verify' \
+ '--setperms[set file permissions]:*:package:->setattrs' \
+ '--setugids[set file owner/group]:*:package:->setattrs' \
+ '(--install)-i+[install mode]:*:install:->install' \
+ '(-i)--install:*:install:->install' \
+ '(--upgrade)-U+[upgrade mode]:*:upgrade:->upgrade' \
+ '(-U)--upgrade:*:upgrade:->upgrade' \
+ '(--freshen)-F+[freshen mode]:*:upgrade:->upgrade' \
+ '(-F)--freshen:*:upgrade:->upgrade' \
+ '(--erase)-e+[uninstall mode]:*:uninstall:->uninstall' \
+ '(-e)--erase:*:uninstall:->uninstall' \
+ '-b+[build mode (spec file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_b' \
+ '(-b)-t+[build mode (tar file)]:build stage:((p\:execute\ \%prep\ stage l\:do\ a\ list\ check c\:execute\ build\ stage i\:execute\ install\ stage b\:build\ a\ binary\ package a\:build\ binary\ and\ source\ packages)):*:build:->build_t' \
+ --{resign,addsign}':*:RPM package:->package_file' \
+ '--rmsource:*:spec file:->spec_file' \
+ --{rebuild,recompile}':*:Src RPM files:->package_src' \
+ '(--checksig)-K+[signature check mode]:*:sigcheck:->sigcheck' \
+ '(-K)--checksig:*:sigcheck:->sigcheck' \
+ '--rebuilddb:*:rebuild:->rebuild' && ret=0
+
+ # As long as we have a state name...
+
+ while [[ -n "$state" ]]; do
+
+ # First try to call a user-defined function.
+
+ _funcall ret _rpm_$state && return ret
+
+ # Copy the state and reset `state', to simplify the test above.
+
+ lstate="$state"
+ state=''
+ tmp=()
+
+ # Dispatch...
+
+ case "$lstate" in
+ query)
+ # --dump requires on of -{l,c,d}
+ # --triggers requires --script
+ _arguments -s \
+ -q "${commonopts[@]}" "${packageopts[@]}" "${pathopts[@]}" \
+ '--queryformat:RPM query format:->tags' \
+ '-i[display package information]' \
+ '--changelog[display change log]' \
+ '-l[display package file list]' \
+ '-s[show file states]' \
+ '-d[documentation files only]' \
+ '-c[configuration files only]' \
+ '--dump[show all information]' \
+ --provides \
+ -{R,-requires}'[list dependencies]' \
+ '--scripts[show (un)install scripts]' \
+ '--triggers[show trigger scripts]' \
+ '*:RPM package:->package_or_file' && ret=0
+ ;;
+ setattrs)
+ _arguments -s --set{perm,ugids} "${packageopts[@]}" && ret = 0
+ ;;
+ verify)
+ _arguments -s \
+ '(-y --verify)-V' '(-V --verify)-y' '(-y -V)--verify' \
+ "${commonopts[@]}" "${pathopts[@]}" \
+ --no{deps,md5,files} \
+ '*:RPM package:->package' && ret=0
+ ;;
+ upgrade)
+ tmp=( '(--upgrade)-U' '(-U)--upgrade' '(--force)--oldpackage' )
+ ;&
+ install)
+ (( $#tmp )) || tmp=( '(--install)-i' '(-i)--install' )
+ _arguments -s "$tmp[@]" \
+ "${commonopts[@]}" "${pathopts[@]}" \
+ '--excludepath:exclude files in following path:_files -/' \
+ '--relocate:relocate:->relocate' \
+ '--prefix:package prefix directory:_files -/' \
+ '(-h)--hash' '(--hash)-h' \
+ '(--replacepkgs --replacefiles --oldpackage)--force' \
+ '(--force)--'{replacefiles,replacepkgs} \
+ --{badreloc,excludedocs,allfiles,ignorearch,ignoreos,includedocs,justdb,nodeps,noorder,noscripts,notriggers,percent,test} \
+ '*:pkg file:->package_file' && ret=0
+ ;;
+ uninstall)
+ _arguments -s \
+ '(-e)--erase' '(--erase)-e' \
+ "${commonopts[@]}" "${pathopts[@]}" \
+ --{allmatches,justdb,nodeps,noorder,noscripts,notriggers} \
+ '*:RPM package:->package' && ret=0
+ ;;
+ build_b)
+ tmp=( '*:spec file:_files -g \*.spec' )
+ ;&
+ build_t)
+ (( $#tmp )) || tmp=( '*:tar file:_files -g \*.\(\#i\)tar\(.\*\|\)' )
+
+ _arguments -s \
+ "${commonopts[@]}" "${pathopts[@]}" \
+ --{short-circuit,clean,rmsource,sign,test} \
+ '--target:specify a build target:->target'\
+ '--buildroot:build root directory:_files -/' \
+ '--buildarch:architecture for which to build:->target' \
+ '--buildos:ositecture for which to build:' \
+ '--timecheck:time check (seconds):' "$tmp[1]" && ret=0
+ ;;
+ sigcheck)
+ _arguments -s \
+ '(-K)--checksig' '(--checksig)-K' \
+ "${commonopts[@]}" \
+ --no{pgp,md5} \
+ '*:RPM package file:->package_file' && ret=0
+ ;;
+ rebuild)
+ _arguments -s \
+ "${commonopts[@]}" "${pathopts[@]}" \
+ '*:RPM source package file:->package_file' && ret=0
+ ;;
+ target)
+ _wanted target expl 'Target platforms' \
+ compadd $(_call target rpm --showrc 2> /dev/null |grep 'compatible archs'|sed 's/.*: //') && ret=0
+ ;;
+ package_or_file)
+ state=package_file
+ ;&
+ package)
+ if ( [[ ${+_rpms} -eq 0 ]] || _cache_invalid RPMs ) &&
+ ! _retrieve_cache RPMs;
+ then
+ _rpms=( $(_call packages rpm -qa 2>/dev/null) )
+ _store_cache RPMs _rpms
+ fi
+ _wanted packages expl 'RPM package' \
+ compadd -M 'r:|-=* r:|=*' - "$_rpms[@]" && ret=0
+ ;;
+ spec_file)
+ _wanted specfiles expl 'spec file' \
+ _files -g \*.spec && ret=0
+ ;;
+ package_file)
+ _wanted files expl 'RPM package file' \
+ _files -g '*.(#i)rpm' && ret=0
+ if [[ -prefix 1 (f|ht)tp:// ]]; then
+ _wanted urls expl 'URL of RPM package file' \
+ _urls -f -g '*.(#i)rpm' "${expl[@]}" && ret=0
+ else
+ _wanted urls expl 'URL of RPM package file' \
+ compadd -S '' "${expl[@]}" ftp:// http:// && ret=0
+ fi
+ ;;
+ package_src)
+ _files -g \*.src\(\#i\).rpm
+ ;&
+ tags)
+ if compset -P '*%*\{'; then
+ _wanted tags expl 'RPM tag' \
+ compadd -M 'm:{a-z}={A-Z}' -S '\}' - \
+ "${(@)${(@f)$(_call tags rpm --querytags 2> /dev/null)}#RPMTAG_}" && ret=0
+ else
+ _message 'RPM format'
+ fi
+ ;;
+ capability)
+ _message 'RPM capability'
+ ;;
+ relocate)
+ if compset -P '*='; then
+ _description directories expl 'new path'
+ else
+ _description directories expl 'old path'
+ fi
+
+ _files "$expl[@]" -/ && ret=0
+ ;;
+ esac
+
+ [[ ret -eq 0 || $nm -ne $compstate[nmatches] ]] && return 0
+ done
+
+ return ret
+}
- _arguments -s \
- "${commonopts[@]}" "${pathopts[@]}" \
- --{short-circuit,clean,rmsource,sign,test} \
- '--target:specify a build target:->target'\
- '--buildroot:build root directory:_files -/' \
- '--buildarch:architecture for which to build:->target' \
- '--buildos:ositecture for which to build:' \
- '--timecheck:time check (seconds):' "$tmp[1]" && ret=0
- ;;
- sigcheck)
- _arguments -s \
- '(-K)--checksig' '(--checksig)-K' \
- "${commonopts[@]}" \
- --no{pgp,md5} \
- '*:RPM package file:->package_file' && ret=0
- ;;
- rebuild)
- _arguments -s \
- "${commonopts[@]}" "${pathopts[@]}" \
- '*:RPM source package file:->package_file' && ret=0
- ;;
- target)
- _wanted target expl 'Target platforms' \
- compadd $(_call target rpm --showrc 2> /dev/null |grep 'compatible archs'|sed 's/.*: //') && ret=0
- ;;
- package_or_file)
- state=package_file
- ;&
- package)
- _wanted packages expl 'RPM package' \
- compadd -M 'r:|-=* r:|=*' - $(_call packages rpm -qa 2> /dev/null) && ret=0
- ;;
- spec_file)
- _wanted specfiles expl 'spec file' \
- _files -g \*.spec && ret=0
- ;;
- package_file)
- _wanted files expl 'RPM package file' \
- _files -g '*.(#i)rpm' && ret=0
- if [[ -prefix 1 (f|ht)tp:// ]]; then
- _wanted urls expl 'URL of RPM package file' \
- _urls -f -g '*.(#i)rpm' "${expl[@]}" && ret=0
- else
- _wanted urls expl 'URL of RPM package file' \
- compadd -S '' "${expl[@]}" ftp:// http:// && ret=0
- fi
- ;;
- package_src)
- _files -g \*.src\(\#i\).rpm
- ;&
- tags)
- if compset -P '*%*\{'; then
- _wanted tags expl 'RPM tag' \
- compadd -M 'm:{a-z}={A-Z}' -S '\}' - \
- "${(@)${(@f)$(_call tags rpm --querytags 2> /dev/null)}#RPMTAG_}" && ret=0
- else
- _message 'RPM format'
- fi
- ;;
- capability)
- _message 'RPM capability'
- ;;
- relocate)
- if compset -P '*='; then
- _description directories expl 'new path'
- else
- _description directories expl 'old path'
- fi
+# set a sensible default caching policy
+local update_policy
+zstyle -s ":completion:*:*:rpm:*" cache-policy update_policy
+if [[ -z "$update_policy" ]]; then
+ zstyle ":completion:*:*:rpm:*" cache-policy _rpms_caching_policy
+fi
- _files "$expl[@]" -/ && ret=0
- ;;
- esac
+_rpms_caching_policy () {
+ # rebuild if cache is more than a week old
+ oldp=( "$1"(Nmw+1) )
+ (( $#oldp )) && return 0
- [[ ret -eq 0 || $nm -ne $compstate[nmatches] ]] && return 0
-done
+ [[ /var/lib/rpm/packages.rpm -nt "$1" ]]
+}
-return ret
+_rpm "$@"
diff --git a/Completion/User/_perl_modules b/Completion/User/_perl_modules
index 88efdd395..117933022 100644
--- a/Completion/User/_perl_modules
+++ b/Completion/User/_perl_modules
@@ -17,49 +17,85 @@
# algorithm (the zsh code does almost the same, but only misses
# modules which don't begin with an uppercase letter).
-local opts
-zparseopts -D -a opts S: q
-
-if [[ ${+_perl_modules} -eq 0 ]]; then
- if zstyle -t ":completion:${curcontext}:modules" try-to-use-pminst \
- && (( ${+commands[pminst]} )); then
- _perl_modules=( $(pminst) )
- else
- local inc libdir new_pms
- if (( ${+commands[perl]} )); then
- inc=( $( perl -e 'print "@INC"' ) )
+_perl_modules () {
+ local opts
+ zparseopts -D -a opts S: q
+
+ # Set a sensible default caching policy. This has to be done inside
+ # this function otherwise we wouldn't know the context for the style.
+ local update_policy
+ zstyle -s ":completion:${curcontext}:" cache-policy update_policy
+ if [[ -z "$update_policy" ]]; then
+ zstyle ":completion:${curcontext}:" cache-policy \
+ _perl_modules_caching_policy
+ fi
+
+ if ( [[ ${+_perl_modules} -eq 0 ]] || _cache_invalid perl_modules ) &&
+ ! _retrieve_cache perl_modules;
+ then
+ if zstyle -t ":completion:${curcontext}:modules" try-to-use-pminst &&
+ (( ${+commands[pminst]} ));
+ then
+ _perl_modules=( $(pminst) )
else
- # If perl isn't there, one wonders why the user's trying to
- # complete Perl modules. Maybe her $path is wrong?
- _message "Didn't find perl on \$PATH; guessing @INC ..."
-
- setopt localoptions extendedglob
- inc=( /usr/lib/perl5{,/{site_perl/,}<5->.([0-9]##)}(N)
- ${(s.:.)PERL5LIB} )
+ local inc libdir new_pms
+ if (( ${+commands[perl]} )); then
+ inc=( $( perl -e 'print "@INC"' ) )
+ else
+ # If perl isn't there, one wonders why the user's trying to
+ # complete Perl modules. Maybe her $path is wrong?
+ _message "Didn't find perl on \$PATH; guessing @INC ..."
+
+ setopt localoptions extendedglob
+ inc=( /usr/lib/perl5{,/{site_perl/,}<5->.([0-9]##)}(N)
+ ${(s.:.)PERL5LIB} )
+ fi
+
+ typeset -agU _perl_modules # _perl_modules is global, no duplicates
+ _perl_modules=( )
+
+ for libdir in $inc; do
+ # Ignore cwd - could be too expensive e.g. if we're near /
+ if [[ $libdir == '.' ]]; then break; fi
+
+ # Find all modules
+ if [[ -d $libdir && -x $libdir ]]; then
+ cd $libdir
+ new_pms=( {[A-Z]*/***/,}*.pm~*blib*(N) )
+ cd $OLDPWD
+ fi
+
+ # Convert to Perl nomenclature
+ new_pms=( ${new_pms:r:fs#/#::#} )
+
+ _perl_modules=( $new_pms $_perl_modules )
+ done
fi
+
+ _store_cache perl_modules _perl_modules
+ fi
+
+ local expl
+
+ _wanted modules expl 'Perl modules' compadd "$opts[@]" -a _perl_modules
+}
- typeset -agU _perl_modules # _perl_modules is global, no duplicates
- _perl_modules=( )
-
- for libdir in $inc; do
- # Ignore cwd - could be too expensive e.g. if we're near /
- if [[ $libdir == '.' ]]; then break; fi
+_perl_modules_caching_policy () {
+ local _perllocals
- # Find all modules
- if [[ -d $libdir && -x $libdir ]]; then
- cd $libdir
- new_pms=( {[A-Z]*/***/,}*.pm~*blib*(N) )
- cd $OLDPWD
- fi
+ # rebuild if cache is more than a week old
+ oldp=( "$1"(Nmw+1) )
+ (( $#oldp )) && return 0
- # Convert to Perl nomenclature
- new_pms=( ${new_pms:r:fs#/#::#} )
+ _perllocals=( /usr/lib/perl5/**/perllocal.pod(N) )
- _perl_modules=( $new_pms $_perl_modules )
+ if (( $#_perllocals )); then
+ for pod in $_perllocals; do
+ [[ "$pod" -nt "$1" ]] && return 0
done
fi
-fi
-local expl
+ return 1
+}
-_wanted modules expl 'Perl modules' compadd "$opts[@]" -a _perl_modules
+_perl_modules "$@"
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index cb0d4880a..9a0c1a57d 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -914,6 +914,13 @@ should be a pattern and all keys matching this pattern will cause the
widget to stop incremental completion without the key having any
further effect.
)
+kindex(cache-path, completion style)
+item(tt(cache-path))(
+This style defines the path where any cache files containing dumped
+completion data are stored. Defaults to `tt($DOTDIR/.zcompcache)', or
+`tt($HOME/.zcompcache)' if tt($DOTDIR) is not defined. The completion
+layer will not be used unless the tt(use-cache) style is set.
+)
kindex(command, completion style)
item(tt(command))(
In many places, completion functions need to call external commands to
@@ -2083,6 +2090,13 @@ sensible default behavior that causes arguments (whether normal command
arguments or arguments of options) to be completed before option names for
most commands.
)
+kindex(use-cache, completion style)
+item(tt(use-cache))(
+If this is set, the completion caching layer is activated for any completions
+which use it (via the tt(_store_cache), tt(_retrieve_cache), and
+tt(_cache_invalid) functions). The directory containing the cache
+files can be changed with the tt(cache-path) style.
+)
kindex(use-compctl, completion style)
item(tt(use-compctl))(
If this style is set to a string em(not) equal to tt(false), tt(0),
@@ -3189,6 +3203,22 @@ arguments. The first one describes the first argument as a
be completed. The last description says that all other arguments are
`var(page numbers)' but does not give possible completions.
)
+findex(_cache_invalid)
+item(tt(_cache_invalid) var(cache_identifier))(
+This function returns 0 if the completions cache corresponding to the
+given cache identifier needs rebuilding. It determines this by
+looking up the tt(cache-policy) style for the current context, and
+if it exists, runs the function of the same name, supplying the full
+path to the relevant cache file as the only argument.
+
+Example:
+
+example(_example_caching_policy () {
+ # rebuild if cache is more than a week old
+ oldp=( "$1"(Nmw+1) )
+ (( $#oldp ))
+})
+)
findex(_call)
item(tt(_call) var(tag) var(string) ...)(
This function is used in places where a command is called, making it
@@ -3595,6 +3625,18 @@ while _tags; do
(( ret )) || break
done)
)
+findex(_retrieve_cache)
+item(tt(_retrieve_cache) var(cache_identifier))(
+This function retrieves completion information from the file given by
+var(cache_identifier), stored in a directory specified by the
+tt(cache-path) style (defaults to tt(~/.zsh/cache)). The return value
+is zero if retrieval was successful. It will only attempt retrieval
+if the tt(use-cache) style is set, so you can call this function
+without worrying about whether the user wanted to use the caching
+layer.
+
+See tt(_store_cache) below for more details.
+)
findex(_sep_parts)
item(tt(_sep_parts))(
This function is passed alternating arrays and separators as arguments.
@@ -3718,6 +3760,36 @@ separate set. With the tt(-m) option, the arguments are treated in the
same way as the the values for the tt(tag-order) style (except for the
`tt(!...)', `tt(-)' and `tt(foo())' forms).
)
+findex(_store_cache)
+item(tt(_store_cache) var(cache_identifier) var(vars) ...)(
+This function, when combined with tt(_retrieve_cache) and
+tt(_cache_invalid), makes it easy to implement a caching layer for
+your completion functions. If a completion function needs to perform
+a costly operation in order to generate data which is used to
+calculate completions, you can store that data in variables, and use
+this function to dump the values of those variables to a file. Then,
+if they are needed in subsequent shell invocations, they can be
+retrieved quickly from that file via tt(_retrieve_cache), avoiding the
+needly for repeating the costly operation.
+
+The var(cache_identifier) specifies the file which the data should be
+dumped to, and is stored in a directory specified by the
+tt(cache-path) style (defaults to tt(~/.zsh/cache)). The remaining
+var(vars) arguments are the variables to dump to the file.
+
+The return value is zero if storage was successful. The function will
+only attempt storage if the tt(use-cache) style is set, so you can
+call this function without worrying about whether the user wanted to
+use the caching layer.
+
+If your completion function avoids calling _retrieve_cache when it
+already has the completion data in the environment, it should probably
+at least call tt(_cache_invalid) to check whether this data and the
+data cached on disk is still valid.
+
+See the _perl_modules completion function for a simple example of
+usage of this caching layer.
+)
findex(_tags)
item(tt(_tags) [ tt(-C) var(name) [ var(tags) ... ] ])(
If called with arguments, these are taken as the names of the tags for