retis-org / retis

Tracing packets in the Linux networking stack & friends
https://retis.readthedocs.io/en/stable/
100 stars 14 forks source link

add bash completion script #68

Closed vlrpl closed 10 months ago

vlrpl commented 1 year ago

There's a chance we'll end up having a decent number of options and adding a bash completion file could be a nice to have

atenart commented 1 year ago

As discussed in #75, it would be worth looking at https://docs.rs/clap_complete/4.1.1/clap_complete/index.html.

liuhangbin commented 11 months ago

Hi @vlrpl @atenart

I checked this feature. clap_complete has 3 ways to generate the shell completion.

The 1st way is using generate_to, which will generate the shell completion file at compile time. With this way we need to put the generator in build.rs. But this way is very hard to implement as we need to include!(src/cli/cli.rs), which also depends on a lot of modules.

The 2nd way is using generate, which could generate the completions file for a specified shell at runtime. But we need to add a generate parameter. e.g.

# retis -h

Usage: retis [OPTIONS] <COMMAND>

Commands:
  collect  Collect network events
  print    Print stored events to stdout
  sort     Sort stored events in series based on tracking id
  pcap     Generate a PCAP file from stored events
  profile  Manage Profiles

Options:
      --log-level <LOG_LEVEL>  Log level [default: info] [possible values: error, warn, info, debug, trace]
  -p, --profile <PROFILE>      Comma separated list of profile names to apply
      --kconf <KCONF>          Path to kernel configuration (e.g. /boot/config-6.3.8-200.fc38.x86_64; default: auto-detect)
  -g, --generate <generator>   [possible values: bash, elvish, fish, powershell, zsh]
  -h, --help                   Print help (see more with '--help')
  -V, --version                Print version

# retis -g bash > bash_complete
# source bash_complete

The 3rd way is using the unstalbe dynamic feature. With this way we can hide the generate parameter and user will not see it normally. e.g.

# retis -h
A subcommand definition to `flatten` into your CLI

Usage: retis [OPTIONS] <COMMAND>

Commands:
  collect  Collect network events
  print    Print stored events to stdout
  sort     Sort stored events in series based on tracking id
  pcap     Generate a PCAP file from stored events
  profile  Manage Profiles

Options:
      --log-level <LOG_LEVEL>  Log level [default: info] [possible values: error, warn, info, debug, trace]
  -p, --profile <PROFILE>      Comma separated list of profile names to apply
      --kconf <KCONF>          Path to kernel configuration (e.g. /boot/config-6.3.8-200.fc38.x86_64; default: auto-detect)
  -h, --help                   Print help (see more with '--help')
  -V, --version                Print version

# retis complete -h
Register shell completions for this program

Usage: retis complete --shell <SHELL> --register <REGISTER> [-- <COMP_WORDS>...]

Options:
      --shell <SHELL>        Specify shell to complete for [possible values: bash, fish]
      --register <REGISTER>  Path to write completion-registration to
  -h, --help                 Print help (see more with '--help')

# retis complete --shell bash --register bash_complete
# source bash_complete

Note the 2nd way supports bash, elvish, fish, powershell, zsh while the 3rd way only supports bash, fish. So which way do you want?

The other question is how to use the completion file after generating? We can't force user to source it every time. Should we add an alise file to source the completion file and execuate retis with all parameters ? e.g.

#!/bin/bash

source ${the_install_path}/shell_completion
retis $@
atenart commented 11 months ago

I checked this feature. clap_complete has 3 ways to generate the shell completion.

Thanks for looking into this, it'll improve the UX by a lot!

The 1st way is using generate_to, which will generate the shell completion file at compile time. With this way we need to put the generator in build.rs. But this way is very hard to implement as we need to include!(src/cli/cli.rs), which also depends on a lot of modules.

Agreed, while generating completion scripts at build time would be best, due to how we handle cli options this is not possible.

The 2nd way is using generate, which could generate the completions file for a specified shell at runtime. But we need to add a generate parameter. e.g.

# retis -h

Usage: retis [OPTIONS] <COMMAND>

Commands:
  collect  Collect network events
  print    Print stored events to stdout
  sort     Sort stored events in series based on tracking id
  pcap     Generate a PCAP file from stored events
  profile  Manage Profiles

Options:
      --log-level <LOG_LEVEL>  Log level [default: info] [possible values: error, warn, info, debug, trace]
  -p, --profile <PROFILE>      Comma separated list of profile names to apply
      --kconf <KCONF>          Path to kernel configuration (e.g. /boot/config-6.3.8-200.fc38.x86_64; default: auto-detect)
  -g, --generate <generator>   [possible values: bash, elvish, fish, powershell, zsh]
  -h, --help                   Print help (see more with '--help')
  -V, --version                Print version

# retis -g bash > bash_complete
# source bash_complete

Nit: if we go this way I think a complete sub-command would be better than a top level parameter.

The 3rd way is using the unstalbe dynamic feature. With this way we can hide the generate parameter and user will not see it normally. e.g.

Note the 2nd way supports bash, elvish, fish, powershell, zsh while the 3rd way only supports bash, fish. So which way do you want?

IIUC the 3rd way would be best, as the completion feature would be built-in and would not require our or the user involvement. However I'm not sure how complete is the unstable-dynamic feature. Do you know if this is unstable because not all shells are supported or because some features are not there yet?

With that, it looks like the best option for now would be to have a retis generate command. @vlrpl @amorenoz WDYT?

The other question is how to use the completion file after generating? We can't force user to source it every time. Should we add an alise file to source the completion file and execuate retis with all parameters ? e.g.

#!/bin/bash

source ${the_install_path}/shell_completion
retis $@

Please, no. We should either install it as part of the rpm or in the container image, or let the user source it (should be documented in docs).

To install the completion script in the rpm or image we would need to generate it though. Maybe this could be done alongside #237 (eg. by calling the resulting binary and generating the completion files once available)? (Not that this is mandatory to get a first implementation in). Or maybe it's possible to call retis generate while building an rpm / image and install the resulting completion file.

liuhangbin commented 11 months ago

Do you know if this is unstable because not all shells are supported or because some features are not there yet?

From the maintainer's comment, the dynamic completions is still under development and will replace static completions in the future. So I'd suggest use this feature directly.

Please, no. We should either install it as part of the rpm or in the container image, or let the user source it

OK, let's do it later. When the dynamic completions got stable. I think the completion file should be stable too as it doesn't depend on the parameter list. Here is an example file for bash completion.

_clap_complete_retis() {
    export IFS=$'\013'
    export _CLAP_COMPLETE_INDEX=${COMP_CWORD}
    export _CLAP_COMPLETE_COMP_TYPE=${COMP_TYPE}
    if compopt +o nospace 2> /dev/null; then
        export _CLAP_COMPLETE_SPACE=false
    else
        export _CLAP_COMPLETE_SPACE=true
    fi
    COMPREPLY=( $("retis" complete --shell bash -- "${COMP_WORDS[@]}") )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete -o nospace -o bashdefault -F _clap_complete_retis retis