Я пишу сценарий завершения zsh
для произвольных сценариев python
(аналогично argcomplete
).
Я пытаюсь заставить сценарий работать в несколько вариантов использования:
- Сценарий вызывается напрямую (например,
~/script.py
) - Сценарий вызывается через
python
(например, python script.py
) - Сценарий вызывается как модуль
python
(например, python -m script
)
. Мне до сих пор удавалось успешно обработать первый случай, но во втором случае не удается получить какие-либо завершения. Используя zsh
журнал отладки завершения, я смог увидеть, где что-то пошло не так:
Функция _arguments
вызывает встроенную функцию с именем comparguments
. В первом случае функция возвращает 0, и поток управления продолжается, как и ожидалось. Во втором случае происходит сбой функции, и _arguments
немедленно возвращает 1. Это происходит, даже если аргументы comparguments
идентичны в обоих случаях.
Здесь - ссылка на журналы отладки для обеих ситуаций. Для первого сценария comparguments
вызывается в строке 199, а во втором сценарии он вызывается в строке 197.
Мой сценарий:
#compdef -p *
_python_script() {
# Expand all words
local -a expanded_words
__pyzshcomplete_exapnd_tilde_in_all_words
# Check if we should run or else quit
__pyzshcomplete_should_run || return 1
# Skip any other completion function
_compskip=all
# Retrieve valid completions and pass them to _arguments
local arguments=(
${(f)"$(PYZSHCOMPLETE=1 __python_argcomplete_run ${expanded_words[@]})"}
)
_arguments -s -w : ${arguments[@]}
# Always return 0 - if _arguments fails, compsys for some reason invokes
# this script again consuming more time and gaining nothing.
# If we are in this context there shouldn't be other completions anyways so
# no reason to return 1 anyways...
return 0
}
__pyzshcomplete_exapnd_tilde_in_all_words() {
for ((i = 1; i <= $#words; i++)); do
expanded_words[$i]=${~words[$i]}
done
}
### The following code is taken from the argcomplete project, including
### original copyright. Changes from the original will be marked by a comment
### Starting with CHANGE.
### Original code:
### https://github.com/kislyuk/argcomplete/blob/v1.11.1/argcomplete/bash_completion.d/python-argcomplete
# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors.
# Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info.
# CHANGE: This function is a heavily refactored copy of the first part of
# _python_argcomplete_global
__pyzshcomplete_should_run() {
local executable=${expanded_words[1]}
if [[ $executable == python* ]] || [[ $executable == pypy* ]]; then
# If 2nd word is the -m flag, check that the module has the magic string
[[ ${expanded_words[2]} == -m ]] && __python_argcomplete_run \
$executable -m argcomplete._check_module ${expanded_words[3]} && \
return 0
# If 2nd word is a file, check that it has the magic string
[[ -f ${expanded_words[2]} ]] && __python_argcomplete_scan_head_noerr \
${expanded_words[2]} && return 0
return 1
fi
# Assume the first word is a script and find its path
local script_path
# Search in path
if type -p $executable > /dev/null 2>&1; then
script_path=$(type -p $executable | sed -r "s:$executable is ::")
# Check if it's a file
elif [[ -f $executable ]]; then
script_path=$executable
fi
# If found a path, scan for magic
if [[ -n $script_path ]]; then
__python_argcomplete_scan_head_noerr $script_path && return 0
return 1
fi
return 1
}
# Run something, muting output or redirecting it to the debug stream
# depending on the value of _ARC_DEBUG.
__python_argcomplete_run() {
if [[ -z "$_ARC_DEBUG" ]]; then
"$@" 8>&1 9>&2 1>/dev/null 2>&1
else
"$@" 8>&1 9>&2 1>&9 2>&1
fi
}
# Scan the beginning of an executable file ($1) for a regexp ($2). By default,
# scan for the magic string indicating that the executable supports the
# argcomplete completion protocol. Scan the first kilobyte.
__python_argcomplete_scan_head() {
# CHANGE: the zsh read builtin has different options and behaves differently
read -s -r -k 1024 -u 0 < "$1"
[[ "$REPLY" =~ ${2:-PYTHON_ARGCOMPLETE_OK} ]]
}
__python_argcomplete_scan_head_noerr() {
__python_argcomplete_scan_head "$@" 2>/dev/null
}
РЕДАКТИРОВАТЬ:
Чтобы временно обойти проблему, я попытался добавить shift words
перед вызовом _arguments
. Это привело к успешному завершению работы comparguments
(!), Но все равно вызывает сбой _arguments
с сообщением no arguments
.
Я добавил журнал для этого случая в сущность, связанную выше.