Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Shadowing builtins and functions



In workers 50316, I wrote:
> That needs some work to avoid conflicting with other helpers that
> redefine compadd

As far as I can tell that now means only _complete_help and
_approximate, and _approximate already avoids redefining compadd when
it is a function instead of a builtin.  I'm not sure if that affects
the results of _complete_help for contexts that use _approximate.

Nevertheless it seems as though a helper for temporarily redefining
builtins and functions as new functions is possible, so an attempt at
it is attached.  The idea here is to create a new unique function name
and link it to the original function, which preserves the function
body, and then unwind that when the redefined function is not needed.
There's no neat way to do this without help from the caller in the
form of an "always" block.

So after a bit of noodling with that idea, the result is _shadow
(attached as _shadow.txt for list server compatibility).  Example of
usage:

diff --git a/Completion/Base/Widget/_complete_help
b/Completion/Base/Widget/_complete_help
index 69855de9d..da5947e7f 100644
--- a/Completion/Base/Widget/_complete_help
+++ b/Completion/Base/Widget/_complete_help
@@ -10,6 +10,7 @@ _complete_help() {
   local -H _help_filter_funcstack="alternative|call_function|describe|dispatch|wanted|requested|all_labels|next_label"

   {
+    _shadow compadd compcall zstyle
     compadd() { return 1 }
     compcall() { _help_sort_tags use-compctl }
     zstyle() {
@@ -43,7 +44,7 @@ _complete_help() {

     ${1:-_main_complete}
   } always {
-    unfunction compadd compcall zstyle
+    _unshadow compadd compcall zstyle
   }

   for i in "${(@ok)help_funcs}"; do

I'm not certain that Completion/Base/Utility is where _shadow would
best be committed, but currently the only places we might want it are
in completion, so it should be loaded along with completions, so
that's where I was thinking to put it.

Remarks?
#autoload

## Recommended usage:
#  {
#    _shadow fname
#    function fname {
#      # Do your new thing
#    }
#    # Invoke callers of fname
#  } always {
#    _unshadow fname
#  }
## Alternate usage:
# {
#   _shadow -s suffix fname
#   function fname {
#     # Do other stuff
#     fname@suffix new args for fname
#   }
#   # Invoke callers of fname
# } always {
#   _unshadow -s suffix fname
# }
##

# BUGS:
# * `functions -c` acts like `autoload +X`
# * name collisions are possible in alternate usage
# * functions that examine $0 probably misfire

zmodload zsh/parameter # Or what?

# This probably never comes up, but protect ourself from recursive call
# chains that may duplicate the top elements of $funcstack by creating
# a counter of _shadow calls and using it to make shadow names unique.
typeset -gHi _shadowdepth=0

# Create a copy of each fname so that a caller may redefine
_shadow() {
  local -A fsfx=( -s ${funcstack[2]}:${functrace[2]}:$((_shadowdepth+1)) )
  local fname
  zparseopts -K -A fsfx -D s:
  for fname; do
    local shadowname=${fname}@${fsfx[-s]}
    (( $+functions[$fname] )) &&
      builtin functions -c $fname $shadowname
  done
  ((_shadowdepth++))
}

# Remove the redefined function and shadowing name
_unshadow() {
  local -A fsfx=( -s ${funcstack[2]}:${functrace[2]}:${_shadowdepth} )
  local fname
  zparseopts -K -A fsfx -D s:
  for fname; do
    local shadowname=${fname}@${fsfx[-s]}
    if (( $+functions[$shadowname] )); then
      builtin functions -c $shadowname $fname
      builtin unfunction $shadowname
    elif (( $+functions[$fname] )); then
      builtin unfunction $fname
    fi
  done
  ((_shadowdepth--))
}

# This is tricky.  When we call _shadow recursively from autoload,
# there's an extra level of stack in $functrace that will confuse
# the later call to _unshadow.  Fool ourself into working correctly.
(( ARGC )) && _shadow -s ${funcstack[2]}:${functrace[2]}:1 "$@"


Messages sorted by: Reverse Date, Date, Thread, Author