copier-org / copier

Library and command-line utility for rendering projects templates.
https://readthedocs.org/projects/copier/
MIT License
1.93k stars 173 forks source link

Support hiding choices in a question #1212

Open GytisBraz opened 1 year ago

GytisBraz commented 1 year ago

Actual Situation

I have a question with multiple choices which depend on a previous question and I would like to be able to hide some of the choices from the user. Consider this example copier.yaml file:

version:
  type: str
  help: Choose your Python version
  choices:
    - "3.8"
    - "3.9"

image:
  type: str
  help: Choose a base Docker image
  choices:
    slim (python==3.8):
      value: slim-3.8
      validator: '{% if version == "3.8" %}{% else %}does not apply{% endif %}'
    bullseye (python==3.8):
      value: bullseye-3.8
      validator: '{% if version == "3.8" %}{% else %}does not apply{% endif %}'
    slim (python==3.9):
      value: slim-3.9
      validator: '{% if version == "3.9" %}{% else %}does not apply{% endif %}'
    bullseye (python==3.9):
      value: bullseye-3.9
      validator: '{% if version == "3.9" %}{% else %}does not apply{% endif %}'

The user chooses a version, and depending on the version chosen, only some of the choices in the next answer will be 'selectable'. However, the 'unselectable' or 'disabled' choices in the second question are still visible to the user. In this toy example they are very similar to the 'enabled' choices and add clutter. Screenshot 2023-06-22 at 14 18 16

The situation isn't too bad with just a few different versions and images but you can imagine things would get very cluttered with more options.

Desired Situation

It would be nice to be able to have less clutter in the CLI prompt. Ideally, some way to be able to only show some choices, and hide others.

Proposed solution

One idea is to have another parameter, in addition to the validator and value parameters in the dict-style choices. Perhaps a parameter such as hidden which could be set to true and would then result in the choice being hidden from the user. E.g. something like

question:
  type: str
  help: Some help text
  choices:
    first:
      value: some-value
      validator: ...
      hidden: true # could be dynamically set
    ...

Or perhaps a question-level parameter, e.g. hide_disabled, which could hide the choices which don't pass the validator. E.g.

question:
  type: str
  help: Some help text
  hide_disabled: true
  choices:
    ...
sisp commented 1 year ago

Thanks for your feature request, the detailed description and solution proposal. :pray:

Copier uses questionary for prompting. Questions with choices are mapped to questionary's select question type, and questions are marked as disabled by setting Choice(..., disabled="<message>").

I see two options to hide disabled questions:

  1. Request this feature upstream in questionary. Once available, Copier could use it.
  2. When a choice or question is configured to hide a disabled choice, we could insert something like

    if disabled and value.get("hidden", False):
        continue

    after

    https://github.com/copier-org/copier/blob/5a5e445f0cff7356436af1d51031c3b3d8d0114d/copier/user_data.py#L328

    If we introduce a question-level setting, we can internally propagate it down to each choice, so that hiding choices can always be handled there.

WDYT, @copier-org/maintainers?

yajo commented 1 year ago

Let me offer you a couple of solutions that currently work, just to see if you stillfeel the need for this feature after trying those.

The 1st one, obvious from the questionary you provided, is to just ask for the base distro:

version:
  type: str
  help: Choose your Python version
  choices:
    - "3.8"
    - "3.9"

image:
  type: str
  help: Choose a base Docker image
  choices:
    - slim 
    - bullseye

You can do {{image}}-{{version}} to get the final result.

The second solution would be to see if it makes sense to split the selections in two questions. Each one of them can have a when clause, so you show one selection list or the other. Later in the template, you'd have to do a small gimnastic to get the value: {{ selection_1|d(selection_2)}} or similar.

GytisBraz commented 1 year ago

Let me offer you a couple of solutions that currently work, just to see if you stillfeel the need for this feature after trying those.

The 1st one, obvious from the questionary you provided, is to just ask for the base distro:

version:
  type: str
  help: Choose your Python version
  choices:
    - "3.8"
    - "3.9"

image:
  type: str
  help: Choose a base Docker image
  choices:
    - slim 
    - bullseye

You can do {{image}}-{{version}} to get the final result.

The second solution would be to see if it makes sense to split the selections in two questions. Each one of them can have a when clause, so you show one selection list or the other. Later in the template, you'd have to do a small gimnastic to get the value: {{ selection_1|d(selection_2)}} or similar.

Unfortunately the first solution doesn't quite work if there is an image that is supported by a different set of Python versions. Perhaps the toy example wasn't the best, but my use-case involves choices which have hard dependencies on the previous question.

So you could imagine that e.g. for version 3.8 there are slim and bullseye images, for 3.9 maybe there's only the bullseye image, for 3.10 there is slim and some other xyz image, etc. And once the user chooses the version, only a subset of all of the images should be shown.

As for the second approach, I don't think two questions would suffice in the general case. If I have n different versions and for each of them I want to show a different subset of images, would it still be possible to retrieve the value, e.g. {{ selection_1|d(selection_2|d(selection_3)|d(selection_4)...)}}? šŸ˜

yajo commented 1 year ago

OK, yet another solution. Would it be OK if the disabled questions looked more different than the enabled ones? Current style seems too similar.

GytisBraz commented 1 year ago

OK, yet another solution. Would it be OK if the disabled questions looked more different than the enabled ones? Current style seems too similar.

Yea, that could be a compromise if hiding the questions is too cumbersome to implement at the moment šŸ˜