Skip to content

Instantly share code, notes, and snippets.

@nolanlawson
Last active September 25, 2024 12:51
Show Gist options
  • Save nolanlawson/8694399 to your computer and use it in GitHub Desktop.
Save nolanlawson/8694399 to your computer and use it in GitHub Desktop.
Gradle tab completion for Bash. Works on both Mac and Linux.

Gradle tab completion script for Bash

A tab completion script that works for Bash. Relies on the BSD md5 command on Mac and md5sum on Linux, so as long as you have one of those two commands, this should work.

Usage

$ gradle [TAB]
androidDependencies      check                    init                     properties
assemble                 clean                    installDebug             signingReport
assembleDebug            connectedCheck           installDebugTest         tasks
assembleDebugTest        connectedInstrumentTest  installRelease           uninstallAll
assembleRelease          dependencies             lint                     uninstallDebug
build                    dependencyInsight        lintDebug                uninstallDebugTest
buildDependents          deviceCheck              lintRelease              uninstallRelease
buildNeeded              help                     projects                 wrapper
$ gradle c[TAB]
check                    clean                    connectedCheck           connectedInstrumentTest

Gives tab completions relevent to the current Gradle project (if any).

Install

curl -L -s https://gist.github.com/nolanlawson/8694399/raw/gradle-tab-completion.bash \
  -o ~/gradle-tab-completion.bash

Then add to your ~/.bash_profile:

source ~/gradle-tab-completion.bash

It will be kinda slow the first time you use it. But after that, it'll be super fast, because everything's cached based on the md5sum of your build.gradle files.

Credits

Thanks to @ligi for Linux support!

