zsh-users
 help / color / mirror / code / Atom feed
* Neat hash -d trick
@ 2010-10-21 22:34 Nikolai Weibull
  2010-10-22  4:05 ` Bart Schaefer
  2010-10-27 15:45 ` Jean-Rene David
  0 siblings, 2 replies; 8+ messages in thread
From: Nikolai Weibull @ 2010-10-21 22:34 UTC (permalink / raw)
  To: Zsh Users

I came up with this just now:

for ((i = 1; i < 9; i++)); do
  hash -d .$i=${(j:/:)${(l:2::.:)${(s::)${(l:i::.:)}}}}
done

It allows you to write

cd ~.4/dir

which would cd to ../../../../dir, given that that directory exists.

If someone could come up with a simpler expansion that would be sweet.

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 and can easily be parameterized.  I couldn’t figure out a
way to include the loop in the expansion.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-21 22:34 Neat hash -d trick Nikolai Weibull
@ 2010-10-22  4:05 ` Bart Schaefer
  2010-10-22  7:28   ` Mikael Magnusson
                     ` (2 more replies)
  2010-10-27 15:45 ` Jean-Rene David
  1 sibling, 3 replies; 8+ messages in thread
From: Bart Schaefer @ 2010-10-22  4:05 UTC (permalink / raw)
  To: Zsh Users

[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.]

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.

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).

} 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::../:)}%/} )


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-22  4:05 ` Bart Schaefer
@ 2010-10-22  7:28   ` Mikael Magnusson
  2010-10-22  8:09     ` Julien Nicoulaud
  2010-10-22  8:39     ` Jérémie Roquet
  2010-10-22 10:29   ` Nikolai Weibull
  2010-10-22 14:24   ` Oliver Kiddle
  2 siblings, 2 replies; 8+ messages in thread
From: Mikael Magnusson @ 2010-10-22  7:28 UTC (permalink / raw)
  To: Zsh Users

On 22 October 2010 06:05, Bart Schaefer <schaefer@brasslantern.com> 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


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-22  7:28   ` Mikael Magnusson
@ 2010-10-22  8:09     ` Julien Nicoulaud
  2010-10-22  8:39     ` Jérémie Roquet
  1 sibling, 0 replies; 8+ messages in thread
From: Julien Nicoulaud @ 2010-10-22  8:09 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Zsh Users

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

I use this too:

rationalise-dot() {
  if [[ $LBUFFER = *.. ]]; then
    LBUFFER+=/..
  else
    LBUFFER+=.
  fi
}
zle -N rationalise-dot
bindkey . rationalise-dot

2010/10/22 Mikael Magnusson <mikachu@gmail.com>

> On 22 October 2010 06:05, Bart Schaefer <schaefer@brasslantern.com> 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
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-22  7:28   ` Mikael Magnusson
  2010-10-22  8:09     ` Julien Nicoulaud
@ 2010-10-22  8:39     ` Jérémie Roquet
  1 sibling, 0 replies; 8+ messages in thread
From: Jérémie Roquet @ 2010-10-22  8:39 UTC (permalink / raw)
  To: Zsh Users

2010/10/22 Mikael Magnusson <mikachu@gmail.com>:
> # 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.

I'm using something similar, though I'll probably use your test now
because the expansion isn't always appropriate :)

function magic-dot()
{
    if [[ $LBUFFER = *. ]]; then
        LBUFFER+=./
        display-path $LBUFFER
    elif [[ $LBUFFER = *../ ]]; then
        LBUFFER+=../
        display-path $LBUFFER
    else
        zle self-insert
    fi
}
zle -N magic-dot

.. gives ../ so you can use completion without typing the /
... gives ../../
and so on

display-path() prints the target path under the current line, so that
I know where I'm going.
It's a simple trick using xterm control sequences; I don't guarantee
it'll work everywhere but at least it works everywhere I've tested it.

function display-path()
{
    local newpath

    newpath=`pwd`/`echo $1 | sed 's_\(.* \)\?\([^ ]\+\)$_\2_'`
    echo -n "\e7\n\e[40m$newpath:A\e[0m\e[0K\e8\e[B\e[A"
}

The only problem I've with these tricks is when pasting paths with
dots in my shell, because ../../foo/bar becomes ../../../foo/bar

Best regards,

-- 
Jérémie


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-22  4:05 ` Bart Schaefer
  2010-10-22  7:28   ` Mikael Magnusson
