diff --git a/bin/reframe b/bin/reframe index 463e021591..8af6ae6c8d 100755 --- a/bin/reframe +++ b/bin/reframe @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # # Copyright 2016-2021 Swiss National Supercomputing Centre (CSCS/ETH Zurich) # ReFrame Project Developers. See the top-level LICENSE file for details. diff --git a/docs/started.rst b/docs/started.rst index 1ea87f041d..7cd1d958e9 100644 --- a/docs/started.rst +++ b/docs/started.rst @@ -90,6 +90,16 @@ All you need is a Python 3.6+ installation with ``pip``: For previous ReFrame versions you should install its requirements using ``pip install -r requirements.txt`` in a Python virtual environment. +Enabling auto-completion +------------------------ + +.. versionadded:: 3.4.1 + +You can enable auto-completion for ReFrame by sourcing in your shell the corresponding script in ``/etc/reframe_completion.``. +Auto-completion is supported for Bash, Tcsh and Fish shells. + + + Running the Unit Tests ---------------------- diff --git a/etc/reframe_completion.bash b/etc/reframe_completion.bash new file mode 100644 index 0000000000..57077f6a7d --- /dev/null +++ b/etc/reframe_completion.bash @@ -0,0 +1,46 @@ + +# Run something, muting output or redirecting it to the debug stream +# depending on the value of _ARC_DEBUG. +# If ARGCOMPLETE_USE_TEMPFILES is set, use tempfiles for IPC. +__python_argcomplete_run() { + if [[ -z "${ARGCOMPLETE_USE_TEMPFILES-}" ]]; then + __python_argcomplete_run_inner "$@" + return + fi + local tmpfile="$(mktemp)" + _ARGCOMPLETE_STDOUT_FILENAME="$tmpfile" __python_argcomplete_run_inner "$@" + local code=$? + cat "$tmpfile" + rm "$tmpfile" + return $code +} + +__python_argcomplete_run_inner() { + if [[ -z "${_ARC_DEBUG-}" ]]; then + "$@" 8>&1 9>&2 1>/dev/null 2>&1 + else + "$@" 8>&1 9>&2 1>&9 2>&1 + fi +} + +_python_argcomplete() { + local IFS=$'\013' + local SUPPRESS_SPACE=0 + if compopt +o nospace 2> /dev/null; then + SUPPRESS_SPACE=1 + fi + COMPREPLY=( $(IFS="$IFS" \ + COMP_LINE="$COMP_LINE" \ + COMP_POINT="$COMP_POINT" \ + COMP_TYPE="$COMP_TYPE" \ + _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \ + _ARGCOMPLETE=1 \ + _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \ + __python_argcomplete_run "$1") ) + if [[ $? != 0 ]]; then + unset COMPREPLY + elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then + compopt -o nospace + fi +} +complete -o nospace -o default -o bashdefault -F _python_argcomplete reframe diff --git a/etc/reframe_completion.fish b/etc/reframe_completion.fish new file mode 100644 index 0000000000..34768ff362 --- /dev/null +++ b/etc/reframe_completion.fish @@ -0,0 +1,17 @@ + +function __fish_reframe_complete + set -x _ARGCOMPLETE 1 + set -x _ARGCOMPLETE_DFS \t + set -x _ARGCOMPLETE_IFS \n + set -x _ARGCOMPLETE_SUPPRESS_SPACE 1 + set -x _ARGCOMPLETE_SHELL fish + set -x COMP_LINE (commandline -p) + set -x COMP_POINT (string length (commandline -cp)) + set -x COMP_TYPE + if set -q _ARC_DEBUG + reframe 8>&1 9>&2 1>/dev/null 2>&1 + else + reframe 8>&1 9>&2 1>&9 2>&1 + end +end +complete -c reframe -f -a '(__fish_reframe_complete)' diff --git a/etc/reframe_completion.tcsh b/etc/reframe_completion.tcsh new file mode 100644 index 0000000000..9560251631 --- /dev/null +++ b/etc/reframe_completion.tcsh @@ -0,0 +1 @@ +complete "reframe" 'p@*@`python-argcomplete-tcsh "reframe"`@' ; diff --git a/reframe/frontend/argparse.py b/reframe/frontend/argparse.py index d2cd39a0b6..3f1fb8f810 100644 --- a/reframe/frontend/argparse.py +++ b/reframe/frontend/argparse.py @@ -3,6 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause +import argcomplete import argparse import os @@ -257,6 +258,9 @@ def parse_args(self, args=None, namespace=None): `add_argument()` call. If no default value was specified either, the attribute will be set to `None`.''' + # Enable auto-completion + argcomplete.autocomplete(self._holder) + # We always pass an empty namespace to our internal argparser and we do # the namespace resolution ourselves. We do this, because we want the # newly parsed options to completely override any options defined in diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py index c7290bb1a6..ce306dbd3a 100644 --- a/reframe/frontend/cli.py +++ b/reframe/frontend/cli.py @@ -443,13 +443,12 @@ def main(): help='Use a login shell for job scripts' ) + # Parse command line + options = argparser.parse_args() if len(sys.argv) == 1: argparser.print_help() sys.exit(1) - # Parse command line - options = argparser.parse_args() - # First configure logging with our generic configuration so as to be able # to print pretty messages; logging will be reconfigured by user's # configuration later diff --git a/requirements.txt b/requirements.txt index cc28e16ad4..a01ade12ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,5 @@ pytest-parallel==0.1.0 coverage==5.3 setuptools==50.3.0 wcwidth==0.2.5 +argcomplete==1.12.2 #+pygelf%pygelf==0.3.6