From 54f9073959d2dcc78f92ccb49082fb862fc76256 Mon Sep 17 00:00:00 2001 From: Marlon Richert Date: Fri, 1 Apr 2022 09:15:28 +0300 Subject: [PATCH] Improve completion of Gradle tasks * Adjust the pattern for matching tasks. The old one was too strict, causing it to filter out perfectly valid tasks. * Add completion of subproject names. * Prevent subproject tasks from overwriting the main task description. * Require `:` prefix for showing subprojects. * Require a subproject prefix before showing subproject tasks. * Standardize the use of completion tags. * Rebuild the cache if it can't be read or doesn't contain the required arrays. --- Completion/Unix/Command/_gradle | 114 +++++++++++++++++--------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/Completion/Unix/Command/_gradle b/Completion/Unix/Command/_gradle index a1d9381cb..c1f7c05ee 100644 --- a/Completion/Unix/Command/_gradle +++ b/Completion/Unix/Command/_gradle @@ -1,13 +1,24 @@ #compdef gradle gradlew -local curcontext="$curcontext" ret=1 state state_descr line -local gradle_inspect=yes cache_policy tag_order filter= -local -A opt_args -local -aU gradle_group_tasks gradle_all_tasks tasks +local -i ret=1 +local curcontext="$curcontext" state state_descr outputline +local gradle_inspect=yes gradle_buildfile cache_policy cache_name tag_order filter +local -A opt_args task_map +local -aU gradle_subprojects gradle_tasks tasks +local -a match mbegin mend -# Set the caching policy to invalidate cache if the build file is newer than the cache. _gradle_caching_policy() { - [[ $gradle_buildfile -nt $1 ]] + # Invalidate the cache if it cannot be read. + [[ -r "$1" ]] || + return 0 + + # Invalidate the cache if it's older than the build file. + [[ $gradle_buildfile -nt $1 ]] && + return + + # Invalidate the cache if it doesn't contain the required arrays. + local cache="$( < $1 )" + [[ "$cache" != *'gradle_subprojects=('*')'* || "$cache" != *'gradle_tasks=('*')'* ]] } zstyle -s ":completion:*:*:$service:*" cache-policy cache_policy || \ @@ -59,76 +70,73 @@ _arguments -C \ '(-)--stop[Stops the Gradle daemon if it is running.]' \ {-u,--no-search-upward}"[Don't search in parent folders for a settings.gradle file.]" \ '(-)'{-v,--version}'[Print version info.]' \ - {-x,--exclude-task}'[Specify a task to be excluded from execution.]:task to exclude:->alltask' \ + {-x,--exclude-task}'[Specify a task to be excluded from execution.]:task to exclude:->task' \ '*:task:->task' \ && ret=0 -if [[ $words[CURRENT] != -* ]]; then +if [[ $state == task && ! -prefix - ]]; then + # : runs in the root project only. + # :: is the same as : (without the leading colon). + compset -P \: + if [[ $gradle_inspect == yes ]]; then # If a build file is specified after '-b' or '--build-file', use this file. Otherwise, # default is the file 'build.gradle' in the current directory. - local gradle_buildfile=${${(v)opt_args[(i)-b|--build-file]}:-build.gradle} + gradle_buildfile=${${(v)opt_args[(i)-b|--build-file]}:-build.gradle} if [[ -f $gradle_buildfile ]]; then # Cache name is constructed from the absolute path of the build file. - local cache_name=${${gradle_buildfile:a}//[^[:alnum:]]/_} + cache_name=${${gradle_buildfile:a}//[^[:alnum:]]/_} + if _cache_invalid $cache_name || ! _retrieve_cache $cache_name; then zle -R "Generating cache from $gradle_buildfile" - local outputline - local -a match mbegin mend + # Run gradle/gradlew and retrieve possible tasks. for outputline in ${(f)"$($service --build-file $gradle_buildfile -q tasks --all)"}; do - # Subproject tasks are prefixed with one or more instances of [[:alnum:]]##\: - # Leaving out the prefix runs the task in all subprojects where it exists. - if [[ $outputline == - [[:blank:]]#(#b)([[:alnum:]]##\:)#([[:alnum:]]##)' - '(*) ]]; then - # The descriptions of main tasks start at beginning of line, descriptions of - # secondary tasks are indented. - if [[ $outputline == [[:alnum:]]* ]]; then - gradle_group_tasks+=( - "$match[-2]:$match[-1]" - "${${(j::)match[1,-2]}//:/\:}:$match[-1]" - ) + + # Tasks and subprojects each start with a lowercase letter, but whereas tasks are in camelCase, each + # subproject consists of one or more sections of kebab-case, with each section ending in a ':'. + # A subproject task is a task prefixed with a subproject and runs in that project only. + # Specifying a task without a subproject prefix runs the task in all subprojects where it exists. + # Tasks prefixed by whitespace are dependencies of the task above them and should be ignored. + if [[ $outputline == (#b)([[:lower:]][-[:lower:][:digit:]]#\:)#([[:lower:]][[:alnum:]]#)(|' - '*) ]] + then + task=$match[-2] + task_descr=${match[-1]# - } + shift -p 2 match + subproject=${(j::)match//:/'\:'} + + if [[ -n $subproject ]]; then + gradle_subprojects+=( ${subproject%'\:'} ) + task_map[$subproject$task]=$task_descr + + # We cannot count on the description of a subproject task to be representative of the task + # in general. + : ${task_map[$task]=} else - gradle_all_tasks+=( - "${${(j::)match[1,-2]}//:/\:}:$match[-1]" - ) + task_map[$task]=$task_descr fi fi done - _store_cache $cache_name gradle_group_tasks gradle_all_tasks + printf -v gradle_tasks '%s:%s' "${(kv@)task_map}" + _store_cache $cache_name gradle_subprojects gradle_tasks fi - filter= - zstyle -T ":completion:${curcontext}:tasks" prefix-needed && - [[ $PREFIX != ([[:alnum:]]##\:)## ]] && - filter+="*\:*" - if [[ $state == task ]]; then - _tags gradle_group gradle_all - while _tags; do - # Offer main tasks and secondary tasks in different tags. - if _requested gradle_group; then - tasks=( ${gradle_group_tasks:#$~filter} ) - _describe -t tasks 'group task' tasks && - ret=0 - fi - if _requested gradle_all; then - tasks=( ${gradle_all_tasks:#$~filter} ) - _describe -t tasks 'secondary task' tasks && - ret=0 - fi - (( ret )) || break - done - elif [[ $state == alltask ]]; then - # After '--exclude-task', we don't make a distinction between main tasks and - # secondary tasks. - tasks=( ${gradle_group_tasks:#$~filter} ${gradle_all_tasks:#$~filter} ) - _describe -t tasks 'task' tasks && + if [[ $IPREFIX == *: ]] || ! zstyle -T ":completion:${curcontext}:subprojects" prefix-needed; then + _describe -t subprojects 'gradle subproject' gradle_subprojects -S \: && ret=0 fi + if [[ $PREFIX == *:* ]] || ! zstyle -T ":completion:${curcontext}:tasks" prefix-needed; then + tasks=( $gradle_tasks[@] ) + else + # If no subproject is specified, then filter out all subproject tasks. + tasks=( ${gradle_tasks[@]:#*'\:'*} ) + fi + _describe -t tasks 'gradle task' tasks && + ret=0 fi else - _describe 'built-in task' '( + _describe -t tasks 'built-in task' '( "dependencies:Displays all dependencies declared in root project." "dependencyInsight:Displays the insight into a specific dependency in root project." "help:Displays a help message." -- 2.35.1