_gradle()
{
local cur=${COMP_WORDS[COMP_CWORD]}
local gradle_cmd='gradle'
if [[ -x ./gradlew ]]; then
gradle_cmd='./gradlew'
fi
if [[ -x ../gradlew ]]; then
gradle_cmd='../gradlew'
fi
local commands=''
local cache_dir="$HOME/.gradle_tabcompletion"
mkdir -p $cache_dir
# TODO: include the gradle version in the checksum? It's kinda slow
#local gradle_version=$($gradle_cmd --version --quiet --no-color | grep '^Gradle ' | sed 's/Gradle //g')
local gradle_files_checksum='';
if [[ -f build.gradle ]]; then # top-level gradle file
if [[ -x `which md5 2 > /dev/null` ]]; then # mac
local all_gradle_files=$(find . -name build.gradle 2>/dev/null)
gradle_files_checksum=$(md5 -q -s "$(md5 -q $all_gradle_files)")
else # linux
gradle_files_checksum=($(find . -name build.gradle | xargs md5sum | md5sum))
fi
else # no top-level gradle file
gradle_files_checksum='no_gradle_files'
fi
if [[ -f $cache_dir/$gradle_files_checksum ]]; then # cached! yay!
commands=$(cat $cache_dir/$gradle_files_checksum)
else # not cached! boo-urns!
commands=$($gradle_cmd --no-color --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')
if [[ ! -z $commands ]]; then
echo $commands > $cache_dir/$gradle_files_checksum
fi
fi
COMPREPLY=( $(compgen -W "$commands" -- $cur) )
}
complete -F _gradle gradle
complete -F _gradle gradlew
complete -F _gradle ./gradlew
@nolanlawson
Copy link
Author

Thanks for the tips everyone; I'll update the script.

@nolanlawson
Copy link
Author

Yeah, it doesn't work with subprojects. Pull requests welcome!

@resamsel
Copy link

Line 21 should have no space between "2" and ">":

if [[ -x `which md5 2> /dev/null` ]]; then # mac

Other than that: great stuff!

@resamsel
Copy link

See my fork for completing tasks from subprojects, though it does only work with this syntax: gradle subproject:build.

@markshiz
Copy link

Because this uses md5sum, I had to run

brew install  md5sha1sum

on Mac.

@ExperimentMonty
Copy link

@nolanlawson Tried to do a pull request to fix the mac issue, but apparently pull requests don't work for gists. The only change was removing a space on line 21 of the script to fix a mac-specific issue. https://gist.github.com/ExperimentMonty/703ce14cb25e989fead4

@rappazzo
Copy link

Regarding the md5: I see you have already addressed it, but you can use git to generate the hash:

gradle_files_checksum=$(find . -name build.gradle 2> /dev/null | sort -u | xargs cat | git hash-object --stdin)

@SansWord
Copy link

I've make gradle tasks list all tasks, since I also need some underlying task.
And hence I've made it consider timestamp of build.gradle, too.

Sometimes even you don't modify build.gradle, tasks will change.
So I need a way to simply touch build.gradle and cache will rebuild.

and this will potentially generate too many cache and never clear it, does it need to clear some cache that is too old?

@SansWord
Copy link

https://gist.github.com/SansWord/0b97cd872d949712b8b0
I've also add mechanism to remove outdated cache:

  1. when reading a cache file, touch it.
  2. every time when this bash is executed, try to remove files that haven't been touched for 7 days.

Question: how to update link in .md so it'll point to my gist file?
I've seen the raw link of bash, but it's much longer then yours.

@mrt181
Copy link

mrt181 commented Sep 8, 2015

Hello, I have added a fix for cygwin. cygwin uses md5sum but the script tries to use md5.

diff --git a/gradle-tab-completion.bash b/gradle-tab-completion.bash
index 485cda2..3ebc35d 100644
--- a/gradle-tab-completion.bash
+++ b/gradle-tab-completion.bash
@@ -18,14 +18,18 @@ _gradle()

   local gradle_files_checksum='';
   if [[ -f build.gradle ]]; then # top-level gradle file
-    if [[ -x `which md5 2 > /dev/null` ]]; then # mac
-      local all_gradle_files=$(find . -name build.gradle 2>/dev/null)
-      gradle_files_checksum=$(md5 -q -s "$(md5 -q $all_gradle_files)")
-    else # linux
+    if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then #cygwin
       gradle_files_checksum=($(find . -name build.gradle | xargs md5sum | md5sum))
+    else
+      if [[ -x `which md5 2 > /dev/null` ]]; then # mac
+        local all_gradle_files=$(find . -name build.gradle 2>/dev/null)
+        gradle_files_checksum=$(md5 -q -s "$(md5 -q $all_gradle_files)")
+      else # linux
+        gradle_files_checksum=($(find . -name build.gradle | xargs md5sum | md5sum))
+      fi
+    else # no top-level gradle file
+      gradle_files_checksum='no_gradle_files'
     fi
-  else # no top-level gradle file
-    gradle_files_checksum='no_gradle_files'
   fi
   if [[ -f $cache_dir/$gradle_files_checksum ]]; then # cached! yay!
     commands=$(cat $cache_dir/$gradle_files_checksum)

@mrt181
Copy link

mrt181 commented Sep 8, 2015

Added fix for deprecated --no-color option and added wrapper task to grep pattern:

diff --git a/gradle-tab-completion.bash b/gradle-tab-completion.bash
index 3ebc35d..e94cf71 100644
--- a/gradle-tab-completion.bash
+++ b/gradle-tab-completion.bash
@@ -34,7 +34,7 @@ _gradle()
   if [[ -f $cache_dir/$gradle_files_checksum ]]; then # cached! yay!
     commands=$(cat $cache_dir/$gradle_files_checksum)
   else # not cached! boo-urns!
-    commands=$($gradle_cmd --no-color --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')
+    commands=$($gradle_cmd --console=plain --quiet tasks | grep -i -e ' - ' -e 'wrapper' | awk '{print $1}' | tr '\n' ' ')
     if [[ ! -z $commands ]]; then
       echo $commands > $cache_dir/$gradle_files_checksum
     fi

@kutzi
Copy link

kutzi commented Oct 12, 2015

There's still a space between 2 and > which breaks it completely for me (on Linux)
After fixing this locally, works great

@mgeis
Copy link

mgeis commented Oct 28, 2015

Nice tool, thanks! One minor bug, it looks like the line that builds the commands only hits on lines that have a hyphen in them (in other words, only tasks with a description visible when you run "gradle tasks" will be added to the cache of commands). For an illustration of what I'm talking about, here's the tail of my execution of the task list.

Other tasks
-----------
install - Installs the 'archives' artifacts into the local Maven repository.
jacocoTestReport

jacocoTestReport does not show up as an available task.

Not sure there's a good fix for this, but figured I'd log it here. The workaround is to edit the cache file, in ~/.gradle_tabcompletion/ and add the missing task name.

@rubensworks
Copy link

The md5 existence check on mac does not work, fixed it in my fork: https://gist.github.com/rubensworks/11ab6892b406bbfccb5c/revisions

@teslacoil
Copy link

@nolanlawson @rubensworks For Mac, I'm betting the 2 > should be 2>, to redirect error messages only. Works for me on mac after making that change.

@calid
Copy link

calid commented Dec 7, 2015

@mgeis this is what I went with to get all tasks (dash or no dash):

$gradle_cmd tasks \
  | sed -e 0,/Build/d -e '/Rules/,$d' \
  | awk 'BEGIN {p=0} /^-+/ {p=1} /^$/ {p=0} /^[^-]/ {if (p) print $1}'

I also incorporated @rappazzo's use of git hash-object to generate the checksum as well as some other tweaks and improvements, such as falling back to default completion if neither gradle or ./gradlew are found:

complete -o default -F _gradle gradle
complete -o default -F _gradle gradlew
complete -o default -F _gradle ./gradlew

A diff of my modified version against the original can be found here:
calid/dotfiles@d8191a1...master#diff-903a6d97f3dbb384ac4758bcbfc2d081

@calid
Copy link

calid commented Dec 7, 2015

Hmm, another gotcha is you need to use gradlew tasks --all, otherwise you'll only get top level tasks

e.g.

task sometask { .. }
build.dependsOn sometask

will result in sometask not showing up if you only use gradle tasks

@unserializable
Copy link

Nice stuff. But when the gradle build file happens to be broken at the time of completion, this fails (and gradle error message from standard error appears on console).

Ended up doing following modifications to task completion for my purposes:

  • send error to /dev/null when Gradle does not complete succesfully (non-zero return code), no completions
  • I prefer autocomplete not to go to the internets, so I used '--offline' flag
  • as '--no-color' option is deprecated in favour of '--console=plain', I used that one
  • as mentioned by @calid, '--all' can be added to tasks to get more completions
  • ... and has been mentioned by @mgeis the completion picked up only tasks that had descriptions which output contained hyphen '-', to work around that I resorted to cut -d' ' -f1 | grep "^[a-z]" to filter tasks from Gradle output (which relies on convention of task names beginning with lowercase alphabetic character -- so far never encountered anything else :))

Snippet of changes:

_gradle() {
    local taskCandidates
    # ...
    else # not cached! boo-urns!
        taskCandidates=$(2>/dev/null $gradle_cmd --console=plain --offline --quiet tasks --all)
        if [ $? -ne 0 ] ; then
           return 13
        fi
        commands=$(echo "$taskCandidates" | cut -d' ' -f1 | grep "^[a-z]" | tr '\n' ' ')
        if [[ ! -z $commands ]]; then
            echo $commands > $cache_dir/$gradle_files_checksum
        else
            return 13
        fi
    # ...
}

@Ea87
Copy link

Ea87 commented Mar 8, 2016

my fork adds complete submodule support + flags + some of the improvements suggested here, if anyone interested

@pythys
Copy link

pythys commented Aug 24, 2016

This script no longer works with Gradle version 3 because it uses the deprecated "--no-color" flag which is removed in Gradle version 3.

To fix this issue, please change it to --console=plain. I show the fix below:

diff --git a/gradle-tab-completion.bash b/gradle-tab-completion.bash
index 485cda2..f1f4bbe 100644
--- a/gradle-tab-completion.bash
+++ b/gradle-tab-completion.bash
@@ -14,7 +14,7 @@ _gradle()
   mkdir -p $cache_dir

   # TODO: include the gradle version in the checksum?  It's kinda slow
-  #local gradle_version=$($gradle_cmd --version --quiet --no-color | grep '^Gradle ' | sed 's/Gradle //g')
+  #local gradle_version=$($gradle_cmd --version --quiet --console=plain | grep '^Gradle ' | sed 's/Gradle //g')

   local gradle_files_checksum='';
   if [[ -f build.gradle ]]; then # top-level gradle file
@@ -30,7 +30,7 @@ _gradle()
   if [[ -f $cache_dir/$gradle_files_checksum ]]; then # cached! yay!
     commands=$(cat $cache_dir/$gradle_files_checksum)
   else # not cached! boo-urns!
-    commands=$($gradle_cmd --no-color --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')
+    commands=$($gradle_cmd --console=plain --quiet tasks | grep ' - ' | awk '{print $1}' | tr '\n' ' ')
     if [[ ! -z $commands ]]; then
       echo $commands > $cache_dir/$gradle_f

@leonschreuder
Copy link

leonschreuder commented Jan 21, 2017

Since I use gradle a lot and the gist is a little outdated, I've cloned the project to Github, added testing to fix any bugs, and improved the caching to support multiple repos.
https://github.com/meonlol/gradle-tab-completion

I'll add all other proposed changes to make one super-hyper-compatible version:
[+] Mac & Linux support
[+] per-project caching
[+] support tasks without descriptions
[+] support all tasks with --all
[+] since the tasks are local, we can go--offline
[ ] gitbash & cygwin support
[ ] commandline flags support
[ ] module support

@iamtodor
Copy link

iamtodor commented Feb 27, 2017

@nolanlawson, could you please help me?
I got unexpected logs while using $ gradle [TAB]:

Here is the logs, probably you can figure out what's going on here:

iamtodors-MBP:custom-sensor-belt-view iamtodor$ gradle bash: md5sum: command not found
xargs: md5sum: No such file or directory
bash: /Users/iamtodor/.gradle_tabcompletion/: Is a directory

@dodalovic
Copy link

Doesn't provide any assistance for me. Using either with zsh or bash on MacOS - doesn't work.

@iamtodor
Copy link

iamtodor commented Mar 4, 2017

@dodalovic the same situation

@matsev
Copy link

matsev commented Mar 14, 2017

As of last week, Gradle has official support for tab completion:
Twitter announcement: https://twitter.com/gradle/status/839166051199787008
GitHub: https://github.com/gradle/gradle-completion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment