1# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the
2# `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. Distribution of the LICENSE and NOTICE
3# files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License.
4# See https://github.com/kislyuk/argcomplete for more info.
5
6from shlex import quote
7
8bashcode = r"""#compdef %(executables)s
9# Run something, muting output or redirecting it to the debug stream
10# depending on the value of _ARC_DEBUG.
11# If ARGCOMPLETE_USE_TEMPFILES is set, use tempfiles for IPC.
12__python_argcomplete_run() {
13 if [[ -z "${ARGCOMPLETE_USE_TEMPFILES-}" ]]; then
14 __python_argcomplete_run_inner "$@"
15 return
16 fi
17 local tmpfile="$(mktemp)"
18 _ARGCOMPLETE_STDOUT_FILENAME="$tmpfile" __python_argcomplete_run_inner "$@"
19 local code=$?
20 cat "$tmpfile"
21 rm "$tmpfile"
22 return $code
23}
24
25__python_argcomplete_run_inner() {
26 if [[ -z "${_ARC_DEBUG-}" ]]; then
27 "$@" 8>&1 9>&2 1>/dev/null 2>&1 </dev/null
28 else
29 "$@" 8>&1 9>&2 1>&9 2>&1 </dev/null
30 fi
31}
32
33_python_argcomplete%(function_suffix)s() {
34 local IFS=$'\013'
35 local script="%(argcomplete_script)s"
36 if [[ -n "${ZSH_VERSION-}" ]]; then
37 local completions
38 completions=($(IFS="$IFS" \
39 COMP_LINE="$BUFFER" \
40 COMP_POINT="$CURSOR" \
41 _ARGCOMPLETE=1 \
42 _ARGCOMPLETE_SHELL="zsh" \
43 _ARGCOMPLETE_SUPPRESS_SPACE=1 \
44 __python_argcomplete_run ${script:-${words[1]}}))
45 local nosort=()
46 local nospace=()
47 if is-at-least 5.8; then
48 nosort=(-o nosort)
49 fi
50 if [[ "${completions-}" =~ ([^\\]): && "${match[1]}" =~ [=/:] ]]; then
51 nospace=(-S '')
52 fi
53 _describe "${words[1]}" completions "${nosort[@]}" "${nospace[@]}"
54 else
55 local SUPPRESS_SPACE=0
56 if compopt +o nospace 2> /dev/null; then
57 SUPPRESS_SPACE=1
58 fi
59 COMPREPLY=($(IFS="$IFS" \
60 COMP_LINE="$COMP_LINE" \
61 COMP_POINT="$COMP_POINT" \
62 COMP_TYPE="$COMP_TYPE" \
63 _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
64 _ARGCOMPLETE=1 \
65 _ARGCOMPLETE_SHELL="bash" \
66 _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
67 __python_argcomplete_run ${script:-$1}))
68 if [[ $? != 0 ]]; then
69 unset COMPREPLY
70 elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
71 compopt -o nospace
72 fi
73 fi
74}
75if [[ -z "${ZSH_VERSION-}" ]]; then
76 complete %(complete_opts)s -F _python_argcomplete%(function_suffix)s %(executables)s
77else
78 # When called by the Zsh completion system, this will end with
79 # "loadautofunc" when initially autoloaded and "shfunc" later on, otherwise,
80 # the script was "eval"-ed so use "compdef" to register it with the
81 # completion system
82 autoload is-at-least
83 if [[ $zsh_eval_context == *func ]]; then
84 _python_argcomplete%(function_suffix)s "$@"
85 else
86 compdef _python_argcomplete%(function_suffix)s %(executables)s
87 fi
88fi
89"""
90
91tcshcode = """\
92complete "%(executable)s" 'p@*@`python-argcomplete-tcsh "%(argcomplete_script)s"`@' ;
93"""
94
95fishcode = r"""
96function __fish_%(function_name)s_complete
97 set -lx _ARGCOMPLETE 1
98 set -lx _ARGCOMPLETE_DFS \t
99 set -lx _ARGCOMPLETE_IFS \n
100 set -lx _ARGCOMPLETE_SUPPRESS_SPACE 1
101 set -lx _ARGCOMPLETE_SHELL fish
102 set -lx COMP_LINE (commandline -p)
103 set -lx COMP_POINT (string length (commandline -cp))
104 set -lx COMP_TYPE
105 if set -q _ARC_DEBUG
106 %(argcomplete_script)s 8>&1 9>&2 1>&9 2>&1
107 else
108 %(argcomplete_script)s 8>&1 9>&2 1>/dev/null 2>&1
109 end
110end
111complete %(completion_arg)s %(executable)s -f -a '(__fish_%(function_name)s_complete)'
112"""
113
114powershell_code = r"""
115Register-ArgumentCompleter -Native -CommandName %(executable)s -ScriptBlock {
116 param($commandName, $wordToComplete, $cursorPosition)
117 $completion_file = New-TemporaryFile
118 $env:ARGCOMPLETE_USE_TEMPFILES = 1
119 $env:_ARGCOMPLETE_STDOUT_FILENAME = $completion_file
120 $env:COMP_LINE = $wordToComplete
121 $env:COMP_POINT = $cursorPosition
122 $env:_ARGCOMPLETE = 1
123 $env:_ARGCOMPLETE_SUPPRESS_SPACE = 0
124 $env:_ARGCOMPLETE_IFS = "`n"
125 $env:_ARGCOMPLETE_SHELL = "powershell"
126 %(argcomplete_script)s 2>&1 | Out-Null
127
128 Get-Content $completion_file | ForEach-Object {
129 [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_)
130 }
131 Remove-Item $completion_file, Env:\_ARGCOMPLETE_STDOUT_FILENAME, Env:\ARGCOMPLETE_USE_TEMPFILES, Env:\COMP_LINE, Env:\COMP_POINT, Env:\_ARGCOMPLETE, Env:\_ARGCOMPLETE_SUPPRESS_SPACE, Env:\_ARGCOMPLETE_IFS, Env:\_ARGCOMPLETE_SHELL
132}
133""" # noqa: E501
134
135shell_codes = {"bash": bashcode, "tcsh": tcshcode, "fish": fishcode, "powershell": powershell_code}
136
137
138def shellcode(executables, use_defaults=True, shell="bash", complete_arguments=None, argcomplete_script=None):
139 """
140 Provide the shell code required to register a python executable for use with the argcomplete module.
141
142 :param list(str) executables: Executables to be completed (when invoked exactly with this name)
143 :param bool use_defaults: Whether to fallback to readline's default completion when no matches are generated
144 (affects bash only)
145 :param str shell: Name of the shell to output code for
146 :param complete_arguments: Arguments to call complete with (affects bash only)
147 :type complete_arguments: list(str) or None
148 :param argcomplete_script: Script to call complete with, if not the executable to complete.
149 If supplied, will be used to complete *all* passed executables.
150 :type argcomplete_script: str or None
151 """
152
153 if complete_arguments is None:
154 complete_options = "-o nospace -o default -o bashdefault" if use_defaults else "-o nospace -o bashdefault"
155 else:
156 complete_options = " ".join(complete_arguments)
157
158 if shell == "bash" or shell == "zsh":
159 quoted_executables = [quote(i) for i in executables]
160 executables_list = " ".join(quoted_executables)
161 script = argcomplete_script
162 if script:
163 # If the script path contain a space, this would generate an invalid function name.
164 function_suffix = "_" + script.replace(" ", "_SPACE_")
165 else:
166 script = ""
167 function_suffix = ""
168 code = bashcode % dict(
169 complete_opts=complete_options,
170 executables=executables_list,
171 argcomplete_script=script,
172 function_suffix=function_suffix,
173 )
174 elif shell == "fish":
175 code = ""
176 for executable in executables:
177 script = argcomplete_script or executable
178 completion_arg = "--path" if "/" in executable else "--command" # use path for absolute paths
179 function_name = executable.replace("/", "_") # / not allowed in function name
180
181 code += fishcode % dict(
182 executable=executable,
183 argcomplete_script=script,
184 completion_arg=completion_arg,
185 function_name=function_name,
186 )
187 elif shell == "powershell":
188 code = ""
189 for executable in executables:
190 script = argcomplete_script or executable
191 code += powershell_code % dict(executable=executable, argcomplete_script=script)
192
193 else:
194 code = ""
195 for executable in executables:
196 script = argcomplete_script
197 # If no script was specified, default to the executable being completed.
198 if not script:
199 script = executable
200 code += shell_codes.get(shell, "") % dict(executable=executable, argcomplete_script=script)
201
202 return code