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

Re: cdablevars and cd completion in 3.1.5

As nobody else has responded to this yet ...

On Nov 30,  7:27pm, Andrej Borsenkow wrote:
} Subject: cdablevars and cd completion in 3.1.5
} I have some long often visited directories; I named them with e.g. smbsrc
} and smbcvs, set cdablevars and can do 'cd smbsrc' quite O.K.
} Is it possible to also complete in smbsrc?

So the question is: Can one complete within a named directory?

Which implies the question:  Can one complete named directories?

Let's start with the basic completion for diretories.  In 3.0.5, that's

    compctl -g '*(-/)' cd pushd

If you also want to complete directories that begin with a ".", you need

    compctl -g '*(-/)' + -g '*(-/D)' cd pushd

In 3.1.5, almost the same effect is achieved more simply with

    compctl -/ cd pushd

(the difference being that the 3.0.5 completion returns directories that
begin with a "." anytime there are no other matching directories, whereas
the -/ completion always requires that the "." be explicitly typed).  From
here on I'm going to use -/ in all examples, but unless I say otherwise
they'll work in 3.0.5 too if you replace -/ with the -g patterns above.

Now things begin to get complicated.  There are three more things we may
want to complete:
    1. The names of cd-able variables.
    2. Subdirectories of any directory named by a variable.
    3. Subdirectories of the directories named in $cdpath.

In 3.1.5 plus a recent patch from Sven W., (3) is as easy (Note 1) as

    compctl -/ -W "( . $cdpath )" cd pushd

but in all earlier versions and for either of (1) and/or (2), a function
such as cdmatch is required, so it might as well do all of this.  First
the compctl command:

    compctl -x 'S[/][~][./][../]' -/ \
	    - 'n[-1,/], s[]' -K cdmatch -S '/' -q \
	    -- cd pushd

Remove that -q if you don't like autoremoveslash behavior.

Now the function (cdpath part copied from the 3.1.5 dist, which is the
same as the 3.0.5 dist):

    cdmatch () {
	local narg pref cdp cdv
	[[ -o cdablevars ]]; cdv=$?

	# This wipes cdablevars, hence remember it above
	emulate -R zsh
	setopt localoptions rcexpandparam

	read -nc narg
	read -Ac pref

	if ((cdv == 0))
	    if [[ "$pref" != ([0-9]*|*/*) ]]
		# Generate names of all variables whose values begin with '/'.
		# Don't include PWD or OLDPWD, lest they become named dirs!
		reply=( ${${${(M)$(set):#${pref:-[A-Za-z]}*${2:h}*\=/*}:#*(PWD|:)*}%\=/*} )
	    elif [[ "$pref" = */* ]]
		# The head may be a variable whose value begins with '/'.
		# If you type PWD/ and then complete, PWD becomes named!
		eval '[[ "${'$pref:h'}" == /* ]]' &&
		    reply=( ~${pref}*$2(-/DN^M:t) )

	# Finally, include anything along the cdpath that looks likely.
	cdp=(. $cdpath)
	reply=( $reply ${^cdp}/${pref}*$2(-/DN^M:t) )

Now the obligatory bit of zsh arcana that still confuses me:

zsh% echo ~/zshfun
zsh% cd HE
	 With cursor here, this calls cdmatch and completes HOME/.
	 But if instead I have
zsh% cd HE/zshfun
	 With the cursor here, pressing tab does NOT call cdmatch!
	 Why not?  I have completeinword set.  The word under the
	 cursor contains a /, so it should match n[-1,/].  I expected
	 it to call cdmatch with 1=H 2=E/zshfun and to be able to
	 read HE/zshfun into $pref.  If Instead I have
zsh% cd HOME/zn
	      with the cursor here, I can complete to HOME/zshfun.

I have the feeling this is something I once knew, but maybe it needs to
be written down somewhere.


Note 1:  To handle directories in cdpath that have spaces in their names,
    compctl -/ -W "( . ${(@j( ))cdpath:gs/ /\\\\\\ /} )" cd pushd

Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com

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