custom directory completion appends whitespace
Asked Answered
F

4

6

I have the following directory structure:

/home/tichy/xxx/yyy/aaa
/home/tichy/xxx/yyy/aab
/home/tichy/xxx/yyy/aac

I would like to enter cdw y<TAB> and get cdw yyy/<CURSOR> as a result, so I could add cdw yyy/a<TAB> and get cdw yyy/aa<CURSOR>

The solution I came up with gives me the following:
cdw y<TAB> => cdw yyy<SPACE><CURSOR>

Following code I have so far:

_cdw () {
    local cur prev dirs
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    COMPREPLY=($(compgen -d -- /home/tichy/xxx/${cur}|perl -pe 's{^/home/tichy/xxx/}{}'))
    # no difference, a bit more logical:
    dirs=$(compgen -o nospace -d /home/tichy/xxx/${cur}|perl -pe 's/{^/home/tichy/xxx/}{}')
    COMPREPLY=($(compgen -d -W ${dir} ${cur}|perl -pe 's{^/home/tichy/xxx/}{}'))
    return 0
}
complete -F _cdw cdw
cdw () {
    cd /home/tichy/xxx/$@
}

Any ideas what's wrong? It seems to me that the completion process seems to be finished and isn't expecting any more input.

Forefinger answered 14/12, 2011 at 10:42 Comment(2)
Did you mean for the &lt; and &gt; to look like '<' and '>' ? Good luck.Talya
yes, sorry, it didn't appear on my previewForefinger
T
2

How about something like this:

COMPREPLY=( $(cdw; compgen -W "$(for d in ${cur}* ${cur}*/*; do [[ -d "$d" ]] && echo $d/; done)" -- ${cur}) )

(I'm not sure if you can call your shell function from here or not, otherwise you may have to duplicate it a bit.)

This also gets rid of your perl hack :-)

Tit answered 14/12, 2011 at 21:55 Comment(5)
That doesn't work: it basically does what compgen -d -o dirnames ${cur} does. It also gives me the complete path I don't want (e.g. /home/tichy/... (which I could filter out with the usual perl command). It also somehow doesn't complete anything: it only shows the matches but doesn't fill in the matching characters. As far as I understand it, you want to postfix a directory with a /: That's what dirs=$(compgen -o nospace -o dirnames /home/.../${cur}|perl -pe 's{^/home/...}{};s/$/\//') does too.Forefinger
@Forefinger : It does work: I'm using it right now. The part I'm using differently is that I'm not using cdw, I'm using a different shell script. It does not do what compgen -d -o dirnames does as mine also looks for subdirectories, and adds the slash at the end. By adding the subdirs, this blocks the space from being added (unless there are no further subdirs), thus we don't need -o nospace.Tit
I apologize: I must have made an error pasting it into my script. It does indeed work like I intended.Forefinger
@Forefinger - sok. Thank you for the question - what I used to have before you asked didn't work, but your question made me think of my approach all over again. It made me re-write my completion function and suddenly it worked. My completion function is now better than it was before you asked. So, again, thanks. :-)Tit
Very well: thanks for accepting my apologizies. After getting your solution to work I was wondering whether it is not possible to have it run through compgen alone, e.g. something like compgen -o dirnames ${cur} ${cur}/*. It doesn't work that simple, but that's what your command basically does -- it only then filters the unwanted files out. But that's one of the purposes of compgen -o dirnames, isn't it?Forefinger
M
9

The simplest solution I've found so far is to generate completions that look like this:

COMPREPLY=( $( compgen -W "file1 file2 file3 dir1/ dir2/ dir3/" ) )

and add this line just before returning

[[ $COMPREPLY == */ ]] && compopt -o nospace

This sets the nospace option whenever the completion may fill in until the slash so that the user ends up with:

cmd dir1/<cursorhere>

instead of:

cmd dir1/ <cursorhere>

and it doesn't set the nospace option whenever the completion may fill in until a full filename so that the user ends up with:

cmd file1 <cursorhere>

instead of:

cmd file1<cursorhere>
Minuend answered 29/8, 2012 at 19:51 Comment(0)
M
5

If I understand correctly, you want to bash-autocomplete a directory name, and not have the extra space? (That's what I was looking for when I got to this page).

If so, when you register the completion function, use "-o nospace".

complete -o nospace -F _cdw cdw

I don't know if nospace works on compgen.

Medwin answered 16/3, 2012 at 20:22 Comment(0)
T
2

How about something like this:

COMPREPLY=( $(cdw; compgen -W "$(for d in ${cur}* ${cur}*/*; do [[ -d "$d" ]] && echo $d/; done)" -- ${cur}) )

(I'm not sure if you can call your shell function from here or not, otherwise you may have to duplicate it a bit.)

This also gets rid of your perl hack :-)

Tit answered 14/12, 2011 at 21:55 Comment(5)
That doesn't work: it basically does what compgen -d -o dirnames ${cur} does. It also gives me the complete path I don't want (e.g. /home/tichy/... (which I could filter out with the usual perl command). It also somehow doesn't complete anything: it only shows the matches but doesn't fill in the matching characters. As far as I understand it, you want to postfix a directory with a /: That's what dirs=$(compgen -o nospace -o dirnames /home/.../${cur}|perl -pe 's{^/home/...}{};s/$/\//') does too.Forefinger
@Forefinger : It does work: I'm using it right now. The part I'm using differently is that I'm not using cdw, I'm using a different shell script. It does not do what compgen -d -o dirnames does as mine also looks for subdirectories, and adds the slash at the end. By adding the subdirs, this blocks the space from being added (unless there are no further subdirs), thus we don't need -o nospace.Tit
I apologize: I must have made an error pasting it into my script. It does indeed work like I intended.Forefinger
@Forefinger - sok. Thank you for the question - what I used to have before you asked didn't work, but your question made me think of my approach all over again. It made me re-write my completion function and suddenly it worked. My completion function is now better than it was before you asked. So, again, thanks. :-)Tit
Very well: thanks for accepting my apologizies. After getting your solution to work I was wondering whether it is not possible to have it run through compgen alone, e.g. something like compgen -o dirnames ${cur} ${cur}/*. It doesn't work that simple, but that's what your command basically does -- it only then filters the unwanted files out. But that's one of the purposes of compgen -o dirnames, isn't it?Forefinger
C
1

completion provides a solution for this without any workaround: funciton _filedir defined in /etc/bash_completion:

 626 # This function performs file and directory completion. It's better than
 627 # simply using 'compgen -f', because it honours spaces in filenames.
 628 # @param $1  If `-d', complete only on directories.  Otherwise filter/pick only
 629 #            completions with `.$1' and the uppercase version of it as file
 630 #            extension.
 631 #
 632 _filedir()

Then, specifying the following is enough:

_cdw () {
    local cur prev dirs
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    _filedir # add -d at the end to complete only dirs, no files
    return 0
}
Coachandfour answered 9/5, 2013 at 14:23 Comment(1)
This is the correct generic solution, it is simpler and it works much better than compgen -f -d.Cauchy

© 2022 - 2024 — McMap. All rights reserved.