1

A basic bash feature is the completion of commands stored in the various bin directories, regardless of the current working directory. This allows users to keep commands and binaries organized without requiring them to specify full paths from whichever directory they need to be run.

Exactly how bash provides this command completion before I run these bin files, I think, from a design standpoint, it makes sense to expect the same command completion before I edit them.

In my particular case this means modifying the completion function of my editor, vi, so it includes filenames not only from my current working dir but also from my ~/bin/ (I don't actually care about adding commands from the other bins).

A simple modification of this SO answer does this:

$ source _vi 
$ complete -f -F _vi vi

where _vi is defined as:

_vi () 
{
    local word=${COMP_WORDS[COMP_CWORD]};
    local pat="~/bin/*"

    COMPREPLY=($(compgen -f -G "$pat" -- "~/bin/${word}"));

    i=0
    for item in "${COMPREPLY[@]}"; do
        COMPREPLY[$i]="${item##*/}"
        i+=1
    done;
    return 0;
}

But vi needs to be passed full paths to open the file!

So my question is: How can I tab-complete filenames from ~/bin/ but still pass the full paths to vi? Is this only a | dream? Should I try a wholly different approach?

Bonus points if you can help me color these ~/bin/* filenames differently from the files actually in the working directory when I pull up all the possible completions with vi (TAB)(TAB)!

EDIT:

Below is the function I'm using, based on @roaima's answer and their suggestion for generalize below. It's easy to add directory-independent completion access for files in directories other than the two (binaries and config files) that I care about.

_vi () 
{
    local word=${COMP_WORDS[COMP_CWORD]};
    local bin_dir="~/bin/"
    local dot_dir="~/dotfiles/"

    COMPREPLY=($(compgen -f -G "$bin_dir*" -- "$bin_dir${word}"));
    COMPREPLY+=($(compgen -f -G "$dot_dir*" -- "$dot_dir${word}"));
    return 0;
}
0

The easiest approach is to avoid stripping the path from $item when you add it to $COMPREPLY[$i]:

_vi () 
{
    local word=${COMP_WORDS[COMP_CWORD]};
    local pat="~/bin/*"

    COMPREPLY=($(compgen -f -G "$pat" -- "~/bin/${word}"));
    return 0;
}

The list of possibles is shown without a path component, but on successful completion the path is inserted.

Another approach is to extend vi itself with a shell script that looks in the current directory for the filename, and if not found then checks ~/bin/. But at this point it starts to make sense to have the editor equivalent of $CDPATH for completion directories rather than limiting the code to searching ~/bin/.

  • Your edit works great. Thank you for taking a look at the code. What I'd still very much like, instead of the coloring I mentioned, is for the filenames sourced from the non-cwd to NOT be displayed when doing a general vi (TAB)(TAB). Right now, the config and binary filenames (which I know by heart and only need completion to save typing paths) are mixed in with the cwd filenames (of which I, in contrast, often like to jog my memory with a vi (TAB)(TAB). The extra non-cwd files make it harder to glance though the cwd files :( – courtyardz Sep 10 '15 at 18:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.