Source code for click_extra.carapace

# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
"""Export a Click command tree as a Carapace completion spec.

[Carapace](https://carapace.sh) is a multi-shell completion engine: a single
spec file drives identical completions across Bash, Zsh, Fish, Nushell,
PowerShell, Elvish, Xonsh, Oil and more. This module walks a Click/Cloup command
tree and serializes it to the YAML [carapace-spec](https://github.com/carapace-sh/carapace-spec) format, answering the request
for native Carapace support in [Click issue #3188](https://github.com/pallets/click/issues/3188) (closed as out of scope for core
Click, redirected here).

Two completion strategies cooperate:

- **Static.** Choices, file/directory operands and the command hierarchy are
  inlined straight into the spec. These complete with no process launch and work
  in every shell Carapace supports, which is the advantage a spec has over
  Carapace's existing bridge to a single shell's native Click completion.

- **Dynamic.** A parameter carrying a custom `shell_complete` (a callback, or a
  {class}`~click.ParamType` that overrides
  {meth}`~click.ParamType.shell_complete`) cannot be frozen into the spec, so its
  action calls back into the CLI through Carapace's shell macro. The callback
  reuses Click's own completion machinery via {class}`CarapaceComplete`.

```{note}
Dynamic completion needs the {class}`CarapaceComplete` class registered in
the target process, which happens on `import click_extra`. A CLI built with
Click Extra gets it for free; a plain Click CLI would have to import
`click_extra` for the dynamic callback to resolve. Static completion has no
such requirement.
```

The dataclasses below mirror the upstream `carapace-spec` [JSON schema](https://github.com/carapace-sh/carapace-spec/blob/master/schema.json); the
flag-key grammar and macro contract are taken from that project's `flag.go` and
`core.go`.

```{todo}
Mark a required flag group once
[carapace-sh/carapace-spec#437](https://github.com/carapace-sh/carapace-spec/issues/437) ships a `Command` key for cobra's
`MarkFlagsOneRequired`. It closes two gaps at once: a required boolean pair
(`--foo` / `--no-foo`) reaches the spec unmarked, since `!` marks one flag at a
time and either spelling satisfies Click; and Cloup's `RequireAtLeast`
constraint is dropped for want of anywhere to put it.
```
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from pathlib import Path

import click
from click.shell_completion import (
    ShellComplete,
    add_completion_class,
    split_arg_string,
)
from cloup.constraints import mutually_exclusive

# Imported first for its side effect: context, color and parameters import each other,
# and only an import chain starting from context resolves that cycle.
from . import context  # noqa: F401
from ._deprecated import warn_deprecated_argument
from ._utils import generator_tag, missing_extra_message
from .parameters import (
    choice_values,
    clean_help,
    full_short_help,
    is_repeatable,
    iter_inline_literals,
    iter_params_for_display,
    iter_subcommands,
    make_resilient_context,
    option_value_kind,
    resolve_param_help,
    short_long_opts,
)

TYPE_CHECKING = False
if TYPE_CHECKING:
    from collections.abc import Sequence
    from types import ModuleType

    from click import Command, Context, Parameter


_CARAPACE_SPECS_DIR = Path("~/.config/carapace/specs")
"""The `~` form of {data}`CARAPACE_SPECS_DIR`, for {func}`xdg_dir` to expand at
call time."""

CARAPACE_SPECS_DIR = _CARAPACE_SPECS_DIR.expanduser()
"""User spec directory Carapace loads on startup.

