diff --git a/completions/Makefile.am b/completions/Makefile.am index d528da6f507..5d3a29a3e98 100644 --- a/completions/Makefile.am +++ b/completions/Makefile.am @@ -53,6 +53,7 @@ bashcomp_DATA = 2to3 \ chrpath \ _chsh \ cksfv \ + clang \ cleanarch \ clisp \ clone_member \ diff --git a/completions/clang b/completions/clang new file mode 100644 index 00000000000..ee1b84e17dc --- /dev/null +++ b/completions/clang @@ -0,0 +1,67 @@ +# clang(1) completion -*- shell-script -*- +_clang() +{ + local cur prev words cword arg flags prev2 + _init_completion || return + + if [[ $cword > 1 ]]; then + prev2="${COMP_WORDS[$cword - 2]}" + fi + + # Clang want to know if -cc1 or -Xclang option is specified or not, because we don't want to show + # cc1 options otherwise. + if [[ "${COMP_WORDS[1]}" == "-cc1" || "$prev" == "-Xclang" ]]; then + arg="#" + fi + + # bash always separates '=' as a token even if there's no space before/after '='. + # On the other hand, '=' is just a regular character for clang options that + # contain '='. For example, "-stdlib=" is defined as is, instead of "-stdlib" and "=". + # So, we need to partially undo bash tokenization here for integrity. + if [[ "$cur" == -* ]]; then + # -foo + arg="$arg$cur" + elif [[ "$prev" == -* && "$cur" == '=' ]]; then + # -foo= + arg="$arg$prev=," + elif [[ "$cur" == -*= ]]; then + # -foo= + arg="$arg$cur," + elif [[ "$prev" == -* ]]; then + # -foo or -foo bar + arg="$arg$prev,$cur" + elif [[ "$prev2" == -* && "$prev" == '=' ]]; then + # -foo=bar + arg="$arg$prev2=,$cur" + elif [[ ${cur: -1} != '=' && ${cur/=} != $cur ]]; then + # -foo=bar + arg="$arg${cur%=*}=,${cur#*=}" + fi + + # expand ~ to $HOME + eval local path=${COMP_WORDS[0]} + flags=$( "$path" --autocomplete="$arg" 2>/dev/null | sed -e 's/\t.*//' ) + # If clang is old that it does not support --autocomplete, + # fall back to the filename completion. + if [[ $? -ne 0 ]]; then + _filedir + return + fi + + # When clang does not emit any possible autocompletion, or user pushed tab after " ", + # just autocomplete files. + if [[ "$flags" == $'\n' || "$arg" == "" ]]; then + # If -foo= and there was no possible values, autocomplete files. + [[ "$cur" == '=' || "$cur" == -*= ]] && cur="" + _filedir + elif [[ "$cur" == '=' ]]; then + COMPREPLY=( $( compgen -W "$flags" -- "") ) + else + # Bash automatically appends a space after '=' by default. + # Disable it so that it works nicely for options in the form of -foo=bar. + [[ "${flags: -1}" == '=' ]] && compopt -o nospace 2> /dev/null + COMPREPLY=( $( compgen -W "$flags" -- "$cur" ) ) + fi +} && +complete -F _clang clang +# ex: filetype=sh diff --git a/test/completion/clang.exp b/test/completion/clang.exp new file mode 100644 index 00000000000..069702d7e43 --- /dev/null +++ b/test/completion/clang.exp @@ -0,0 +1 @@ +assert_source_completions clang diff --git a/test/docker/Dockerfile-ubuntu14 b/test/docker/Dockerfile-ubuntu14 index dc84ea47058..97761acbca0 100644 --- a/test/docker/Dockerfile-ubuntu14 +++ b/test/docker/Dockerfile-ubuntu14 @@ -39,6 +39,7 @@ RUN dpkg --add-architecture i386 && \ chrpath \ cksfv \ clisp \ + clang \ cowsay \ cppcheck \ cryptsetup-bin \ diff --git a/test/lib/completions/clang.exp b/test/lib/completions/clang.exp new file mode 100644 index 00000000000..08e6822ee7b --- /dev/null +++ b/test/lib/completions/clang.exp @@ -0,0 +1,18 @@ +proc setup {} { + save_env +} + + +proc teardown {} { + assert_env_unmodified +} + + +setup + + +assert_complete_any "clang " +sync_after_int + + +teardown