Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,16 @@ if (NOT WIN32)
# Not ready for Windows yet
#

# Install shell completion scripts for the supported shells. The files are
# renamed to match each shell's expected completion-script name.
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/bash-autocomplete.sh
DESTINATION share/bash-completion/completions RENAME insights)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/zsh-autocomplete.zsh
DESTINATION share/zsh/site-functions RENAME _insights)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/fish-autocomplete.fish
DESTINATION share/fish/vendor_completions.d RENAME insights.fish)


# additional includes we need when building outside the llvm-folder
if (BUILD_INSIGHTS_OUTSIDE_LLVM)
if(INSIGHTS_STRIP)
Expand Down Expand Up @@ -644,6 +654,34 @@ else()
find_package(Python3 COMPONENTS Interpreter )
endif()

# Detect zsh and fish for the shell-completion tests. They are optional; the
# corresponding tests are skipped when the shell is not installed.
find_program(ZSH_EXECUTABLE zsh)
find_program(FISH_EXECUTABLE fish)

# Build the optional completion-test commands up front. When the shell is
# missing the variable is left empty, so the COMMAND in the test targets
# expands to nothing (no subcommand is generated).
if(ZSH_EXECUTABLE)
set(ZSH_COMPLETION_TEST
${CMAKE_CURRENT_SOURCE_DIR}/tests/test-zsh-completion.sh
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tests/shell
${CMAKE_CURRENT_SOURCE_DIR}/scripts/zsh-autocomplete.zsh)
else()
set(ZSH_COMPLETION_TEST "")
endif()

if(FISH_EXECUTABLE)
set(FISH_COMPLETION_TEST
${CMAKE_CURRENT_SOURCE_DIR}/tests/test-fish-completion.sh
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/tests/shell
${CMAKE_CURRENT_SOURCE_DIR}/scripts/fish-autocomplete.fish)
else()
set(FISH_COMPLETION_TEST "")
endif()

if (NOT Python3_FOUND)
message(WARNING "Could not find the program Python3. Target tests disabled.")
else()
Expand All @@ -657,6 +695,8 @@ else()
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/testSTDIN.sh ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_NAME:insights>
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/testInvalidOption.sh ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_NAME:insights>
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell/test-bash-completion.sh ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell ${CMAKE_CURRENT_SOURCE_DIR}/scripts/bash-autocomplete.sh ${TEST_FAILURE_IS_OK}
COMMAND ${ZSH_COMPLETION_TEST}
COMMAND ${FISH_COMPLETION_TEST}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/$<TARGET_FILE_NAME:insights> ${CMAKE_CURRENT_SOURCE_DIR}/tests/runTest.py ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell/test-bash-completion.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
COMMENT "Running tests" VERBATIM
Expand All @@ -669,6 +709,8 @@ if (NOT WIN32)
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/testSTDIN.sh ${CMAKE_CURRENT_BINARY_DIR}/insights
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/testInvalidOption.sh ${CMAKE_CURRENT_BINARY_DIR}/insights
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell/test-bash-completion.sh ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell ${CMAKE_CURRENT_SOURCE_DIR}/scripts/bash-autocomplete.sh
COMMAND ${ZSH_COMPLETION_TEST}
COMMAND ${FISH_COMPLETION_TEST}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/insights ${CMAKE_CURRENT_SOURCE_DIR}/tests/runTest.py ${CMAKE_CURRENT_SOURCE_DIR}/tests/shell/test-bash-completion.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests
COMMENT "Running tests" VERBATIM
Expand Down
8 changes: 7 additions & 1 deletion Insights.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,13 @@ extern struct __mptr* __vtbl_array[];

