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