Repository: pallets/click (python)

Issue:
Improvement for show_default showing in case of a flag the name instead of True/False
First to say: It's not a bug ... it's (in my opinion) a reasonable improvement.

### Expected Behavior

That's how the boolean flag is configured:

```python
@click.option('--cache/--no-cache', default=False, show_default=True,
              help="Enable/Diable the cache.")
```
Using the --help you see following (just the line of interest):

```
--cache / --no-cache        Enable/Diable the cache.  [default: False]
```

but preferable woud be:

```
--cache / --no-cache        Enable/Diable the cache.  [default: no-cache]
```

It's clear to me that this works for --cache/--no-cache like flags only.

### Actual Behavior

The output is as documented by the API the default **value** (*not the name*) which is for a boolean True or False.

### Environment

* Python version: 3.8.1
* Click version: 7.0


Pull request:
Show flag name instead of True/False with Boolean flags
Fixes #1538 

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 2900361..041de51 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -37,6 +37,8 @@ Unreleased
     ``python -m name``. :issue:`1603`
 -   Include required parent arguments in help synopsis of subcommands.
     :issue:`1475`
+-   Help for boolean flags with ``show_default=True`` shows the flag
+    name instead of ``True`` or ``False``. :issue:`1538`
 
 
 Version 7.1.2
diff --git a/src/click/core.py b/src/click/core.py
index 71178dc..726c69d 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -1948,6 +1948,12 @@ class Option(Parameter):
                 default_string = ", ".join(str(d) for d in self.default)
             elif inspect.isfunction(self.default):
                 default_string = "(dynamic)"
+            elif self.is_bool_flag and self.secondary_opts:
+                # For boolean flags that have distinct True/False opts,
+                # use the opt without prefix instead of the value.
+                default_string = split_opt(
+                    (self.opts if self.default else self.secondary_opts)[0]
+                )[1]
             else:
                 default_string = self.default
             extra.append(f"default: {default_string}")


Test patch:
diff --git a/tests/test_options.py b/tests/test_options.py
index bd66bd2..c22f95d 100644
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -570,3 +570,32 @@ def test_option_names(runner, option_args, expected):
 def test_flag_duplicate_names(runner):
     with pytest.raises(ValueError, match="cannot use the same flag for true/false"):
         click.Option(["--foo/--foo"], default=False)
+
+
+@pytest.mark.parametrize(("default", "expect"), [(False, "no-cache"), (True, "cache")])
+def test_show_default_boolean_flag_name(runner, default, expect):
+    """When a boolean flag has distinct True/False opts, it should show
+    the default opt name instead of the default value. It should only
+    show one name even if multiple are declared.
+    """
+    opt = click.Option(
+        ("--cache/--no-cache", "--c/--nc"),
+        default=default,
+        show_default=True,
+        help="Enable/Disable the cache.",
+    )
+    ctx = click.Context(click.Command("test"))
+    message = opt.get_help_record(ctx)[1]
+    assert f"[default: {expect}]" in message
+
+
+def test_show_default_boolean_flag_value(runner):
+    """When a boolean flag only has one opt, it will show the default
+    value, not the opt name.
+    """
+    opt = click.Option(
+        ("--cache",), is_flag=True, show_default=True, help="Enable the cache.",
+    )
+    ctx = click.Context(click.Command("test"))
+    message = opt.get_help_record(ctx)[1]
+    assert "[default: False]" in message