if(auto err = opExpected.takeError()) {
if(gAutoComplete) {
#define INSIGHTS_OPT(option, name, deflt, description, category) llvm::outs() << "--" << option << " ";
// Emit options in the same format clang uses for its own --autocomplete, i.e.
// "option\tdescription" (a single TAB separates the option from its
// description). This lets every shell completion script reuse clang's parsing
// and show the description alongside the option. The leading "--" is added here
// so the bare option name from InsightsOptions.def can be reused directly.
#define INSIGHTS_OPT(option, name, deflt, description, category) \
llvm::outs() << "--" << option << "\t" << description << "\n";

#include "InsightsOptions.def"

Expand Down
19 changes: 16 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,23 @@ At least for macOS, you can install C++ Insights via Homebrew thanks to [this fo
brew install cppinsights
```

## Bash autocomplete
## Shell autocomplete

There is a bash autocomplete script. It autocompletes the C++ Insights options as well as the Clang options provided after `--`.
You can download it with the following commands:
There are completion scripts for bash, zsh and fish. They autocomplete the C++ Insights options (including their descriptions) as well as the Clang options provided after `--`.

When building from source, the completion scripts are installed automatically into the shell-specific directories under `share/` (configurable via `CMAKE_INSTALL_PREFIX`):

```
cmake --install build
```

This places the scripts at:

* bash: `share/bash-completion/completions/insights`
* zsh: `share/zsh/site-functions/_insights`
* fish: `share/fish/vendor_completions.d/insights.fish`

If you install C++ Insights yourself (or just want the script), you can download them individually:

```
cd <YOUR_BASH_COMPLETION.D>
Expand Down
5 changes: 4 additions & 1 deletion scripts/bash-autocomplete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ _insights_clang_options()
[[ $i != $cword && "${COMP_WORDS[$(($i))]}" != '=' ]] && arg="$arg,"
done

# Each line is "option\tdescription" (TAB separated, matching clang's
# --autocomplete format). Keep only the option name (the first field).
options=$( insights -- --autocomplete="$arg" 2>/dev/null | awk '{print $1}' | tr '\n' ' ' )

if [[ "$options" == "" || "$options" == " " ]]; then
Expand Down Expand Up @@ -60,7 +62,8 @@ _insights()
COMP_WORDS=$words
COMP_CWORD=$cword

VALUES=$(insights --autocomplete)
# Each line is "option\tdescription" (TAB separated). Keep only the name.
VALUES=$(insights --autocomplete | awk '{print $1}')
COMPREPLY=( $( compgen -W '-h --help --help-list --version -p --extra-arg --extra-arg-before $VALUES' -- "$cur" ) )

# Expanding files if nothing was provided
Expand Down
43 changes: 43 additions & 0 deletions scripts/fish-autocomplete.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#
# C++ Insights, copyright (C) by Andreas Fertig
# Distributed under an MIT license. See LICENSE for details
#
#------------------------------------------------------------------------------
# fish completion for C++ Insights.
#
# The C++ Insights options (with their descriptions) are registered statically
# when this file is loaded: `insights --autocomplete` emits lines of the form
# "option\tdescription" (a single TAB, the same format clang uses), which we
# split on the TAB here.
#
# Options given after "--" are forwarded to Clang. Because fish's dynamic
# completions cannot attach a description to computed arguments, those are
# completed by name only.

# Clear any previously registered insights completions (idempotent reload).
complete -c insights -e

# Register the C++ Insights options (and their descriptions) statically.
for line in (insights --autocomplete 2>/dev/null | string split \n)
# Each line is "option\tdescription" (TAB separated).
set -l fields (string split -- (printf '\t') -- $line)
set -l opt $fields[1]
set -l desc $fields[2]
[ -z "$opt" ]; and continue
complete -c insights -l (string replace -- '--' '' $opt) -d "$desc"
end

# A handful of built-in flags that are not part of InsightsOptions.def.
complete -c insights -s h -l help -d 'Display available options'
complete -c insights -s h -l help-list -d 'Display available options'
complete -c insights -l version -d 'Display the version'
complete -c insights -s p -l build-path -d 'Specify the build path'
complete -c insights -l extra-arg -d 'Additional argument appended to the compiler command line'
complete -c insights -l extra-arg-before -d 'Additional argument prepended to the compiler command line'

# After "--" the remaining arguments are passed to Clang. Complete them by
# name (no description) using clang's own autocomplete via `insights --`.
complete -c insights -n '__fish_seen_argument --' \
-a '(set -l cargs (commandline -opc); set -l idx (contains -i -- -- $cargs); set -l rest (string join , -- $cargs[(math $idx + 1)..-1]); insights -- --autocomplete="$rest" 2>/dev/null | string replace -r \'\t.*\' \'\')'

# ex: ts=4 sw=4 et filetype=fish
102 changes: 102 additions & 0 deletions scripts/zsh-autocomplete.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#compdef insights
#
# C++ Insights, copyright (C) by Andreas Fertig
# Distributed under an MIT license. See LICENSE for details
#
#------------------------------------------------------------------------------
# zsh completion for C++ Insights.
#
# Completes the C++ Insights options (with descriptions) as well as the Clang
# options provided after "--". The option list is obtained from `insights
# --autocomplete` and `insights -- --autocomplete=...` which both emit lines in
# the form "option\tdescription" (a single TAB, the same format clang uses).

_insights_get_options()
{
# $1 == "" -> C++ Insights options (insights --autocomplete)
# $1 == arg -> Clang options after "--" (insights -- --autocomplete="arg")
local raw
if [[ -z "$1" ]]; then
raw=$(insights --autocomplete 2>/dev/null)
else
raw=$(insights -- --autocomplete="$1" 2>/dev/null)
fi

# Split each "option\tdescription" line into option + description.
local IFS=$'\n'
local line opt desc
_insights_opts=()
_insights_descs=()
for line in ${=raw}; do
opt=${line%%$'\t'*}
desc=${line#*$'\t'}
[[ -z "$opt" ]] && continue
_insights_opts+=("$opt")
_insights_descs+=("$desc")
done
}

_insights()
{
local sep_index=0 i j clang_args arg builtin_opt builtin_desc

# Detect a "--" separator: everything after it is passed to Clang.
for (( i = 1; i <= ${#words[@]}; i++ )); do
if [[ "${words[i]}" == "--" ]]; then
sep_index=$i
break
fi
done

if (( sep_index > 0 )); then
# Build the comma-separated argument list clang expects (words after "--").
clang_args=("${words[@]:sep_index+1}")
arg=""
for (( j = 1; j <= ${#clang_args[@]}; j++ )); do
arg="$arg${clang_args[j-1]}"
[[ $j != ${#clang_args[@]} && "${clang_args[j]}" != '=' ]] && arg="$arg,"
done

_insights_get_options "$arg"

if (( ${#_insights_opts[@]} == 0 )); then
_files
return
fi

compadd -d _insights_descs -a _insights_opts
return
fi

# No "--" yet: complete C++ Insights options plus a few built-in flags.
_insights_get_options ""

local -a all_opts all_descs
all_opts=("${_insights_opts[@]}")
all_descs=("${_insights_descs[@]}")

# A handful of built-in flags that are not part of InsightsOptions.def.
for builtin_desc in \
'-h:Display available options' \
'--help:Display available options' \
'--help-list:Display available options' \
'--version:Display the version' \
'-p:Specify the build path' \
'--extra-arg:Additional argument appended to the compiler command line' \
'--extra-arg-before:Additional argument prepended to the compiler command line'; do
builtin_opt="${builtin_desc%%:*}"
all_opts+=("$builtin_opt")
all_descs+=("${builtin_desc#*:}")
done

if (( ${#all_opts[@]} == 0 )); then
_files
return
fi

compadd -d all_descs -a all_opts
}

_insights "$@"

# ex: ts=4 sw=4 et filetype=zsh
6 changes: 6 additions & 0 deletions tests/shell/test-bash-completion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
PATH=$PATH:$1
failureIsOkay=$4

# Redirect any LLVM profiling output (e.g. default.profraw written by an
# instrumented build) away from the sandbox directory so it does not pollute the
# file-completion list below.
export LLVM_PROFILE_FILE=/tmp/cppinsights-bash-test-%p.profraw
rm -f "$2"/*.profraw

cd $2

source $3
Expand Down
40 changes: 40 additions & 0 deletions tests/test-fish-completion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#! /bin/sh
#
# C++ Insights, copyright (C) by Andreas Fertig
# Distributed under an MIT license. See LICENSE for details
#
#------------------------------------------------------------------------------
# Verifies the fish completion for C++ Insights:
# 1. `insights --autocomplete` emits "option\001description\001" lines.
# 2. `complete -C "insights --use"` offers --use-libc++ (with a description).

build_dir=$1
test_dir=$2
script=$3

export PATH="$build_dir:$PATH"

ret=0

echo "Running fish autocomplete tests..."

# 1. The C++ backend must emit descriptions separated by a TAB.
if insights --autocomplete 2>/dev/null | grep -q "$(printf '\t')"; then
echo "[PASSED] insights --autocomplete emits descriptions"
else
echo "[FAILED] insights --autocomplete does not emit descriptions"
ret=1
fi

# 2. Completing "insights --use" must offer --use-libc++.
out=$(fish -c "source '$script'; complete -C 'insights --use'" 2>/dev/null)

if echo "$out" | grep -q -- '--use-libc++'; then
echo "[PASSED] completing --use offers --use-libc++"
else
echo "[FAILED] completing --use does not offer --use-libc++"
echo "$out"
ret=1
fi

exit $ret
Loading
Loading