Coverage for typer / _completion_classes.py: 100%
114 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-26 21:46 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-26 21:46 +0000
1import importlib.util 1abcdefg
2import os 1abcdefg
3import re 1abcdefg
4import sys 1abcdefg
5from typing import Any 1abcdefg
7import click 1abcdefg
8import click.parser 1abcdefg
9import click.shell_completion 1abcdefg
10from click.shell_completion import split_arg_string as click_split_arg_string 1abcdefg
12from ._completion_shared import ( 1abcdefg
13 COMPLETION_SCRIPT_BASH,
14 COMPLETION_SCRIPT_FISH,
15 COMPLETION_SCRIPT_POWER_SHELL,
16 COMPLETION_SCRIPT_ZSH,
17 Shells,
18)
21def _sanitize_help_text(text: str) -> str: 1abcdefg
22 """Sanitizes the help text by removing rich tags"""
23 if not importlib.util.find_spec("rich"): 1abcdefg
24 return text 1abcdefg
25 from . import rich_utils 1abcdefg
27 return rich_utils.rich_render_text(text) 1abcdefg
30class BashComplete(click.shell_completion.BashComplete): 1abcdefg
31 name = Shells.bash.value 1abcdefg
32 source_template = COMPLETION_SCRIPT_BASH 1abcdefg
34 def source_vars(self) -> dict[str, Any]: 1abcdefg
35 return { 1abcdefg
36 "complete_func": self.func_name,
37 "autocomplete_var": self.complete_var,
38 "prog_name": self.prog_name,
39 }
41 def get_completion_args(self) -> tuple[list[str], str]: 1abcdefg
42 cwords = click_split_arg_string(os.environ["COMP_WORDS"]) 1abcdefg
43 cword = int(os.environ["COMP_CWORD"]) 1abcdefg
44 args = cwords[1:cword] 1abcdefg
46 try: 1abcdefg
47 incomplete = cwords[cword] 1abcdefg
48 except IndexError: 1abcdefg
49 incomplete = "" 1abcdefg
51 return args, incomplete 1abcdefg
53 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1abcdefg
54 # TODO: Explore replicating the new behavior from Click, with item types and
55 # triggering completion for files and directories
56 # return f"{item.type},{item.value}"
57 return f"{item.value}" 1abcdefg
59 def complete(self) -> str: 1abcdefg
60 args, incomplete = self.get_completion_args() 1abcdefg
61 completions = self.get_completions(args, incomplete) 1abcdefg
62 out = [self.format_completion(item) for item in completions] 1abcdefg
63 return "\n".join(out) 1abcdefg
66class ZshComplete(click.shell_completion.ZshComplete): 1abcdefg
67 name = Shells.zsh.value 1abcdefg
68 source_template = COMPLETION_SCRIPT_ZSH 1abcdefg
70 def source_vars(self) -> dict[str, Any]: 1abcdefg
71 return { 1abcdefg
72 "complete_func": self.func_name,
73 "autocomplete_var": self.complete_var,
74 "prog_name": self.prog_name,
75 }
77 def get_completion_args(self) -> tuple[list[str], str]: 1abcdefg
78 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1abcdefg
79 cwords = click_split_arg_string(completion_args) 1abcdefg
80 args = cwords[1:] 1abcdefg
81 if args and not completion_args.endswith(" "): 1abcdefg
82 incomplete = args[-1] 1abcdefg
83 args = args[:-1] 1abcdefg
84 else:
85 incomplete = "" 1abcdefg
86 return args, incomplete 1abcdefg
88 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1abcdefg
89 def escape(s: str) -> str: 1abcdefg
90 return ( 1abcdefg
91 s.replace('"', '""')
92 .replace("'", "''")
93 .replace("$", "\\$")
94 .replace("`", "\\`")
95 .replace(":", r"\\:")
96 )
98 # TODO: Explore replicating the new behavior from Click, pay attention to
99 # the difference with and without escape
100 # return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}"
101 if item.help: 1abcdefg
102 return f'"{escape(item.value)}":"{_sanitize_help_text(escape(item.help))}"' 1abcdefg
103 else:
104 return f'"{escape(item.value)}"' 1abcdefg
106 def complete(self) -> str: 1abcdefg
107 args, incomplete = self.get_completion_args() 1abcdefg
108 completions = self.get_completions(args, incomplete) 1abcdefg
109 res = [self.format_completion(item) for item in completions] 1abcdefg
110 if res: 1abcdefg
111 args_str = "\n".join(res) 1abcdefg
112 return f"_arguments '*: :(({args_str}))'" 1abcdefg
113 else:
114 return "_files" 1abcdefg
117class FishComplete(click.shell_completion.FishComplete): 1abcdefg
118 name = Shells.fish.value 1abcdefg
119 source_template = COMPLETION_SCRIPT_FISH 1abcdefg
121 def source_vars(self) -> dict[str, Any]: 1abcdefg
122 return { 1abcdefg
123 "complete_func": self.func_name,
124 "autocomplete_var": self.complete_var,
125 "prog_name": self.prog_name,
126 }
128 def get_completion_args(self) -> tuple[list[str], str]: 1abcdefg
129 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1abcdefg
130 cwords = click_split_arg_string(completion_args) 1abcdefg
131 args = cwords[1:] 1abcdefg
132 if args and not completion_args.endswith(" "): 1abcdefg
133 incomplete = args[-1] 1abcdefg
134 args = args[:-1] 1abcdefg
135 else:
136 incomplete = "" 1abcdefg
137 return args, incomplete 1abcdefg
139 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1abcdefg
140 # TODO: Explore replicating the new behavior from Click, pay attention to
141 # the difference with and without formatted help
142 # if item.help:
143 # return f"{item.type},{item.value}\t{item.help}"
145 # return f"{item.type},{item.value}
146 if item.help: 1abcdefg
147 formatted_help = re.sub(r"\s", " ", item.help) 1abcdefg
148 return f"{item.value}\t{_sanitize_help_text(formatted_help)}" 1abcdefg
149 else:
150 return f"{item.value}" 1abcdefg
152 def complete(self) -> str: 1abcdefg
153 complete_action = os.getenv("_TYPER_COMPLETE_FISH_ACTION", "") 1abcdefg
154 args, incomplete = self.get_completion_args() 1abcdefg
155 completions = self.get_completions(args, incomplete) 1abcdefg
156 show_args = [self.format_completion(item) for item in completions] 1abcdefg
157 if complete_action == "get-args": 1abcdefg
158 if show_args: 1abcdefg
159 return "\n".join(show_args) 1abcdefg
160 elif complete_action == "is-args": 1abcdefg
161 if show_args: 1abcdefg
162 # Activate complete args (no files)
163 sys.exit(0) 1abcdefg
164 else:
165 # Deactivate complete args (allow files)
166 sys.exit(1) 1abcdefg
167 return "" # pragma: no cover
170class PowerShellComplete(click.shell_completion.ShellComplete): 1abcdefg
171 name = Shells.powershell.value 1abcdefg
172 source_template = COMPLETION_SCRIPT_POWER_SHELL 1abcdefg
174 def source_vars(self) -> dict[str, Any]: 1abcdefg
175 return { 1abcdefg
176 "complete_func": self.func_name,
177 "autocomplete_var": self.complete_var,
178 "prog_name": self.prog_name,
179 }
181 def get_completion_args(self) -> tuple[list[str], str]: 1abcdefg
182 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1abcdefg
183 incomplete = os.getenv("_TYPER_COMPLETE_WORD_TO_COMPLETE", "") 1abcdefg
184 cwords = click_split_arg_string(completion_args) 1abcdefg
185 args = cwords[1:-1] if incomplete else cwords[1:] 1abcdefg
186 return args, incomplete 1abcdefg
188 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1abcdefg
189 return f"{item.value}:::{_sanitize_help_text(item.help) if item.help else ' '}" 1abcdefg
192def completion_init() -> None: 1abcdefg
193 click.shell_completion.add_completion_class(BashComplete, Shells.bash.value) 1abcdefg
194 click.shell_completion.add_completion_class(ZshComplete, Shells.zsh.value) 1abcdefg
195 click.shell_completion.add_completion_class(FishComplete, Shells.fish.value) 1abcdefg
196 click.shell_completion.add_completion_class( 1abcdefg
197 PowerShellComplete, Shells.powershell.value
198 )
199 click.shell_completion.add_completion_class(PowerShellComplete, Shells.pwsh.value) 1abcdefg