Coverage for typer/_completion_classes.py: 100%

115 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2025-04-14 00:18 +0000

1import importlib.util 1iabcdefgh

2import os 1iabcdefgh

3import re 1iabcdefgh

4import sys 1iabcdefgh

5from typing import Any, Dict, List, Tuple 1iabcdefgh

6 

7import click 1iabcdefgh

8import click.parser 1iabcdefgh

9import click.shell_completion 1iabcdefgh

10 

11from ._completion_shared import ( 1iabcdefgh

12 COMPLETION_SCRIPT_BASH, 

13 COMPLETION_SCRIPT_FISH, 

14 COMPLETION_SCRIPT_POWER_SHELL, 

15 COMPLETION_SCRIPT_ZSH, 

16 Shells, 

17) 

18 

19try: 1iabcdefgh

20 import shellingham 1iabcdefgh

21except ImportError: # pragma: no cover 

22 shellingham = None 

23 

24 

25def _sanitize_help_text(text: str) -> str: 1iabcdefgh

26 """Sanitizes the help text by removing rich tags""" 

27 if not importlib.util.find_spec("rich"): 1iabcdefgh

28 return text 1iabcdefgh

29 from . import rich_utils 1iabcdefgh

30 

31 return rich_utils.rich_render_text(text) 1iabcdefgh

32 

33 

34class BashComplete(click.shell_completion.BashComplete): 1iabcdefgh

35 name = Shells.bash.value 1iabcdefgh

36 source_template = COMPLETION_SCRIPT_BASH 1iabcdefgh

37 

38 def source_vars(self) -> Dict[str, Any]: 1iabcdefgh

39 return { 1abcdefgh

40 "complete_func": self.func_name, 

41 "autocomplete_var": self.complete_var, 

42 "prog_name": self.prog_name, 

43 } 

44 

45 def get_completion_args(self) -> Tuple[List[str], str]: 1iabcdefgh

46 cwords = click.parser.split_arg_string(os.environ["COMP_WORDS"]) 1iabcdefgh

47 cword = int(os.environ["COMP_CWORD"]) 1iabcdefgh

48 args = cwords[1:cword] 1iabcdefgh

49 

50 try: 1iabcdefgh

51 incomplete = cwords[cword] 1iabcdefgh

52 except IndexError: 1iabcdefgh

53 incomplete = "" 1iabcdefgh

54 

55 return args, incomplete 1iabcdefgh

56 

57 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1iabcdefgh

58 # TODO: Explore replicating the new behavior from Click, with item types and 

59 # triggering completion for files and directories 

60 # return f"{item.type},{item.value}" 

61 return f"{item.value}" 1iabcdefgh

62 

63 def complete(self) -> str: 1iabcdefgh

64 args, incomplete = self.get_completion_args() 1iabcdefgh

65 completions = self.get_completions(args, incomplete) 1iabcdefgh

66 out = [self.format_completion(item) for item in completions] 1iabcdefgh

67 return "\n".join(out) 1iabcdefgh

68 

69 

70class ZshComplete(click.shell_completion.ZshComplete): 1iabcdefgh

71 name = Shells.zsh.value 1iabcdefgh

72 source_template = COMPLETION_SCRIPT_ZSH 1iabcdefgh

73 

74 def source_vars(self) -> Dict[str, Any]: 1iabcdefgh

75 return { 1abcdefgh

76 "complete_func": self.func_name, 

77 "autocomplete_var": self.complete_var, 

78 "prog_name": self.prog_name, 

79 } 

80 

81 def get_completion_args(self) -> Tuple[List[str], str]: 1iabcdefgh

82 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1iabcdefgh

83 cwords = click.parser.split_arg_string(completion_args) 1iabcdefgh

84 args = cwords[1:] 1iabcdefgh

85 if args and not completion_args.endswith(" "): 1iabcdefgh

86 incomplete = args[-1] 1iabcdefgh

87 args = args[:-1] 1iabcdefgh

88 else: 

89 incomplete = "" 1iabcdefgh

90 return args, incomplete 1iabcdefgh

91 

92 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1iabcdefgh

93 def escape(s: str) -> str: 1iabcdefgh

94 return ( 1abcdefgh

95 s.replace('"', '""') 

96 .replace("'", "''") 

97 .replace("$", "\\$") 

98 .replace("`", "\\`") 

99 .replace(":", r"\\:") 

100 ) 

101 

102 # TODO: Explore replicating the new behavior from Click, pay attention to 

103 # the difference with and without escape 

104 # return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" 

105 if item.help: 1iabcdefgh

106 return f'"{escape(item.value)}":"{_sanitize_help_text(escape(item.help))}"' 1iabcdefgh

107 else: 

108 return f'"{escape(item.value)}"' 1iabcdefgh

109 

110 def complete(self) -> str: 1iabcdefgh

111 args, incomplete = self.get_completion_args() 1iabcdefgh

