Skip to content

Commit fa01833

Browse files
committed
fix: Undocumented completions.
1 parent 9f8ff8d commit fa01833

7 files changed

Lines changed: 55 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
## Unreleased: 0.29.0
66

7+
- fix: Undocumented completions.
78
- docs: Add argument validation documentation.
89
- feat: Test against python 3.14.
910
- fix: Apply pyright strict typing.

src/cappa/completion/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,5 @@ def format_completions(*completions: Completion | FileCompletion) -> str | None:
6969
result: list[str] = []
7070
for item in completions:
7171
item = cast(Completion, item)
72-
result.append(f"{item.value}:{item.help if item.help else ''}")
72+
result.append(f"{item.value}:{item.description}")
7373
return "\n".join(result)

src/cappa/completion/types.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ def backend_template(self, prog: str, arg: Arg[Any]) -> str:
2929
class Completion:
3030
value: str | None = None
3131
help: str | None = None
32+
arg: Arg[Any] | None = None
33+
34+
@property
35+
def description(self) -> str:
36+
if not self.arg or self.arg.help:
37+
return self.help or ""
38+
39+
return f"<{self.arg.value_name}> {self.help}".strip()
3240

3341

3442
@dataclasses.dataclass

src/cappa/help.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
if typing.TYPE_CHECKING:
2424
from cappa.command import Command
2525

26-
HelpFormattable: TypeAlias = typing.Callable[
27-
["Command[typing.Any]", str], typing.List[Displayable]
28-
]
2926
ArgGroup: TypeAlias = typing.Tuple[
3027
typing.Tuple[str, bool], typing.List[typing.Union[Arg[Any], Subcommand]]
3128
]
@@ -44,6 +41,16 @@
4441
]
4542

4643

44+
class HelpFormattable(typing.Protocol):
45+
left_padding: Dimension
46+
arg_format: ArgFormat
47+
default_format: str
48+
49+
def __call__(
50+
self, command: Command[Any], prog: str
51+
) -> list[Displayable]: ... # pragma: no cover
52+
53+
4754
def create_version_arg(version: str | Arg[Any] | None = None) -> Arg[Any] | None:
4855
if not version:
4956
return None
@@ -108,7 +115,7 @@ def create_completion_arg(completion: bool | Arg[bool] = True) -> Arg[bool] | No
108115

109116

110117
@dataclass(frozen=True)
111-
class HelpFormatter:
118+
class HelpFormatter(HelpFormattable):
112119
left_padding: Dimension = (0, 0, 0, 2)
113120
arg_format: ArgFormat = (
114121
Markdown("{help}"),
@@ -117,7 +124,7 @@ class HelpFormatter:
117124
)
118125
default_format: str = "(Default: {default})"
119126

120-
default: typing.ClassVar[Self]
127+
default: typing.ClassVar[HelpFormatter]
121128

122129
def __call__(self, command: Command[Any], prog: str) -> list[Displayable]:
123130
arg_groups = generate_arg_groups(command)
@@ -176,7 +183,7 @@ def add_long_args(
176183

177184

178185
def format_arg(
179-
console: Console, help_formatter: HelpFormatter, arg: Arg[Any]
186+
console: Console, help_formatter: HelpFormattable, arg: Arg[Any]
180187
) -> Displayable:
181188
arg_format = help_formatter.arg_format
182189
if not isinstance(arg_format, Iterable) or isinstance(arg_format, str):
@@ -259,10 +266,7 @@ def _replace_rich_text_component(c: TextComponent, text: str) -> TextComponent:
259266
style=c.style,
260267
justify=c.justify,
261268
overflow=c.overflow,
262-
# no_wrap=c.no_wrap,
263269
end=c.end,
264-
# tab_size=c.tab_size,
265-
# spans=c.spans,
266270
)
267271

268272
if isinstance(c, Markdown):

src/cappa/parser.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from cappa.arg import Arg, ArgAction, ArgActionType, Group
99
from cappa.command import Command, Subcommand
1010
from cappa.completion.types import Completion, FileCompletion
11-
from cappa.help import format_subcommand_names
11+
from cappa.help import format_arg, format_subcommand_names
1212
from cappa.invoke import fulfill_deps
1313
from cappa.output import Exit, HelpExit, Output
1414
from cappa.typing import T, assert_type
@@ -372,20 +372,30 @@ def parse_option(
372372
parse_state: ParseState, context: ParseContext, raw: RawOption
373373
) -> None:
374374
if raw.name not in context.options:
375-
possible_values = [
376-
name for name in context.options if name.startswith(raw.name)
377-
]
375+
possible_options: dict[str, Arg[Any]] = {
376+
name: arg
377+
for name, arg in context.options.items()
378+
if name.startswith(raw.name)
379+
}
378380

379381
if parse_state.provide_completions:
380-
options = [
381-
Completion(option, help=context.options[option].help)
382-
for option in possible_values
383-
]
382+
options: list[Completion] = []
383+
for name, option in possible_options.items():
384+
rendered_help = str(
385+
format_arg(
386+
parse_state.output.output_console,
387+
context.command.help_formatter,
388+
option,
389+
)
390+
).strip()
391+
completion = Completion(name, help=rendered_help, arg=option)
392+
options.append(completion)
393+
384394
raise CompletionAction(*options)
385395

386396
message = f"Unrecognized arguments: {raw.name}"
387-
if possible_values:
388-
message += f" (Did you mean: {', '.join(possible_values)})"
397+
if possible_options:
398+
message += f" (Did you mean: {', '.join(possible_options.keys())})"
389399

390400
raise BadArgumentError(
391401
message, value=raw.name, command=parse_state.current_command

tests/completion/test_option.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Args:
1414
default: bool = False
1515

1616
result = parse_completion(Args, "--d")
17-
assert result == "--default:"
17+
assert result == "--default:<default> (Default: False)"
1818

1919

2020
def test_long_option_name_with_help():
@@ -23,7 +23,7 @@ class Args:
2323
default: Annotated[bool, cappa.Arg(help="Enables default")] = False
2424

2525
result = parse_completion(Args, "--d")
26-
assert result == "--default:Enables default"
26+
assert result == "--default:Enables default (Default: False)"
2727

2828

2929
def test_multiple_matches():
@@ -44,3 +44,12 @@ class Args:
4444

4545
result = parse_completion(Args, "-a")
4646
assert result is None
47+
48+
49+
def test_no_help_value_name():
50+
@dataclass
51+
class Args:
52+
apple: Annotated[str, cappa.Arg(long=True)]
53+
54+
result = parse_completion(Args, "--a")
55+
assert result == "--apple:<apple>"

tests/completion/test_subcommand.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ def test_subcommand_name_with_partial():
3737
def test_subcommand_args():
3838
result = parse_completion(Args, "bar", "--n")
3939
assert result
40-
assert result == "--nested-opt:"
40+
assert result == "--nested-opt:<nested_opt>"

0 commit comments

Comments
 (0)