Skip to content

Make target completion doesn't work with subdirectories #544

Closed
@PhilipRoman

Description

@PhilipRoman

Describe the bug

Make target completion doesn't work when target files are in subdirectories.

To reproduce

There are two behaviours that happen here. Sample makefile:

.PHONY: abc/xyz

abc/xyz:
	mkdir -p abc
	date > $@

For clarity, i've marked spaces with ·

  1. When typing make·TAB the result is make·abc/·
  2. When typing make·abc/TAB the result is make·xyz·

Expected behavior

Both described cases should auto complete to make·abc/xyz· because it is unambiguous.

Versions (please complete the following information)

  • Operating system name/distribution and version: Artix Linux on WSL
  • bash version: 5.1.8(1)-release
  • bash-completion version: 2.11 (md5sum completions/make = 84866af8ff59c9e1698f90a8f3a67f22)

Additional context

Debug trace

+++ _on_command_start
�[?2004h�[38;5;214mᛋ �[97;1mmake abc/+ local cur prev words cword split
+ _init_completion -s
+ local exclude= flag outx errx inx OPTIND=1
+ getopts n:e:o:i:s flag -s
+ case $flag in
+ split=false
+ exclude+==
+ getopts n:e:o:i:s flag -s
+ COMPREPLY=()
+ local 'redir=@(?([0-9])<|?([0-9&])>?(>)|>&)'
+ _get_comp_words_by_ref -n '=<>&' cur prev words cword
+ local exclude flag i OPTIND=1
+ words=()
+ local cur cword words
+ upargs=()
+ upvars=()
+ local upargs upvars vcur vcword vprev vwords
+ getopts c:i:n:p:w: flag -n '=<>&' cur prev words cword
+ case $flag in
+ exclude='=<>&'
+ getopts c:i:n:p:w: flag -n '=<>&' cur prev words cword
+ [[ 6 -ge 3 ]]
+ case ${!OPTIND} in
+ vcur=cur
+ (( OPTIND += 1 ))
+ [[ 6 -ge 4 ]]
+ case ${!OPTIND} in
+ vprev=prev
+ (( OPTIND += 1 ))
+ [[ 6 -ge 5 ]]
+ case ${!OPTIND} in
+ vwords=words
+ (( OPTIND += 1 ))
+ [[ 6 -ge 6 ]]
+ case ${!OPTIND} in
+ vcword=cword
+ (( OPTIND += 1 ))
+ [[ 6 -ge 7 ]]
+ __get_cword_at_cursor_by_ref '=<>&' words cword cur
+ words=()
+ local cword words
+ __reassemble_comp_words_by_ref '=<>&' words cword
+ local exclude i j line ref
+ [[ -n =<>& ]]
+ exclude='[=<>&]'
+ printf -v cword %s 1
+ [[ -v exclude ]]
+ line='make abc/'
+ (( i = 0, j = 0 ))
+ (( i < 2 ))
+ [[ 0 -gt 0 ]]
+ ref='words[0]'
+ printf -v 'words[0]' %s make
+ line=' abc/'
+ (( i == COMP_CWORD ))
+ (( i++, j++ ))
+ (( i < 2 ))
+ [[ 1 -gt 0 ]]
+ [[ abc/ == +([=<>&]) ]]
+ ref='words[1]'
+ printf -v 'words[1]' %s abc/
+ line=
+ (( i == COMP_CWORD ))
+ printf -v cword %s 1
+ (( i++, j++ ))
+ (( i < 2 ))
+ (( i == COMP_CWORD ))
+ local i cur= index=9 'lead=make abc/'
+ [[ 9 -gt 0 ]]
+ [[ -n make abc/ ]]
+ [[ -n makeabc/ ]]
+ cur='make abc/'
+ (( i = 0 ))
+ (( i <= cword ))
+ [[ 9 -ge 4 ]]
+ [[ make != \m\a\k\e ]]
+ (( i < cword ))
+ local old_size=9
+ cur=' abc/'
+ local new_size=5
+ (( index -= old_size - new_size ))
+ (( ++i ))
+ (( i <= cword ))
+ [[ 5 -ge 4 ]]
+ [[  abc != \a\b\c\/ ]]
+ cur=abc/
+ (( index > 0 ))
+ (( index-- ))
+ [[ 4 -ge 4 ]]
+ [[ abc/ != \a\b\c\/ ]]
+ (( i < cword ))
+ (( ++i ))
+ (( i <= cword ))
+ [[ -n abc/ ]]
+ [[ ! -n abc/ ]]
+ (( index < 0 ))
+ local words cword cur
+ _upvars -a2 words make abc/ -v cword 1 -v cur abc/
+ (( 10 ))
+ (( 10 ))
+ case $1 in
+ [[ -n 2 ]]
+ printf %d 2
+ [[ -n words ]]
+ unset -v words
+ eval 'words=("${@:3:2}")'
++ words=("${@:3:2}")
+ shift 4
+ (( 6 ))
+ case $1 in
+ [[ -n cword ]]
+ unset -v cword
+ eval 'cword="$3"'
++ cword=1
+ shift 3
+ (( 3 ))
+ case $1 in
+ [[ -n cur ]]
+ unset -v cur
+ eval 'cur="$3"'
++ cur=abc/
+ shift 3
+ (( 0 ))
+ [[ -v vcur ]]
+ upvars+=("$vcur")
+ upargs+=(-v $vcur "$cur")
+ [[ -v vcword ]]
+ upvars+=("$vcword")
+ upargs+=(-v $vcword "$cword")
+ [[ -v vprev ]]
+ [[ 1 -ge 1 ]]
+ upvars+=("$vprev")
+ upargs+=(-v $vprev "${words[cword - 1]}")
+ [[ -v vwords ]]
+ upvars+=("$vwords")
+ upargs+=(-a${#words[@]} $vwords ${words+"${words[@]}"})
+ (( 4 ))
+ local cur cword prev words
+ _upvars -v cur abc/ -v cword 1 -v prev make -a2 words make abc/
+ (( 13 ))
+ (( 13 ))
+ case $1 in
+ [[ -n cur ]]
+ unset -v cur
+ eval 'cur="$3"'
++ cur=abc/
+ shift 3
+ (( 10 ))
+ case $1 in
+ [[ -n cword ]]
+ unset -v cword
+ eval 'cword="$3"'
++ cword=1
+ shift 3
+ (( 7 ))
+ case $1 in
+ [[ -n prev ]]
+ unset -v prev
+ eval 'prev="$3"'
++ prev=make
+ shift 3
+ (( 4 ))
+ case $1 in
+ [[ -n 2 ]]
+ printf %d 2
+ [[ -n words ]]
+ unset -v words
+ eval 'words=("${@:3:2}")'
++ words=("${@:3:2}")
+ shift 4
+ (( 0 ))
+ _variables
+ [[ abc/ =~ ^(\$(\{[!#]?)?)([A-Za-z0-9_]*)$ ]]
+ [[ abc/ =~ ^(\$\{[#!]?)([A-Za-z0-9_]*)\[([^]]*)$ ]]
+ [[ abc/ =~ ^\$\{[#!]?[A-Za-z0-9_]*\[.*]$ ]]
+ case ${prev-} in
+ return 1
+ [[ abc/ == @(?([0-9])<|?([0-9&])>?(>)|>&)* ]]
+ [[ make == @(?([0-9])<|?([0-9&])>?(>)|>&) ]]
+ local i skip
+ (( i = 1 ))
+ (( i < 2 ))
+ [[ abc/ == @(?([0-9])<|?([0-9&])>?(>)|>&)* ]]
+ (( i++ ))
+ (( 1 ))
+ (( i < 2 ))
+ (( cword <= 0 ))
+ prev=make
+ [[ -n false ]]
+ _split_longopt
+ [[ abc/ == --?*=* ]]
+ return 1
+ return 0
+ makef_dir=('-C' '.')
+ local makef makef_dir i
+ case $prev in
+ false
+ [[ abc/ == -* ]]
+ [[ abc/ == *=* ]]
+ (( i = 1 ))
+ (( i < 2 ))
+ [[ abc/ == -@(C|-directory) ]]
+ (( i++ ))
+ (( i < 2 ))
+ (( i = 1 ))
+ (( i < 2 ))
+ [[ abc/ == -@(f|-?(make)file) ]]
+ (( i++ ))
+ (( i < 2 ))
+ local mode=--
+ (( COMP_TYPE != 9 ))
+ mode=-d
++ _make_target_extract_script -d abc/
++ local mode=-d
++ shift
++ local prefix=abc/
+++ command sed 's/[][\,.*^$(){}?+|/]/\\&/g'
++ local 'prefix_pat=abc\/'
++ local basename=
++ local dirname_len=4
++ [[ -d == -d ]]
++ local 'output=\2'
++ cat
++ [[ -z abc\/ ]]
++ [[ abc\/ == */ ]]
++ cat
++ cat
+ local 'IFS= 	
' 'script=    1,/^# * Make data base/           d;        # skip any makefile output
    /^# * Finished Make data base/,/^# * Make data base/{
                                      d;        # skip any makefile output
    }
    /^# * Variables/,/^# * Files/     d;        # skip until files section
    /^# * Not a target/,/^$/          d;        # skip not target blocks
    /^abc\//,/^$/!            d;        # skip anything user dont want

    # The stuff above here describes lines that are not
    #  explicit targets or not targets other than special ones
    # The stuff below here decides whether an explicit target
    #  should be output.

    /^# * File is an intermediate prerequisite/ {
      s/^.*$//;x;                               # unhold target
      d;                                        # delete line
    }

    /^$/ {                                      # end of target block
      x;                                        # unhold target
      /^$/d;                                    # dont print blanks
      s|^\(.\{4\}\)\(.\{0\}[^:/]*/\{0,1\}\)[^:]*:.*$|\2|p;
      d;                                        # hide any bugs
    }

    # This pattern includes a literal tab character as \t is not a portable
    # representation and fails with BSD sed
    /^[^#	:%]\{1,\}:/ {         # found target block
      /^\.PHONY:/                 d;            # special target
      /^\.SUFFIXES:/              d;            # special target
      /^\.DEFAULT:/               d;            # special target
      /^\.PRECIOUS:/              d;            # special target
      /^\.INTERMEDIATE:/          d;            # special target
      /^\.SECONDARY:/             d;            # special target
      /^\.SECONDEXPANSION:/       d;            # special target
      /^\.DELETE_ON_ERROR:/       d;            # special target
      /^\.IGNORE:/                d;            # special target
      /^\.LOW_RESOLUTION_TIME:/   d;            # special target
      /^\.SILENT:/                d;            # special target
      /^\.EXPORT_ALL_VARIABLES:/  d;            # special target
      /^\.NOTPARALLEL:/           d;            # special target
      /^\.ONESHELL:/              d;            # special target
      /^\.POSIX:/                 d;            # special target
      /^\.NOEXPORT:/              d;            # special target
      /^\.MAKE:/                  d;            # special target
      /^abc\/[^a-zA-Z0-9]/d;            # convention for hidden tgt
      h;                                        # hold target
      d;                                        # delete line
    }'
+ COMPREPLY=($(LC_ALL=C             $1 -npq __BASH_MAKE_COMPLETION__=1             ${makef+"${makef[@]}"} "${makef_dir[@]}" .DEFAULT 2>/dev/null |
            command sed -ne "$script"))
++ LC_ALL=C
++ make -npq __BASH_MAKE_COMPLETION__=1 -C . .DEFAULT
++ command sed -ne '    1,/^# * Make data base/           d;        # skip any makefile output
    /^# * Finished Make data base/,/^# * Make data base/{
                                      d;        # skip any makefile output
    }
    /^# * Variables/,/^# * Files/     d;        # skip until files section
    /^# * Not a target/,/^$/          d;        # skip not target blocks
    /^abc\//,/^$/!            d;        # skip anything user dont want

    # The stuff above here describes lines that are not
    #  explicit targets or not targets other than special ones
    # The stuff below here decides whether an explicit target
    #  should be output.

    /^# * File is an intermediate prerequisite/ {
      s/^.*$//;x;                               # unhold target
      d;                                        # delete line
    }

    /^$/ {                                      # end of target block
      x;                                        # unhold target
      /^$/d;                                    # dont print blanks
      s|^\(.\{4\}\)\(.\{0\}[^:/]*/\{0,1\}\)[^:]*:.*$|\2|p;
      d;                                        # hide any bugs
    }

    # This pattern includes a literal tab character as \t is not a portable
    # representation and fails with BSD sed
    /^[^#	:%]\{1,\}:/ {         # found target block
      /^\.PHONY:/                 d;            # special target
      /^\.SUFFIXES:/              d;            # special target
      /^\.DEFAULT:/               d;            # special target
      /^\.PRECIOUS:/              d;            # special target
      /^\.INTERMEDIATE:/          d;            # special target
      /^\.SECONDARY:/             d;            # special target
      /^\.SECONDEXPANSION:/       d;            # special target
      /^\.DELETE_ON_ERROR:/       d;            # special target
      /^\.IGNORE:/                d;            # special target
      /^\.LOW_RESOLUTION_TIME:/   d;            # special target
      /^\.SILENT:/                d;            # special target
      /^\.EXPORT_ALL_VARIABLES:/  d;            # special target
      /^\.NOTPARALLEL:/           d;            # special target
      /^\.ONESHELL:/              d;            # special target
      /^\.POSIX:/                 d;            # special target
      /^\.NOEXPORT:/              d;            # special target
      /^\.MAKE:/                  d;            # special target
      /^abc\/[^a-zA-Z0-9]/d;            # convention for hidden tgt
      h;                                        # hold target
      d;                                        # delete line
    }'
+ [[ -d != -d ]]
����xyz 
�[C�[C�[Kset +x && exec 2>/dev/tty
�[?2004l
++ _on_command_start
+ set +x

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions