zsh-workers
 help / color / mirror / code / Atom feed
From: Bart Schaefer <schaefer@brasslantern.com>
To: Zsh hackers list <zsh-workers@zsh.org>
Subject: Shadowing builtins and functions
Date: Thu, 2 Jun 2022 18:36:52 -0700	[thread overview]
Message-ID: <CAH+w=7Y+hv5JkkdgmBr9wOr1zE8Ud4U6mevRGuBqrem6AArw4A@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1922 bytes --]

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?

[-- Attachment #2: _shadow.txt --]
[-- Type: text/plain, Size: 1935 bytes --]

#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 "$@"

             reply	other threads:[~2022-06-03  1:37 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-03  1:36 Bart Schaefer [this message]
2022-06-04 21:22 ` Bart Schaefer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH+w=7Y+hv5JkkdgmBr9wOr1zE8Ud4U6mevRGuBqrem6AArw4A@mail.gmail.com' \
    --to=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    --subject='Re: Shadowing builtins and functions' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Code repositories for project(s) associated with this inbox:

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).