@ 2010-10-22 10:29   ` Nikolai Weibull
  2010-10-22 14:24   ` Oliver Kiddle
  2 siblings, 0 replies; 8+ messages in thread
From: Nikolai Weibull @ 2010-10-22 10:29 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

On Fri, Oct 22, 2010 at 06:05, Bart Schaefer <schaefer@brasslantern.com> wrote:

> On Oct 22, 12:34am, Nikolai Weibull wrote:
> }
> } for ((i = 1; i < 9; i++)); do
>
> You probably mean <= 9 there?  Or just

Yes.

>  for i in {1..9}

Ah, of course.

> }   hash -d .$i=${(j:/:)${(l:2::.:)${(s::)${(l:i::.:)}}}}
>
>    hash -d .$i=${${(l:i*3::../:)}%/}

Again, obvious, once you see it.  I had a feeling my way was
unnecessarily complicated ;-).


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-22  4:05 ` Bart Schaefer
  2010-10-22  7:28   ` Mikael Magnusson
  2010-10-22 10:29   ` Nikolai Weibull
@ 2010-10-22 14:24   ` Oliver Kiddle
  2 siblings, 0 replies; 8+ messages in thread
From: Oliver Kiddle @ 2010-10-22 14:24 UTC (permalink / raw)
  To: Zsh Users

Bart wrote:
> A similar trick:
  [snip]
> 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).

If you find counting the directories harder than doing it interactively
on the screen we could try using menu selection. This splits either the
path currently on the command-line or $PWD if that's empty into
directory components and then does menu selection with them in a packed
list.
  zle -C up-dir reverse-menu-complete _generic
  zstyle ':completion:up-dir::::' completer _up-dir
  bindkey '^X\e[A' up-dir

And _up-dir contains:
  #compdef -k reverse-menu-complete ^X\e[A

  local -a dirs
  local -a dest
  compset -P '*[=:]'
  compset -S ':*'
  if [[ $compstate[context] = tilde ]]; then
    PREFIX="~$PREFIX"
    IPREFIX=${IPREFIX%\~}
  fi
  dirs=( ${(s./.)${~PREFIX:-$PWD}} )
  for f in $dirs; do
    dest+=${dest[-1]}/$f
  done
  PREFIX=
  compstate[list]=packed
  compadd -V dirs -d dirs -f -qS/ -a dest

I'm not sure if that's depending on further styles I have but you should
be able to get the idea. I can't remember how to make menu selection
really do reverse-menu-complete and give me the last completion to begin
with.  It clears off anything up to and equals or colon in the current
word and includes a hack for the tilde context. I was also trying to
remember if there was a way to separate the completion matches with a
slash instead of spaces but I don't think there is.

Oliver
 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Neat hash -d trick
  2010-10-21 22:34 Neat hash -d trick Nikolai Weibull
  2010-10-22  4:05 ` Bart Schaefer
@ 2010-10-27 15:45 ` Jean-Rene David
  1 sibling, 0 replies; 8+ messages in thread
From: Jean-Rene David @ 2010-10-27 15:45 UTC (permalink / raw)
  To: zsh-users

* Nikolai Weibull [2010.10.21 18:40]:
> I came up with this just now:
> 
> for ((i = 1; i < 9; i++)); do
>   hash -d .$i=${(j:/:)${(l:2::.:)${(s::)${(l:i::.:)}}}}
> done
> 
> It allows you to write
> 
> cd ~.4/dir
> 
> which would cd to ../../../../dir, given that that directory exists.
> 
> If someone could come up with a simpler expansion that would be sweet.

Once, a long time ago, I bound "\C-k" to "cd .."
and I never looked back. It's easy to type and
easy to repeat. And with the current directory in
the prompt (RPS1 in my case) it's very easy (and
very fast) to go up high enough but no further in
the directory tree (without having to count
directories).

Neat trick though... :-)

-- 
JR


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-10-27 16:18 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-21 22:34 Neat hash -d trick Nikolai Weibull
2010-10-22  4:05 ` Bart Schaefer
2010-10-22  7:28   ` Mikael Magnusson
2010-10-22  8:09     ` Julien Nicoulaud
2010-10-22  8:39     ` Jérémie Roquet
2010-10-22 10:29   ` Nikolai Weibull
2010-10-22 14:24   ` Oliver Kiddle
2010-10-27 15:45 ` Jean-Rene David

Code repositories for project(s) associated with this public 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).