Tab completion with CDPATH in ksh
I used bash and zsh for most of my Linux/BSD life. I switched to ksh when
I started using OpenBSD as my daily driver since that’s the default. CDPATH is
one of my favorite features of these shells. It allows me to navigate between
projects quickly and from anywhere on my file system. For example, if I had
three projects, foo, bar, and baz:
$ ls ~/Projects/src/**/*
/home/dane/Projects/src/github.com/dane:
foo bar baz
By exporting my CDPATH to CDPATH=$CDPATH:~/Projects/src, I can navigate
between them:
$ pwd
/home/dane
$ cd github.com/dane/foo
$ pwd
/home/dane/Projects/src/github.com/dane/foo
bash and zsh have cd tab complete paths and subpaths declared in CDPATH,
but ksh does not. I wanted to have a similar experience with ksh. There is
a built-in means of creating tab completions in ksh for any command, but you
must declare all possible values that can be completed. This is done with:
$ set -A complete_{COMMAND}_{ARG_COUNT} -- {VALUES}
For example, if I wanted to use tab completion to build the path
github.com/dane/foo, I would run the following:
$ export CDPATH=$CDPATH:~/Projects/src
$ set -A complete_cd_1 -- github.com/dane/foo
$ cd git<TAB>
I wanted cd to complete all of my project paths so I needed to create a
command that would list all possible paths:
$ find ~/Projects/src -type d -maxdepth 3
/home/dane/Projects/src
/home/dane/Projects/src/github.com
/home/dane/Projects/src/github.com/dane
/home/dane/Projects/src/github.com/dane/foo
/home/dane/Projects/src/github.com/dane/bar
/home/dane/Projects/src/github.com/dane/baz
Next, I needed to remove the /home/dane/Projects/src/ prefix:
find ~/Projects/src -type d -maxdepth 3 | sed "s#^$HOME/Projects/src/##g"
/home/dane/Projects/src
github.com
github.com/dane
github.com/dane/foo
github.com/dane/bar
github.com/dane/baz
find has an -exec flag, I know, but I couldn’t get it to work so I piped the
output to sed instead. Let me know if you can get -exec working!
With the directory list generated it just needed to be passed to the set -A:
set -A complete_cd_1 -- `
find ~/Projects/src -type d -maxdepth 3 | \
sed "s#^$HOME/Projects/src/##g"
`
My solution isn’t perfect. It only targets one path where CDPATH may contain
many. I can’t cd into a new project path until the complete_cd_1 values are
regenerated. /home/dane/Projects/src is unnecessarily passed to
complete_cd_1. Knowing this, I feel like my approach struck a balance between
addressing my need while remaining simple.