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

Re: Neat hash -d trick



On 22 October 2010 06:05, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
> [Aside to -workers:  This reminds me about Mikael Magnusson's thread
> for his proposed HASH_LOOKUP option, which sort of died out without
> resolution after a discussion of findcmd() behaving oddly.]

[I'm still meaning to look into that, it's just that hacking on zsh C
code requires a pretty rare set of circumstances, being both pretty
bored, but also in a very optimistic mood. :)]

> On Oct 22, 12:34am, Nikolai Weibull wrote:
> }
> } for ((i = 1; i < 9; i++)); do
>
> You probably mean <= 9 there?  Or just
>
>  for i in {1..9}
>
> }   hash -d .$i=${(j:/:)${(l:2::.:)${(s::)${(l:i::.:)}}}}
>
>    hash -d .$i=${${(l:i*3::../:)}%/}
>
> } done
> }
> } cd ~.4/dir
>
> A generic word of caution about using "hash -d": if you for any reason
> change the value of $PATH or $path after this, all your custom hash
> entries are lost when the table is rebuilt for the new searchpath.

Hmm? Doesn't seem to happen for me.

> A similar trick:
>
>    dotdot() {
>      if (( NUMERIC > 0 ))
>      then LBUFFER+=..; repeat $((NUMERIC-1)) LBUFFER+=/..
>      else LBUFFER+=.
>      fi
>    }
>    zle -N dotdot
>    bindkey . dotdot
>
> Now you can type ESC 4 . to insert ../../../.. (or ESC 9 ESC 9 . to
> insert 99 levels, if for some insane reason you need that many).

Doing this actually causes you to be unable to type dots in an isearch
widget, since it aborts on custom bindings. I think pws is partly
responsible for this
# just type '...' to get '../..'
rationalise-dot() {
local MATCH
if [[ $LBUFFER =~ '(^|/| |	|'$'\n''|\||;|&)\.\.$' ]]; then
  LBUFFER+=/
  zle self-insert
  zle self-insert
else
  zle self-insert
fi
}
zle -N rationalise-dot
bindkey . rationalise-dot
# without this, typing a . aborts incremental history search
bindkey -M isearch . self-insert

You only need the last line to avoid the problem of course.

> } What would be even sweeter is if someone would come up with a way to
> } do this with only one call to hash -d without writing out all the
> } expansions
>
> Because the counter has to be referenced twice in the expansion, I
> don't think there's any way of avoiding the "for" loop that's worth
> the effort to figure out.  However,
>
>    for i in {1..9}; h+=(.$i=${${(l:i*3::../:)}%/}); hash -d $h
>
> Or to avoid leaving $i and $h with a value at the end,
>
>    hash -d $( for i in {1..9}; print .$i=${${(l:i*3::../:)}%/} )

To avoid leaving $i and $h with a value and a fork ;)
% () {local i h; for i in {1..9}; h+=(.$i=${${(l:i*3::../:)}%/}); hash -d $h}
% path+=/tmp
% path[2]=()
% hash -d|head -1
.1=..

I also have a vaguely related custom widget, in case you're unsure
just how many dotdots you need.

function _showcurrargrealpath() {
  setopt localoptions nonomatch
  local REPLY REALPATH
  _split_shell_arguments_under
  #zle -M "$(realpath ${(Q)${~REPLY}} 2> /dev/null | head -n1 || echo
1>&2 "No such path")"
  REALPATH=( ${(Q)${~REPLY}}(N:A) )
  zle -M "${REALPATH:-Path not found: $REPLY}"
}
zle -N _showcurrargrealpath
bindkey "^X." _showcurrargrealpath

# which i now notice in turn relies on this bit:
autoload -U modify-current-argument
autoload -U split-shell-arguments

function _split_shell_arguments_under()
{
  local -a reply
  integer REPLY2
  split-shell-arguments
  #have to duplicate some of modify-current-argument to get the word
  #_under_ the cursor, not after.
  setopt localoptions noksharrays multibyte
  if (( REPLY > 1 )); then
    if (( REPLY & 1 )); then
      (( REPLY-- ))
    fi
  fi
  REPLY=${reply[$REPLY]}
}

I also noticed now that if you mistype a named dir and run that
widget, the whole command line aborted due to the failed $~ expansion,
not sure why it would do that. That's why I added the setopt nonomatch
just now, it'll print the original path with (:A) appended as if it
succeeds but at least better than eating the command line.

[aside to -workers: would it make sense if ~ also checked for
nullglob? i noticed in the code that it checks for nomatch only
(merely looking at zsh C code is possible more often than editing it).
not sure how many different bits use that same function to expand
stuff though.]

-- 
Mikael Magnusson



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