112 completions = self.get_completions(args, incomplete) 1iabcdefgh

113 res = [self.format_completion(item) for item in completions] 1iabcdefgh

114 if res: 1iabcdefgh

115 args_str = "\n".join(res) 1iabcdefgh

116 return f"_arguments '*: :(({args_str}))'" 1iabcdefgh

117 else: 

118 return "_files" 1iabcdefgh

119 

120 

121class FishComplete(click.shell_completion.FishComplete): 1iabcdefgh

122 name = Shells.fish.value 1iabcdefgh

123 source_template = COMPLETION_SCRIPT_FISH 1iabcdefgh

124 

125 def source_vars(self) -> Dict[str, Any]: 1iabcdefgh

126 return { 1abcdefgh

127 "complete_func": self.func_name, 

128 "autocomplete_var": self.complete_var, 

129 "prog_name": self.prog_name, 

130 } 

131 

132 def get_completion_args(self) -> Tuple[List[str], str]: 1iabcdefgh

133 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1iabcdefgh

134 cwords = click.parser.split_arg_string(completion_args) 1iabcdefgh

135 args = cwords[1:] 1iabcdefgh

136 if args and not completion_args.endswith(" "): 1iabcdefgh

137 incomplete = args[-1] 1iabcdefgh

138 args = args[:-1] 1iabcdefgh

139 else: 

140 incomplete = "" 1iabcdefgh

141 return args, incomplete 1iabcdefgh

142 

143 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1iabcdefgh

144 # TODO: Explore replicating the new behavior from Click, pay attention to 

145 # the difference with and without formatted help 

146 # if item.help: 

147 # return f"{item.type},{item.value}\t{item.help}" 

148 

149 # return f"{item.type},{item.value} 

150 if item.help: 1iabcdefgh

151 formatted_help = re.sub(r"\s", " ", item.help) 1iabcdefgh

152 return f"{item.value}\t{_sanitize_help_text(formatted_help)}" 1iabcdefgh

153 else: 

154 return f"{item.value}" 1iabcdefgh

155 

156 def complete(self) -> str: 1iabcdefgh

157 complete_action = os.getenv("_TYPER_COMPLETE_FISH_ACTION", "") 1iabcdefgh

158 args, incomplete = self.get_completion_args() 1iabcdefgh

159 completions = self.get_completions(args, incomplete) 1iabcdefgh

160 show_args = [self.format_completion(item) for item in completions] 1iabcdefgh

161 if complete_action == "get-args": 1iabcdefgh

162 if show_args: 1iabcdefgh

163 return "\n".join(show_args) 1iabcdefgh

164 elif complete_action == "is-args": 1iabcdefgh

165 if show_args: 1iabcdefgh

166 # Activate complete args (no files) 

167 sys.exit(0) 1iabcdefgh

168 else: 

169 # Deactivate complete args (allow files) 

170 sys.exit(1) 1iabcdefgh

171 return "" # pragma: no cover 

172 

173 

174class PowerShellComplete(click.shell_completion.ShellComplete): 1iabcdefgh

175 name = Shells.powershell.value 1iabcdefgh

176 source_template = COMPLETION_SCRIPT_POWER_SHELL 1iabcdefgh

177 

178 def source_vars(self) -> Dict[str, Any]: 1iabcdefgh

179 return { 1abcdefgh

180 "complete_func": self.func_name, 

181 "autocomplete_var": self.complete_var, 

182 "prog_name": self.prog_name, 

183 } 

184 

185 def get_completion_args(self) -> Tuple[List[str], str]: 1iabcdefgh

186 completion_args = os.getenv("_TYPER_COMPLETE_ARGS", "") 1iabcdefgh

187 incomplete = os.getenv("_TYPER_COMPLETE_WORD_TO_COMPLETE", "") 1iabcdefgh

188 cwords = click.parser.split_arg_string(completion_args) 1iabcdefgh

189 args = cwords[1:-1] if incomplete else cwords[1:] 1iabcdefgh

190 return args, incomplete 1iabcdefgh

191 

192 def format_completion(self, item: click.shell_completion.CompletionItem) -> str: 1iabcdefgh

193 return f"{item.value}:::{_sanitize_help_text(item.help) if item.help else ' '}" 1iabcdefgh

194 

195 

196def completion_init() -> None: 1iabcdefgh

197 click.shell_completion.add_completion_class(BashComplete, Shells.bash.value) 1iabcdefgh

198 click.shell_completion.add_completion_class(ZshComplete, Shells.zsh.value) 1iabcdefgh

199 click.shell_completion.add_completion_class(FishComplete, Shells.fish.value) 1iabcdefgh

200 click.shell_completion.add_completion_class( 1iabcdefgh

201 PowerShellComplete, Shells.powershell.value 

202 ) 

203 click.shell_completion.add_completion_class(PowerShellComplete, Shells.pwsh.value) 1iabcdefgh