Skip to content

Commit

Permalink
Add shell completion via shtab
Browse files Browse the repository at this point in the history
Having a shell completion improves developers' workflow and makes it
easier for everyone to notice as the program gains new options.

This commit adds support for generatic static completion files via
shtab. Unlike other solutions which repeatedly invoke the underlying
program, to retrieve the next suggestion, shtab parses argparse options
and produces a complete static file.

It currently supports bash, tcsh and bash with PR opened for fish
support. For example, to generate zsh completion use:
 - reuse --print-completion zsh > /usr/share/zsh/site-functions/_reuse

This can be done by the reuse project itself, the package maintainer or
end-user.

For more details, see https://github.com/iterative/shtab

Closes: #629
Co-authored-by: Carmen Bianca BAKKER <carmenbianca@fsfe.org>
Reviewed-by: Emil Velikov <emil.l.velikov@gmail.com>
Signed-off-by: Carmen Bianca BAKKER <carmenbianca@fsfe.org>
Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com
  • Loading branch information
evelikov and carmenbianca committed Sep 26, 2024
1 parent 26f0be9 commit 814627b
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Install dependencies
run: |
pip install poetry~=1.3.0
poetry install --no-interaction --only main,test
poetry install --no-interaction --only main,test --all-extras
- name: Run tests with pytest
run: |
poetry run pytest --cov=reuse
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,4 @@ Contributors
- Сергій <sergiy.goncharuk.1@gmail.com>
- Mersho <code.rezaei@gmail.com>
- Skyler Grey <sky@a.starrysky.fyi>
- Emil Velikov <emil.l.velikov@gmail.com>
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ repos:
- id: reuse-lint-file
```

### Shell completion

You can generate a shell completion script with `reuse --print-completion bash`.
Replace 'bash' as needed. You must place the printed text in a file dictated by
your shell to benefit from completions.

This functionality depends on `shtab`, which is an optional dependency. To
benefit from this feature, install reuse with the `completion` extra, like
`pipx install reuse[completion]`.

## Maintainers

- Carmen Bianca Bakker <carmenbianca@fsfe.org>
Expand Down
1 change: 1 addition & 0 deletions changelog.d/added/shtab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added `--print-completion` using a new `shtab` optional dependency. (#1076)
12 changes: 12 additions & 0 deletions docs/man/reuse.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
..
SPDX-FileCopyrightText: 2019 Free Software Foundation Europe e.V. <https://fsfe.org>
SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
SPDX-FileCopyrightText: 2024 Emil Velikov <emil.l.velikov@gmail.com>
SPDX-License-Identifier: CC-BY-SA-4.0

Expand Down Expand Up @@ -77,6 +78,17 @@ Options
current working directory's VCS repository, or to the current working
directory.

.. option:: -s, --print-completion SHELL

Print a static shell completion script for the given shell and exit. You must
place the printed text in a file dictated by your shell before the completions
will function. For Bash, this file is
``${XDG_DATA_HOME}/bash-completion/reuse``.

This option depends on ``shtab``, which is an optional dependency of
:program:`reuse`. The supported shells depend on your installed version of
``shtab``.

.. option:: -h, --help

Display help and exit. If no command is provided, this option is implied.
Expand Down
21 changes: 20 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ license-expression = ">=1.0"
python-debian = ">=0.1.34,!=0.1.45,!=0.1.46,!=0.1.47"
tomlkit = ">=0.8"
attrs = ">=21.3"
shtab = { version = ">=1.4.0", optional = true }

[tool.poetry.extras]
completion = ["shtab"]

[tool.poetry.group.test.dependencies]
pytest = ">=6.0.0"
Expand Down
10 changes: 10 additions & 0 deletions src/reuse/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
# SPDX-FileCopyrightText: 2024 Carmen Bianca BAKKER <carmenbianca@fsfe.org>
# SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
# SPDX-FileCopyrightText: 2024 Kerry McAdams <github@klmcadams>
# SPDX-FileCopyrightText: 2024 Emil Velikov <emil.l.velikov@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""Entry functions for reuse."""

import argparse
import contextlib
import logging
import os
import sys
import warnings
from gettext import gettext as _
from pathlib import Path
from types import ModuleType
from typing import IO, Callable, List, Optional, Type, cast

from . import (
Expand All @@ -34,6 +37,10 @@
from .project import GlobalLicensingConflict, GlobalLicensingFound, Project
from .vcs import find_root

shtab: Optional[ModuleType] = None
with contextlib.suppress(ImportError):
import shtab # type: ignore[no-redef,import-not-found]

_LOGGER = logging.getLogger(__name__)

_DESCRIPTION_LINES = [
Expand Down Expand Up @@ -103,6 +110,9 @@ def parser() -> argparse.ArgumentParser:
type=PathType("r", force_directory=True),
help=_("define root of project"),
)
if shtab:
# This is magic. Running `reuse -s bash` now prints bash completions.
shtab.add_argument_to(parser, ["-s", "--print-completion"])
parser.add_argument(
"--version",
action="store_true",
Expand Down
12 changes: 11 additions & 1 deletion tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from freezegun import freeze_time

from reuse import download
from reuse._main import main
from reuse._main import main, shtab
from reuse._util import GIT_EXE, HG_EXE, JUJUTSU_EXE, PIJUL_EXE, cleandoc_nl
from reuse.report import LINT_VERSION

Expand Down Expand Up @@ -88,6 +88,16 @@ def mock_put_license_in_file(monkeypatch):
return result


@pytest.mark.skipif(not shtab, reason="shtab required")
def test_print_completion(capsys):
"""shtab completions are printed."""
with pytest.raises(SystemExit) as error:
main(["--print-completion", "bash"])

assert error.value.code == 0
assert "AUTOMATICALLY GENERATED by `shtab`" in capsys.readouterr().out


def test_lint(fake_repository, stringio, optional_git_exe, optional_hg_exe):
"""Run a successful lint. The optional VCSs are there to make sure that the
test also works if these programs are not installed.
Expand Down

0 comments on commit 814627b

Please sign in to comment.