@@ -98,9 +98,8 @@ _bazel__get_workspace_path() {
9898 echo $workspace
9999}
100100
101-
102101# Find the current piece of the line to complete, but only do word breaks at
103- # certain characters. In particular, ignore these: "':=
102+ # certain characters. In particular, ignore these: "':=@
104103# This method also takes into account the current cursor position.
105104#
106105# Works with both bash 3 and 4! Bash 3 and 4 perform different word breaks when
@@ -109,13 +108,14 @@ _bazel__get_workspace_path() {
109108_bazel__get_cword () {
110109 local cur=${COMP_LINE: 0: $COMP_POINT }
111110 # This expression finds the last word break character, as defined in the
112- # COMP_WORDBREAKS variable, but without '=' or ':', which is not preceeded by
113- # a slash. Quote characters are also excluded.
111+ # COMP_WORDBREAKS variable, but without '@', ' =' or ':', which is not
112+ # preceded by a slash. Quote characters are also excluded.
114113 local wordbreaks=" $COMP_WORDBREAKS "
115114 wordbreaks=" ${wordbreaks// \' / } "
116115 wordbreaks=" ${wordbreaks// \" / } "
117116 wordbreaks=" ${wordbreaks//:/ } "
118117 wordbreaks=" ${wordbreaks// =/ } "
118+ wordbreaks=" ${wordbreaks//@/ } "
119119 local word_start=$( expr " $cur " : ' .*[^\][' " ${wordbreaks} " ' ]' )
120120 echo " ${cur: $word_start } "
121121}
@@ -281,6 +281,111 @@ _bazel__expand_package_name() {
281281 done
282282}
283283
284+ # Usage: _bazel__filter_repo_mapping <filter> <field>
285+ #
286+ # Returns all entries of the main repo's repository mapping whose apparent repo
287+ # name, followed by a double quote, matches the given filter. To return the
288+ # matching apparent names, set field to 2. To return the matching canonical
289+ # names, set field to 4.
290+ # Note: Instead of returning an empty canonical name for the main repository,
291+ # this function returns the string "_main" so that this case can be
292+ # distinguished from that of no match.
293+ _bazel__filter_repo_mapping () {
294+ local filter=$1 field=$2
295+ # 1. dump_repo_mapping '' returns a single line consisting of a minified JSON
296+ # object.
297+ # 2. Transform JSON to have lines of the form "apparent_name":"canonical_name".
298+ # 3. Filter by apparent repo name.
299+ # 4. Replace an empty canonical name with "_main".
300+ # 5. Cut out either the apparent or canonical name.
301+ ${BAZEL} mod dump_repo_mapping ' ' --noshow_progress 2> /dev/null |
302+ tr ' {},' ' \n' |
303+ " grep" " ^\" ${filter} " |
304+ sed ' s|:""$|:"_main"|' |
305+ cut -d' "' -f${field}
306+ }
307+
308+ # Usage: _bazel__expand_repo_name <current>
309+ #
310+ # Returns completions for apparent repository names. Each line is of the form
311+ # @apparent_name or @apparent_name//, where apparent_name starts with current.
312+ _bazel__expand_repo_name () {
313+ local current=$1
314+ # If current exactly matches a repo name, also provide the @current//
315+ # completion so that users can tab through to package completion, but also
316+ # complete just the shorthand for "@repo_name//:repo_name".
317+ _bazel__filter_repo_mapping " ${current#@ } " 2 |
318+ sed ' s|^|@|' |
319+ sed " s|^${current} \$ |${current} ${current} //|"
320+ }
321+
322+ # Usage: _bazel__repo_root <workspace> <repo>
323+ #
324+ # Returns the absolute path to the root of the repository identified by the
325+ # repository part <repo> of a label. <repo> can be either of the form
326+ # "@apparent_name" or "@@canonical_name" and may also refer to the main
327+ # repository.
328+ _bazel__repo_root () {
329+ local workspace=$1 repo=$2
330+ local canonical_repo
331+ if [[ " $repo " == @@ ]]; then
332+ # Match the sentinel value for the main repository used by
333+ # _bazel__filter_repo_mapping.
334+ canonical_repo=_main
335+ elif [[ " $repo " =~ ^@@ ]]; then
336+ # Canonical repo names should not go through repo mapping.
337+ canonical_repo=${repo#@@ }
338+ else
339+ canonical_repo=$( _bazel__filter_repo_mapping " ${repo#@ } \" " 4)
340+ fi
341+ if [ -z " $canonical_repo " ]; then
342+ return
343+ fi
344+ if [ " $canonical_repo " == " _main" ]; then
345+ echo " $workspace "
346+ return
347+ fi
348+ local output_base=" $( ${BAZEL} info output_base --noshow_progress 2> /dev/null) "
349+ if [ -z " $output_base " ]; then
350+ return
351+ fi
352+ local repo_root=" $output_base /external/$canonical_repo "
353+ echo " $repo_root "
354+ }
355+
356+ # Usage: _bazel__expand_package_name <workspace> <current> <label-type>
357+ #
358+ # Expands packages under the potentially external repository pointed to by
359+ # <current>, which is expected to start with "@repo//".
360+ _bazel__expand_external_package_name () {
361+ local workspace=$1 current=$2 label_syntax=$3
362+ local repo=$( echo " $current " | cut -f1 -d/)
363+ local package=$( echo " $current " | cut -f3- -d/)
364+ local repo_root=$( _bazel__repo_root " $workspace " " $repo " )
365+ if [ -z " $repo_root " ]; then
366+ return
367+ fi
368+ _bazel__expand_package_name " $repo_root " " " " $package " " $label_syntax " |
369+ sed " s|^|${repo} //|"
370+ }
371+
372+ # Usage: _bazel__expand_rules_in_external_package <workspace> <current>
373+ # <label-type>
374+ #
375+ # Expands rule names in the potentially external package pointed to by
376+ # <current>, which is expected to start with "@repo//some/pkg:".
377+ _bazel__expand_rules_in_external_package () {
378+ local workspace=$1 current=$2 label_syntax=$3
379+ local repo=$( echo " $current " | cut -f1 -d/)
380+ local package=$( echo " $current " | cut -f3- -d/ | cut -f1 -d:)
381+ local name=$( echo " $current " | cut -f2 -d:)
382+ local repo_root=$( _bazel__repo_root " $workspace " " $repo " )
383+ if [ -z " $repo_root " ]; then
384+ return
385+ fi
386+ _bazel__expand_rules_in_package " $repo_root " " " " //$package :$name " " $label_syntax "
387+ }
388+
284389# Usage: _bazel__expand_target_pattern <workspace> <displacement>
285390# <word> <label-syntax>
286391#
@@ -290,6 +395,26 @@ _bazel__expand_package_name() {
290395_bazel__expand_target_pattern () {
291396 local workspace=$1 displacement=$2 current=$3 label_syntax=$4
292397 case " $current " in
398+ @* //* :* ) # Expand rule names within external repository.
399+ _bazel__expand_rules_in_external_package " $workspace " " $current " " $label_syntax "
400+ ;;
401+ @* /* ) # Expand package names within external repository.
402+ # Append a second slash after the repo name before performing completion
403+ # if there is no second slash already.
404+ if [[ " $current " =~ ^@[^/]* /$ ]]; then
405+ current=" $current /"
406+ fi
407+ _bazel__expand_external_package_name " $workspace " " $current " " $label_syntax "
408+ ;;
409+ @* ) # Expand external repository names.
410+ # Do not expand canonical repository names: Users are not expected to
411+ # compose them manually and completing them based on the contents of the
412+ # external directory has a high risk of returning stale results.
413+ if [[ " $current " =~ ^@@ ]]; then
414+ return
415+ fi
416+ _bazel__expand_repo_name " $current "
417+ ;;
293418 //* :* ) # Expand rule names within package, no displacement.
294419 if [ " ${label_syntax} " = " label-package" ]; then
295420 compgen -S " " -W " BUILD" " $( echo current | cut -f ' :' -d2) "
0 commit comments