Writing `<prog>.yaml` here (see {func}`install_carapace_spec`) is all it takes
for Carapace to pick up a CLI's completions. Mirrors the `$XDG_CONFIG_HOME`
default documented by Carapace, expanded against the `HOME` of the process at
import. {func}`install_carapace_spec` reads `XDG_CONFIG_HOME` and `HOME` at call
time instead, through {func}`xdg_dir`.
"""

CARAPACE_DOCS_URL = "https://kdeldycke.github.io/click-extra/carapace.html"
"""Documentation page stamped into every generated spec's header comment, so a
reader of the raw YAML knows where the feature is documented."""


[docs] def xdg_dir(variable: str, fallback: str | Path, *parts: str) -> Path: """A directory under an XDG base directory, resolved at call time. Reads the base directory from the environment variable `variable` (`XDG_CONFIG_HOME`, say) and appends `parts` to it. An unset or empty variable falls back to `fallback`, the full default path the XDG spec derives from that variable's own default base, which already carries the `parts`. A leading `~` is expanded against the `HOME` in force at the call, so neither the environment nor the home directory is read at import time: a value set for one invocation, by a test fixture or a packaging script staging into a build root, is the one honored. Pass `fallback` in its `~` form for that: a path expanded at import carries that `HOME` for good. """ base = os.environ.get(variable) if base: return Path(base).expanduser().joinpath(*parts) return Path(fallback).expanduser()
def _require_yaml() -> ModuleType: """Import PyYAML on demand, with a pointed error when it is missing. PyYAML ships behind the `carapace` extra, and only the YAML-serializing entry points need it. """ # Deferred import: keeps PyYAML off the startup path of every CLI, installed # or not. try: import yaml except ImportError: raise ImportError( missing_extra_message("carapace", subject="Carapace spec serialization") ) from None return yaml def _clean_description(text: str | None) -> str: r"""Collapse a help string to a single Carapace-friendly description line. Normalizes the prose with {func}`~click_extra.parameters.clean_help`, which drops Click's `\b` no-rewrap marker, then keeps only the first paragraph, strips the reST inline-literal backticks while keeping the text they wrap (Click stores docstrings verbatim, so `"`...`"` markup leaks through) and squashes internal whitespace. Carapace truncates long descriptions itself. Every description of a spec, a command's included, goes through here. """ if not text: return "" paragraph = clean_help(text).split("\n\n", 1)[0] plain = "".join(segment for segment, _ in iter_inline_literals(paragraph)) return " ".join(plain.split()) # --- flag-key construction -------------------------------------------------- def _flag_key( opts: list[str], *, value: bool, optarg: bool = False, repeatable: bool = False, required: bool = False, hidden: bool = False, ) -> str: """Build a Carapace flag key from option spellings and modifiers. Reproduces `carapace-spec`'s own `Flag.format()` grammar: the short spelling first, then the long spelling joined by `", "`, then `?` for an optional value or `=` for a mandatory one, then `*` when the flag is repeatable, `!` when it is required and `&` when it is hidden. So a plain switch is `--foo`, a valued option `--foo=`, an optional-value option `--foo?`, a counter `--foo*`, a repeatable valued option `--foo=*`, a mandatory one `--foo=!` and a hidden one `--foo&`. The modifiers are emitted in that order because upstream writes them that way; its parser reads them as a set, so any order round-trips. """ short, long = short_long_opts(opts) key = short if short and long: key += ", " key += long if optarg: key += "?" elif value: key += "=" if repeatable: key += "*" if required: key += "!" if hidden: key += "&" return key def _flag_name(opts: list[str]) -> str: """Carapace completion key for a flag: long spelling, else short, no dashes.""" short, long = short_long_opts(opts) return (long or short).lstrip("-") # --- completion action derivation ------------------------------------------- def _env_var(prog_name: str) -> str: """Completion env var Click derives from a program name (`_FOO_COMPLETE`). Mirrors the derivation of `click.Command._main_shell_completion`: a `-` and a `.` both become `_`, then the whole name is upper-cased, so a program named `garden.py` reads `_GARDEN_PY_COMPLETE`. A spec exporting any other spelling calls back into a variable Click never reads. """ complete_name = prog_name.replace("-", "_").replace(".", "_") return f"_{complete_name}_COMPLETE".upper() def _dynamic_action(command_path: tuple[str, ...], option: str | None = None) -> str: r"""A Carapace shell-macro action that calls back into the CLI for completion. Carapace's default `$(...)` macro runs `sh -c '<script>' -- <words>`, exposing the already-typed words as `$*` and parsing each output line as `value\tdescription`. The script re-invokes the program in Click's completion mode ({class}`CarapaceComplete`, registered as the `carapace` shell), which prints exactly that. Carapace prefix-filters the result, so the callback returns the full candidate set. `COMP_WORDS` is rebuilt so {class}`CarapaceComplete` hands Click the command line it needs to resolve the completed parameter. Two pieces must be baked in, because Carapace withholds them from `$*`: - `command_path`: the chain of command names from the root program down to the command owning the parameter, like `("weather", "forecast")`. Carapace's `traverse` descends into each subcommand with only the remaining words, so `$*` never carries the parent command names. Without the baked prefix the callback would reconstruct `weather --city` and resolve against the root command. - `option`: the spelling of the flag whose value is being completed, for an option (not an argument). Carapace routes a flag's value completion to this per-flag action and keeps the flag in its own `inFlag` state rather than `c.Args`, so `$*` does not contain it; Click would not know which value to resolve. It is appended after `$*` so it stays the trailing token Click reads as the option awaiting a value. So a subcommand option `--city` yields ``COMP_WORDS=weather forecast $* --city`, while a subcommand argument yields `COMP_WORDS=weather forecast $*`. The env var and the invoked binary stay rooted at `command_path[0]``, the executable Carapace dispatches and the name Click derives its completion variable from. """ root = command_path[0] comp_words = " ".join(command_path) + " $*" if option: comp_words += f" {option}" return ( f'$(env "{_env_var(root)}=carapace_complete" ' f'"COMP_WORDS={comp_words}" {root} 2>/dev/null)' ) def _static_action(param_type: click.ParamType) -> list[str] | None: """Inline completion action for a statically-knowable type, else `None`. Maps choice-like types to their literal values and path/file types to Carapace's `$files` / `$directories` macros, mirroring how Click's own {meth}`Choice.shell_complete <click.Choice.shell_complete>` and {meth}`Path.shell_complete <click.Path.shell_complete>` behave. The choices are spelled by {func}`~click_extra.parameters.choice_values`, so an `Enum` member completes by the name Click accepts. """ choices = choice_values(param_type) if choices: return list(choices) if isinstance(param_type, click.Path): if param_type.dir_okay and not param_type.file_okay: return ["$directories"] return ["$files"] if isinstance(param_type, click.File): return ["$files"] return None def _overrides_shell_complete(param_type: click.ParamType) -> bool: """Whether a type provides a non-static custom `shell_complete` override.""" if isinstance(param_type, (click.Choice, click.Path, click.File)): return False if getattr(param_type, "choices", None): return False return type(param_type).shell_complete is not click.ParamType.shell_complete def _param_action( param: Parameter, command_path: tuple[str, ...], option: str | None = None ) -> list[str]: """Resolve the Carapace completion action for one parameter. An explicit `shell_complete=` callback always routes to the dynamic macro; otherwise a statically-knowable type is inlined; otherwise a type that overrides `shell_complete` falls back to the dynamic macro. Anything else yields an empty action (no completion offered). `option` carries the flag spelling when the parameter is an option, so the dynamic macro can name the flag whose value is being completed (see {func}`_dynamic_action`); arguments leave it `None`. """ if getattr(param, "_custom_shell_complete", None) is not None: return [_dynamic_action(command_path, option)] static = _static_action(param.type) if static is not None: return static if _overrides_shell_complete(param.type): return [_dynamic_action(command_path, option)] return [] # --- spec data model -------------------------------------------------------- def _positional_entries( positional: Sequence[object], positionalany: object, ) -> dict: """Serialize the `positional` and `positionalany` pair a spec block carries. The two blocks holding one, `completion` and `documentation`, index their operands the same way and drop an empty member the same way, so they emit the pair through here rather than each writing the rule out. :param positional: what each fixed-arity slot carries, in order. A trailing run of empty entries is dropped: a slot carrying nothing says nothing, and the surviving ones keep the indices the other block reads them at. :param positionalany: what the trailing variadic operand carries. :return: the mapping, holding only the members that carry something. """ out: dict = {} trimmed = list(positional) while trimmed and not trimmed[-1]: trimmed.pop() if trimmed: out["positional"] = trimmed if positionalany: out["positionalany"] = positionalany return out
[docs] @dataclass class CarapaceCompletion: """The `completion` block of a command: per-flag and positional actions.""" flag: dict[str, list[str]] = field(default_factory=dict) """Map of flag name (long spelling, no dashes) to its completion action.""" positional: list[list[str]] = field(default_factory=list) """Per-position completion actions for fixed-arity arguments, in order.""" positionalany: list[str] = field(default_factory=list) """Completion action repeated for every trailing variadic argument."""
[docs] def to_dict(self) -> dict: """Serialize to the `carapace-spec` `completion` mapping, dropping empty members and trailing empty positional slots.""" out: dict = {} if self.flag: out["flag"] = self.flag out.update(_positional_entries(self.positional, self.positionalany)) return out
[docs] @dataclass class CarapaceDocumentation: """The `documentation` block of a command: prose the spec has no other slot for. Carapace keys a flag's own description off `flags`, and leaves an operand with nowhere to carry one: `completion.positional` holds actions, not text. This block is where the upstream schema puts that prose, indexed the same way, so `carapace --style` and the spec's own readers can show what an operand means. Only the two members Click can fill are modeled. The schema also carries `command`, `flag`, `dash` and `dashany`: the first two would restate what `description` and `flags` already say, and Click has no `--`-separated argument family to feed the last two. """ positional: list[str] = field(default_factory=list) """Per-position help text for fixed-arity arguments, in order.""" positionalany: str = "" """Help text for the trailing variadic argument."""
[docs] def to_dict(self) -> dict: """Serialize to the `carapace-spec` `documentation` mapping. Drops empty members and trailing undocumented slots, keeping the surviving ones index-aligned with `completion.positional`. """ return _positional_entries(self.positional, self.positionalany)
[docs] @dataclass class CarapaceCommand: """One node of a Carapace spec: a command and its flags, completions and subcommands, mirroring the upstream `Command` schema.""" name: str """The command's invocation name (the root carries the program name).""" description: str = "" """One-line command description (NAME-section short help).""" aliases: tuple[str, ...] = () """Alternative names the command also answers to (from Cloup).""" hidden: bool = False """Whether the command is hidden from listings (still completable).""" flags: dict[str, str] = field(default_factory=dict) """Map of flag key (with shape suffixes) to its description.""" persistentflags: dict[str, str] = field(default_factory=dict) """Flags inherited by every subcommand. {func}`extract_carapace_command` leaves it empty: Click parses an option only at the level of the command declaring it, so no option is inherited. """ exclusiveflags: list[list[str]] = field(default_factory=list) """Groups of mutually-exclusive flag names (from Cloup constraints).""" completion: CarapaceCompletion = field(default_factory=CarapaceCompletion) """Static and dynamic completion actions for this command's parameters.""" documentation: CarapaceDocumentation = field(default_factory=CarapaceDocumentation) """Prose for the operands, which carry theirs nowhere else in the spec.""" commands: list[CarapaceCommand] = field(default_factory=list) """Nested subcommands."""
[docs] def to_dict(self) -> dict: """Serialize to a `carapace-spec` command mapping, dropping empties. Only `name` is required by the schema, so every other member is omitted when empty to keep the YAML compact. """ out: dict = {"name": self.name} if self.description: out["description"] = self.description if self.aliases: out["aliases"] = list(self.aliases) if self.hidden: out["hidden"] = True if self.flags: out["flags"] = self.flags if self.persistentflags: out["persistentflags"] = self.persistentflags if self.exclusiveflags: out["exclusiveflags"] = self.exclusiveflags completion = self.completion.to_dict() if completion: out["completion"] = completion documentation = self.documentation.to_dict() if documentation: out["documentation"] = documentation if self.commands: out["commands"] = [sub.to_dict() for sub in self.commands] return out
# --- extraction ------------------------------------------------------------- def _exclusive_flag_groups(command: Command) -> list[list[str]]: """Mutually-exclusive flag-name groups from Cloup option-group constraints. Only `@option_group(..., constraint=mutually_exclusive)` is exported: Carapace's `exclusiveflags` has no equivalent for Cloup's richer constraints (`RequireAtLeast`, `RequireExactly`, `If`), which are dropped. """ groups: list[list[str]] = [] for option_group in getattr(command, "option_groups", ()): if getattr(option_group, "constraint", None) is not mutually_exclusive: continue names = [ _flag_name(option.opts) for option in option_group.options if not getattr(option, "hidden", False) ] if len(names) > 1: groups.append(names) return groups def _add_option( node: CarapaceCommand, param: Parameter, ctx: Context, *, command_path: tuple[str, ...], ) -> None: """Encode one Click option into `flags` and completion. A boolean flag with a secondary spelling (`--foo` / `--no-foo`) is split into two independent Carapace flags, since the spec has no negation primitive. Only value-taking options contribute a `completion.flag` action. Dynamic actions reference `command_path` (the chain of command names down to this command) and the option's own spelling, both baked into the callback so Click can resolve the right flag's value (see {func}`_dynamic_action`). The description goes through {func}`~click_extra.parameters.resolve_param_help`, like every other render of this help text: an option computing its own help leaves `param.help` at `None`, and reading the attribute shipped `-v` and `-q` with an empty description in the spec. A required option is marked `!`, and a required *pair* is not: either spelling satisfies Click there, and Carapace marks one flag at a time with no way to say "one of these two". Marking both would demand both, and marking the positive alone would hide that `--no-foo` answers just as well, so the pair is left unmarked rather than described wrongly. """ description = _clean_description(resolve_param_help(param, ctx)) kind = option_value_kind(param) value = kind != "flag" optarg = kind == "optional" repeatable = is_repeatable(param) hidden = bool(getattr(param, "hidden", False)) secondary = getattr(param, "secondary_opts", None) required = bool(getattr(param, "required", False)) and not secondary node.flags[ _flag_key( param.opts, value=value, optarg=optarg, repeatable=repeatable, required=required, hidden=hidden, ) ] = description # Boolean flags expose their negative spelling as a separate switch. if secondary: node.flags[ _flag_key( param.secondary_opts, value=False, repeatable=False, hidden=hidden, ) ] = description if value: short, long = short_long_opts(param.opts) action = _param_action(param, command_path, option=long or short) if action: node.completion.flag[_flag_name(param.opts)] = action
[docs] def extract_carapace_command( command: Command, ctx: Context, *, command_path: tuple[str, ...], is_root: bool | None = None, default_opts: frozenset[str] | None = None, inherited_opts: frozenset[str] | None = None, ) -> CarapaceCommand: """Build a {class}`CarapaceCommand` from a Click command and its context. The context must have been created for `command` (typically via {meth}`click.Command.make_context` with `resilient_parsing=True`). Subcommands are discovered dynamically and recursed into. Every option lands in the `flags` of the command declaring it. Click parses an option only at the level of its own command: a group option goes before the subcommand, never after it. Carapace `persistentflags` would offer it after the subcommand too, where Click rejects it. `command_path` is the chain of command names from the root down to this command, grown by one name per recursion and baked into dynamic callback macros (see {func}`_dynamic_action`). `is_root`, `default_opts` and `inherited_opts` have no effect, and warn: they used to route the default options into `persistentflags`. """ for argument, value in ( ("is_root", is_root), ("default_opts", default_opts), ("inherited_opts", inherited_opts), ): if value is not None: warn_deprecated_argument( "extract_carapace_command", argument, "the call without it" ) node = CarapaceCommand( name=ctx.info_name or command.name or "", description=_clean_description(full_short_help(command)), aliases=tuple(getattr(command, "aliases", ()) or ()), hidden=bool(getattr(command, "hidden", False)), ) positional: list[list[str]] = [] positional_docs: list[str] = [] for param in iter_params_for_display(command, ctx): if isinstance(param, click.Argument): action = _param_action(param, command_path) # One entry per slot the operand occupies, so the documentation # stays index-aligned with the actions beside it. doc = _clean_description(resolve_param_help(param, ctx)) if param.nargs == -1: node.completion.positionalany = action node.documentation.positionalany = doc else: slots = max(param.nargs, 1) positional.extend([action] * slots) positional_docs.extend([doc] * slots) continue _add_option(node, param, ctx, command_path=command_path) node.completion.positional = positional node.documentation.positional = positional_docs node.exclusiveflags = _exclusive_flag_groups(command) for sub_name, sub in iter_subcommands(command, ctx, skip_hidden=False): sub_ctx = make_resilient_context(sub, sub_name, parent=ctx) node.commands.append( extract_carapace_command( sub, sub_ctx, command_path=command_path + (sub_name,), ) ) return node
[docs] def to_carapace_spec( command: Command, prog_name: str | None = None, ctx: Context | None = None, ) -> dict: """Build the Carapace spec for a command tree as a plain dict. Reuses `ctx` when given (the live invocation context), otherwise builds a throwaway one with `resilient_parsing=True`. The returned mapping conforms to the `carapace-spec` schema and is ready to hand to `yaml.safe_dump`. """ name = prog_name or command.name or "" if ctx is None: ctx = make_resilient_context(command, prog_name) node = extract_carapace_command( command, ctx, command_path=(name,), ) # The root node's name follows the program name, not the context's. node.name = name return node.to_dict()
[docs] def dump_carapace_spec( command: Command, prog_name: str | None = None, ctx: Context | None = None, *, invocation: str | None = None, ) -> str: """Serialize a command tree to a Carapace spec YAML string. Requires the optional PyYAML dependency (`click-extra[carapace]`). The output keeps Click's declaration order rather than sorting keys, so flags and subcommands line up with the help screen, and is prefixed with a provenance header: the generator, the `invocation` command line that produced it (when given), and a link to the documentation. """ yaml = _require_yaml() spec = to_carapace_spec(command, prog_name, ctx) body: str = yaml.safe_dump(spec, sort_keys=False, allow_unicode=True, width=88) header_lines = [f"# Generated by {generator_tag()}. Do not edit by hand."] if invocation: header_lines.append(f"# $ {invocation}") header_lines.append(f"# Documentation: {CARAPACE_DOCS_URL}") header = "".join(f"{line}\n" for line in header_lines) return header + body
[docs] def write_carapace_spec( command: Command, target: str | Path, prog_name: str | None = None, *, invocation: str | None = None, ) -> Path: """Render the spec and write it to `target`, returning the written path. Creates parent directories as needed. `invocation` is recorded in the header comment (see {func}`dump_carapace_spec`). """ path = Path(target) path.parent.mkdir(parents=True, exist_ok=True) path.write_text( dump_carapace_spec(command, prog_name, invocation=invocation), encoding="utf-8", ) return path
[docs] def install_carapace_spec( command: Command, prog_name: str | None = None, *, invocation: str | None = None, ) -> Path: """Write the spec into Carapace's user spec directory. Targets `$XDG_CONFIG_HOME/carapace/specs/<prog>.yaml` (honoring an explicit `XDG_CONFIG_HOME`, else the default behind {data}`CARAPACE_SPECS_DIR`, both resolved at call time by {func}`xdg_dir`), which Carapace loads on startup. Returns the path. """ name = prog_name or command.name or "cli" base = xdg_dir("XDG_CONFIG_HOME", _CARAPACE_SPECS_DIR, "carapace", "specs") return write_carapace_spec( command, base / f"{name}.yaml", prog_name=name, invocation=invocation )
# --- dynamic completion backend ---------------------------------------------
[docs] @add_completion_class class CarapaceComplete(ShellComplete): r"""Click completion backend that emits Carapace's value/description lines. Registered as the `carapace` shell, so `_FOO_COMPLETE=carapace_complete` makes a Click CLI print completions in the `value\tdescription` text format Carapace's shell macro parses. This is the callback target of the dynamic actions emitted by {func}`_dynamic_action`; it reuses Click's own {meth}`~click.shell_completion.ShellComplete.get_completions`, so a parameter's custom `shell_complete` is honored verbatim. The current word is left to Carapace to filter, so completion args are the already-typed words with an empty incomplete value. """ name = "carapace" """Shell name Click registers this backend under (the `carapace_complete` completion instruction).""" source_template = ""
[docs] def source(self) -> str: """No-op source step: Carapace, not a shell, consumes this backend.""" return ""
[docs] def get_completion_args(self) -> tuple[list[str], str]: """Read the words Carapace passed via `COMP_WORDS` (program name first). Returns the typed arguments with an empty incomplete value; Carapace applies its own prefix filtering to the candidates we return. """ words = split_arg_string(os.environ.get("COMP_WORDS", "")) args = words[1:] if words else [] return args, ""
[docs] def format_completion(self, item) -> str: r"""Render one completion as Carapace's `value` or `value\tdescription`.""" if item.help: return f"{item.value}\t{item.help}" return str(item.value)