shell_integration.py 7.7 KB

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