Autocomplete Grails Script Names in bash/zsh

2008/03/25

Jesse over at Refactr posted a nice tip about using tab completion for the ssh command. It grabs host names and IPs out of the ssh known_hosts file.

That got me to thinking that it would be pretty useful to have tab completion of Grails commands available. At my company, we’ve written about 20 custom Gant scripts and it can sometimes be a problem to remember their names. Running “grails help” often takes too long, so I probably ls scripts once a day or so to remind myself if it’s clean-db or clear-db.

I did a quick google search and found that Scott Davis posted some instructions about a year ago (originally by Doyle@DoyleCentral).

It was a good start, but there were two issues I had with the solution. The first is that it only worked in bash (I prefer zsh) and the second is that it used a static list of script names stored in the GRAILS_HOME directory. Any new scripts, or app specific scripts would need to be manually added to the static list.

Grails Gant scripts can exist in 4 possible locations:

  • $GRAILS_HOME/scripts
  • $USER_HOME/.grails/scripts
  • $PROJECT_HOME/scripts
  • $PROJECT_HOME/plugins/*/scripts

After some playing around, I was able to come up with a bash script that allows for real-time completion of all four potential script repositories. $PROJECT_HOME is considered to be the current directory, so if you’re not in a grails app, you’ll only see completion scripts for the first two.

Just copy this into your .profile/.bash_profile and restart your terminal session.

_grailsscripts() {
	SCRIPT_DIRS="$GRAILS_HOME/scripts ./scripts ~/.grails/scripts"
	if [ -d plugins ]
		then for PLUGIN_DIR in $(ls -d plugins/*/scripts 2> /dev/null); do
		SCRIPT_DIRS="$SCRIPT_DIRS $PLUGIN_DIR"
		done
	fi
 
	for D in $SCRIPT_DIRS; do
		if [ -d $D ]
			then ls -1 $D/*.groovy 2> /dev/null | sed -E 's/(.*)\/(.*)\.groovy/\2/' | sed -E 's/([A-Z])/-\1/g' | sed -E 's/^-//' | tr "[:upper:]" "[:lower:]"
		fi
	done | sort | uniq | grep -vE "^_"
}
 
_grails() {
	COMPREPLY=( $(compgen -W "$(_grailsscripts)" -- ${COMP_WORDS[COMP_CWORD]}) )
}
 
complete -F _grails grails

After you do that you can type grails c and then if you hit tab twice, you’ll see this list:

fourthwall:~ ted$ grails c
clean                    create-app               create-integration-test  create-service
compile                  create-controller        create-plugin            create-tag-lib
console                  create-domain-class      create-script            create-unit-test

If you type another r and hit tab it will finish the word create-. Then type d and hit tab and it will finish off grails create-domain-class for you.

For those using zsh, you need to have run compinstall. After that, all you need to do is put this in your .zshrc, but it needs to be below the compinit declaration added by compinstall:

_grailsscriptdirs() {
	local SCRIPT_DIRS="$GRAILS_HOME/scripts ./scripts ~/.grails/scripts"
	if [ -d plugins ]
		then for PLUGIN_DIR in $(ls -d plugins/*/scripts); do
		SCRIPT_DIRS="$SCRIPT_DIRS $PLUGIN_DIR"
		done
	fi
	echo $SCRIPT_DIRS
}
 
_grailsscripts() {
	for D in $(_grailsscriptdirs); do
		if [ -d $D ]
			then ls -1 $D/*.groovy | sed -E 's/(.*)\/(.*)\.groovy/\2/' | sed -E 's/([A-Z])/-\1/g' | sed -E 's/^-//' | tr "[:upper:]" "[:lower:]"
		fi
	done | sort | uniq | grep -vE "^_"
}
 
_grails() {
	if (( CURRENT == 2 )); then
		scripts=( $(_grailsscripts) )
		_multi_parts / scripts
    else
        _files
    fi
}
 
compdef _grails grails

(3/28/08 – zsh updated slightly to only insert scripts for the first parameter of a grails command)

I’m sure it’s possible to clean up the bash/zsh scripting (I’m a groovy programmer not a bash shell scripter :), but these both seemed to work pretty well from my testing.

There are 12 comments in this article:

  1. 2008/03/25Bernd Schiffer say:

    Cool! Works great! Thanks for the tip!

  2. 2008/03/25Siegfried say:

    Great idea! The only thing to mention is that it prints out “ls: ./scripts/*.groovy: No such file or directory” if you don’t have custom scripts in your grails app.

    To work around that, you have to redirect the stderr to /dev/null like that:

    ls -1 $D/*.groovy 2>/dev/null

    Cheers, sigi

  3. 2008/03/25tednaleid say:

    Nice catch Sigi! I had tested a blank project in the zsh version without issues, but forgot to test it in the bash one. That bug also caused output to stderr if there’s a plugins directory but nothing in it. I’ve applied your fix to the code above for both situations.

  4. 2008/03/26Sakuraba say:

    THANKS!!!

  5. 2008/04/5Marius Scurtescu say:

    Great script Ted!

    I added your script, slightly modified so it works under Linux, to an Ubuntu deb package of Grails. See the 1.0.2 Grails package:
    http://code.google.com/p/ant-deb-task/downloads/list

  6. 2008/08/5Grails Auto Complete « blogo, logo penso? say:

    [...] I thought it would be more difficult to do it, but thanks to tutorials (here and here) and the other script for completion, it was quite [...]

  7. 2010/01/6A Day At The Races » Updated bash completion script for OSX and Grails 1.1 & 1.2 say:

    [...] Takai wrote an excellent bash completion script (also available here, originally from Ted Naleid), which makes calling Grails commands much [...]

  8. 2010/02/21Danai Sae-Han say:

    Thanks for the ZSH autocomplete code! It helps a lot, because running “grails help” always takes around three seconds to load.

  9. 2010/03/2Updated grails autocomplete script for zsh - Ted Naleid say:

    [...] A couple of years ago, I created a grails auto-completion script for bash and zsh. [...]

  10. 2010/03/25grails bash completion « NAzT's Blog say:

    [...] ไปๆ มาๆ ก็ไปเจอที่ http://naleid.com/blog/2008/03/25/autocomplete-grails-script-names-in-bashzsh/ [...]

  11. 2010/05/10Tweets that mention Ubuntu: Bash completion scripts go in ~/.bash_completion Like this one for grails scripts (thx -- Topsy.com say:

    [...] This post was mentioned on Twitter by Colin Harrington. Colin Harrington said: #Ubuntu: Bash completion scripts go in ~/.bash_completion Like this one for grails scripts http://bit.ly/bvHsIs (thx @TedNaleid) [...]

  12. 2010/05/10Mihai say:

    Thanks for sharing this script!
    Add this on the ‘then ls -1 …’ line: | sed -E ’s/_$//’
    This is for replacing commands that ends with ‘_’ symbol:

    $ grails create-
    create-app_ create-integration-test create-tag-lib
    create-controller create-plugin_ create-unit-test
    create-domain-class create-script
    create-filters create-service

Write a comment: