Skip to content

Commit 31bcef2

Browse files
committed
Generate zsh completion script automatically
Modified from <https://github.com/ytdl-org/youtube-dl/tree/master/devscripts> 1. `scripts/zsh_completion.py` 2. `sudo mv _* /usr/share/zsh/site-functions` 3. `compinit`
1 parent 0af5c10 commit 31bcef2

File tree

4 files changed

+247
-2
lines changed

4 files changed

+247
-2
lines changed

Diff for: ptpython/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .repl import embed
2+
from .entry_points.run_ptpython import create_parser
23

34
__all__ = ["embed"]

Diff for: ptpython/entry_points/run_ptpython.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ def create_parser() -> _Parser:
7777
help="Run on a dark background (use light colors for text).",
7878
),
7979
parser.add_argument(
80-
"--config-file", type=str, help="Location of configuration file."
80+
"--config-file", type=str, metavar="file", help="Location of configuration file."
81+
)
82+
parser.add_argument(
83+
"--history-file", type=str, metavar="file", help="Location of history file."
8184
)
82-
parser.add_argument("--history-file", type=str, help="Location of history file.")
8385
parser.add_argument(
8486
"-V",
8587
"--version",

Diff for: scripts/zsh_completion.in

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#compdef -P ptpython[0-9.]# ptipython[0-9.]#
2+
# https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide
3+
4+
_arguments -s -S \
5+
{{flags}} \
6+
'*:file:_files'

Diff for: scripts/zsh_completion.py

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
#!/usr/bin/env python
2+
"""Generate zsh completion script.
3+
4+
Usage
5+
-----
6+
.. code-block:: zsh
7+
scripts/zsh_completion.py
8+
sudo mv _[^_]* /usr/share/zsh/site-functions # don't mv __pycache__
9+
rm -f ~/.zcompdump # optional
10+
compinit # regenerate ~/.zcompdump
11+
12+
Debug
13+
-----
14+
.. code-block:: zsh
15+
scripts/zsh_completion.py MODULE_NAME - # will output to stdout
16+
17+
Refer
18+
-----
19+
- https://github.com/ytdl-org/youtube-dl/blob/master/devscripts/zsh-completion.py
20+
- https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide
21+
22+
Examples
23+
--------
24+
.. code-block::
25+
'(- *)'{-h,--help}'[show this help message and exit]'
26+
|<-1->||<---2--->||<---------------3--------------->|
27+
28+
.. code-block:: console
29+
% foo --<TAB>
30+
option
31+
--help show this help message and exit
32+
% foo --help <TAB>
33+
no more arguments
34+
35+
.. code-block::
36+
--color'[When to show color. Default: auto. Support: auto, always, never]:when:(auto always never)'
37+
|<-2->||<------------------------------3------------------------------->||<4>||<--------5-------->|
38+
39+
.. code-block:: console
40+
% foo --color <TAB>
41+
when
42+
always
43+
auto
44+
never
45+
46+
.. code-block::
47+
--color'[When to show color. Default: auto. Support: auto, always, never]:when:((auto\:"only when output is stdout" always\:always never\:never))'
48+
|<-2->||<------------------------------3------------------------------->||<4>||<--------------------------------5------------------------------->|
49+
50+
.. code-block:: console
51+
% foo --color <TAB>
52+
when
53+
always always
54+
auto only when output is stdout
55+
never never
56+
57+
.. code-block::
58+
--config='[Config file. Default: ~/.config/foo/foo.toml]:config file:_files -g toml'
59+
|<--2-->||<---------------------3--------------------->||<---4---->||<-----5------>|
60+
61+
.. code-block:: console
62+
% foo --config <TAB>
63+
config file
64+
a.toml b/
65+
...
66+
67+
.. code-block::
68+
{1,2}'::_command_names -e'
69+
|<2->|4|<-------5------->|
70+
71+
.. code-block:: console
72+
% foo hel<TAB>
73+
_command_names -e
74+
hello friendly greeting program
75+
help2man generate a simple manual page
76+
...
77+
% foo hello hello <TAB>
78+
no more arguments
79+
80+
.. code-block::
81+
'*: :_command_names -e'
82+
2|4||<-------5------->|
83+
84+
.. code-block:: console
85+
% foo hel<TAB>
86+
external command
87+
hello friendly greeting program
88+
help2man generate a simple manual page
89+
...
90+
% foo hello hello hel<TAB>
91+
external command
92+
hello friendly greeting program
93+
help2man generate a simple manual page
94+
...
95+
96+
+----+------------+----------+------+
97+
| id | variable | requierd | expr |
98+
+====+============+==========+======+
99+
| 1 | prefix | F | (.*) |
100+
| 2 | optionstr | T | .* |
101+
| 3 | helpstr | F | [.*] |
102+
| 4 | metavar | F | :.* |
103+
| 5 | completion | F | :.* |
104+
+----+------------+----------+------+
105+
"""
106+
from argparse import (
107+
FileType,
108+
SUPPRESS,
109+
_HelpAction,
110+
_SubParsersAction,
111+
_VersionAction,
112+
)
113+
import os
114+
from os.path import dirname as dirn
115+
import sys
116+
from typing import Final, Tuple
117+
118+
from setuptools import find_packages
119+
120+
rootpath = dirn(dirn(os.path.abspath(__file__)))
121+
path = os.path.join(rootpath, "src")
122+
packages = find_packages(path)
123+
if packages == []:
124+
path = rootpath
125+
packages = find_packages(path)
126+
sys.path.insert(0, path)
127+
PACKAGES: Final = packages
128+
PACKAGE: Final = PACKAGES[0] if sys.argv[1:2] == [] else sys.argv[1]
129+
parser = __import__(PACKAGE).create_parser()
130+
actions = parser._actions
131+
BINNAME: Final = PACKAGE.replace("_", "-")
132+
BINNAMES: Final = [BINNAME]
133+
ZSH_COMPLETION_FILE: Final = (
134+
"_" + BINNAME if sys.argv[2:3] == [] else sys.argv[2]
135+
)
136+
ZSH_COMPLETION_TEMPLATE: Final = os.path.join(
137+
dirn(os.path.abspath(__file__)), "zsh_completion.in"
138+
)
139+
SUPPRESS_HELP = SUPPRESS
140+
SUPPRESS_USAGE = SUPPRESS
141+
142+
flags = []
143+
position = 1
144+
for action in actions:
145+
if action.__class__ in [_HelpAction, _VersionAction]:
146+
prefix = "'(- *)'"
147+
elif isinstance(action, _SubParsersAction): # TODO
148+
raise NotImplementedError
149+
elif action.option_strings == []:
150+
prefix = "'(-)"
151+
else:
152+
prefix = ""
153+
154+
if len(action.option_strings) > 1: # {} cannot be quoted
155+
optionstr = "{" + ",".join(action.option_strings) + "}'"
156+
elif len(action.option_strings) == 1:
157+
optionstr = action.option_strings[0] + "'"
158+
else: # action.option_strings == [], positional argument
159+
if action.nargs in ["*", "+"]:
160+
optionstr = "1" # change in template file
161+
else:
162+
if isinstance(action.nargs, int) and action.nargs > 1:
163+
old_position = position
164+
position += action.nargs
165+
optionstr = ",".join(map(str, range(old_position, position)))
166+
optionstr = "{" + optionstr + "}'"
167+
else: # action.nargs in [1, None, "?"]:
168+
optionstr = str(position) + "'"
169+
position += 1
170+
171+
if (
172+
action.help
173+
and action.help != SUPPRESS_HELP
174+
and action.option_strings != []
175+
):
176+
helpstr = action.help.replace("]", "\\]").replace("'", "'\\''")
177+
helpstr = "[" + helpstr + "]"
178+
else:
179+
helpstr = ""
180+
181+
if isinstance(action.metavar, str):
182+
metavar = action.metavar
183+
elif isinstance(action.metavar, Tuple):
184+
metavar = " ".join(map(str, action.metavar))
185+
# need some changes in template file
186+
else: # action.metavar is None
187+
if action.nargs == 0:
188+
metavar = ""
189+
elif action.option_strings == []:
190+
metavar = action.dest
191+
elif isinstance(action.type, FileType):
192+
metavar = "file"
193+
elif action.type:
194+
metavar = action.type.__name__
195+
else:
196+
metavar = action.default.__class__.__name__
197+
if metavar != "":
198+
# use lowcase conventionally
199+
metavar = metavar.lower().replace(":", "\\:")
200+
201+
if action.choices:
202+
completion = "(" + " ".join(map(str, action.choices)) + ")"
203+
elif metavar == "file":
204+
completion = "_files"
205+
elif metavar == "args":
206+
completion = "_files -g *.py"
207+
elif metavar == "dir":
208+
completion = "_dirs"
209+
elif metavar == "url":
210+
completion = "_urls"
211+
elif metavar == "command":
212+
completion = "_command_names -e"
213+
else:
214+
completion = ""
215+
216+
if metavar != "":
217+
metavar = ":" + metavar
218+
if completion != "":
219+
completion = ":" + completion
220+
221+
flag = "{0}{1}{2}{3}{4}'".format(
222+
prefix, optionstr, helpstr, metavar, completion
223+
)
224+
flags += [flag]
225+
226+
with open(ZSH_COMPLETION_TEMPLATE) as f:
227+
template = f.read()
228+
229+
template = template.replace("{{flags}}", " \\\n ".join(flags))
230+
231+
with (
232+
open(ZSH_COMPLETION_FILE, "w")
233+
if ZSH_COMPLETION_FILE != "-"
234+
else sys.stdout
235+
) as f:
236+
f.write(template)

0 commit comments

Comments
 (0)