Skip to content
Open
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
20 changes: 20 additions & 0 deletions cwmscli/utils/click_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cwmscli.utils.version import get_cwms_cli_version

DOCS_BASE_URL = "https://cwms-cli.readthedocs.io/en/latest"
SHELL_COMPLETION_DOCS_URL = f"{DOCS_BASE_URL}/cli/shell_completion.html"


def _render_version_line(ctx: click.Context) -> str:
Expand Down Expand Up @@ -61,6 +62,14 @@ def _render_docs_line(ctx: click.Context) -> Optional[str]:
return f"Docs: {colors.c(docs_url, 'blue', bright=True)}"


def _render_shell_completion_line(ctx: click.Context) -> Optional[str]:
if ctx.parent is not None:
return None
return (
f"Shell completion: {colors.c(SHELL_COMPLETION_DOCS_URL, 'blue', bright=True)}"
)


def _command_path(ctx: click.Context) -> str:
names: list[str] = []
cur: Optional[click.Context] = ctx
Expand Down Expand Up @@ -98,6 +107,7 @@ def _inject_help_header(help_text: str, ctx: click.Context) -> str:

version_line = _render_version_line(ctx)
docs_line = _render_docs_line(ctx)
shell_completion_line = _render_shell_completion_line(ctx)
maintainers_line = _render_maintainers_line(ctx)
if lines[0].startswith("Usage:"):
lines.insert(1, version_line)
Expand All @@ -106,13 +116,23 @@ def _inject_help_header(help_text: str, ctx: click.Context) -> str:
lines.insert(3, maintainers_line)
else:
lines.insert(2, maintainers_line)
if shell_completion_line is not None:
insert_at = 3 if docs_line is not None else 2
if docs_line is not None:
insert_at += 1
lines.insert(insert_at, shell_completion_line)
else:
lines.insert(0, version_line)
if docs_line is not None:
lines.insert(1, docs_line)
lines.insert(2, maintainers_line)
else:
lines.insert(1, maintainers_line)
if shell_completion_line is not None:
insert_at = 2 if docs_line is not None else 1
if docs_line is not None:
insert_at += 1
lines.insert(insert_at, shell_completion_line)
return "\n".join(lines)


Expand Down
1 change: 1 addition & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CLI reference
See also
--------

- :doc:`Shell Completion <cli/shell_completion>`
- :doc:`csv2cwms <cli/csv2cwms>`
- :doc:`CDA Regex Guide <cli/cda_regex>`
- :doc:`load location ids-all <cli/load_location_ids_all>`
Expand Down
5 changes: 5 additions & 0 deletions docs/cli/setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Install the base CLI package:

pip install cwms-cli

After installation, see :doc:`Shell Completion <shell_completion>` if you want
tab completion for supported shells.

Some commands require additional libraries that are not installed with the base
package. You will be alerted to missing dependencies if you try to run a
command that requires an optional library that is not installed. See the next
Expand Down Expand Up @@ -62,4 +65,6 @@ See :doc:`Common API Arguments <api_arguments>` for environment setup examples.
See also
--------

- :doc:`Shell Completion <shell_completion>`
- :doc:`csv2cwms <csv2cwms>`
- :doc:`Common API Arguments <api_arguments>`
107 changes: 107 additions & 0 deletions docs/cli/shell_completion.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Shell Completion
================

``cwms-cli`` uses Click, which provides built-in shell completion support for
``bash`` (4.4+), ``zsh``, and ``fish`` when the CLI is installed as an entry
point such as ``cwms-cli``.

Shell completion can suggest:

- command and subcommand names
- option names
- values for supported parameter types such as choices and paths

See the official Click shell completion guide for background and advanced
customization:

- https://click.palletsprojects.com/en/stable/shell-completion/

Requirements
------------

- Install ``cwms-cli`` so the ``cwms-cli`` executable is available in your
shell.
- Start completion from the installed command name, not ``python -m``.
- Restart your shell after changing your shell startup files.

Bash
----

Add this line to ``~/.bashrc``:

.. code-block:: bash

eval "$(_CWMS_CLI_COMPLETE=bash_source cwms-cli)"

That asks Click to generate the completion script each time a new shell starts.

If you prefer to generate the script once and source a saved file instead:

.. code-block:: bash

_CWMS_CLI_COMPLETE=bash_source cwms-cli > ~/.cwms-cli-complete.bash

Then add this line to ``~/.bashrc``:

.. code-block:: bash

. ~/.cwms-cli-complete.bash

Zsh
---

Add this line to ``~/.zshrc``:

.. code-block:: zsh

eval "$(_CWMS_CLI_COMPLETE=zsh_source cwms-cli)"

If you prefer to generate the script once and source a saved file instead:

.. code-block:: zsh

_CWMS_CLI_COMPLETE=zsh_source cwms-cli > ~/.cwms-cli-complete.zsh

Then add this line to ``~/.zshrc``:

.. code-block:: zsh

. ~/.cwms-cli-complete.zsh

Fish
----

Save the generated completion script to Fish's completions directory:

.. code-block:: fish

_CWMS_CLI_COMPLETE=fish_source cwms-cli > ~/.config/fish/completions/cwms-cli.fish

Fish will load that file automatically in new shell sessions.

Verify Completion
-----------------

After installing the shell integration and opening a new shell, try:

.. code-block:: bash

cwms-cli <TAB>

You should see top-level commands such as ``load``, ``usgs``, and ``blob``.
You can also try:

.. code-block:: bash

cwms-cli load <TAB>
cwms-cli blob --<TAB>

Unsupported Shells
------------------

Click's built-in shell completion support covers ``bash``, ``zsh``, and
``fish``. For now, ``cwms-cli`` does not document built-in completion setup
for PowerShell or ``cmd.exe``.

If Windows shell completion becomes a requirement, that will need either a
custom completion integration or a third-party Click shell extension.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Contents
:caption: Getting Started

cli/setup
cli/shell_completion
cli
cli/api_arguments

Expand Down
5 changes: 4 additions & 1 deletion tests/cli/test_all_commands_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from cwmscli.__main__ import cli
from cwmscli.ownership import format_command_maintainers
from cwmscli.utils.click_help import DOCS_BASE_URL
from cwmscli.utils.click_help import DOCS_BASE_URL, SHELL_COMPLETION_DOCS_URL
from cwmscli.utils.version import get_cwms_cli_version


Expand Down Expand Up @@ -68,6 +68,7 @@ def test_root_help(runner):
assert "Usage:" in result.output
assert f"Version: {get_cwms_cli_version()}" in result.output
assert f"Docs: {DOCS_BASE_URL}/cli.html" in result.output
assert f"Shell completion: {SHELL_COMPLETION_DOCS_URL}" in result.output
assert f"Maintainers: {format_command_maintainers('cwms-cli')}" in result.output


Expand Down Expand Up @@ -143,5 +144,7 @@ def test_every_command_has_help(runner, path, command):
path[0], f"{DOCS_BASE_URL}/cli.html#cwms-cli-{path[0]}"
)
assert f"Docs: {expected_docs}" in result.output
assert "Shell completion:" not in result.output
else:
assert "Docs:" not in result.output
assert "Shell completion:" not in result.output
Loading