/
make_bash_completion.py
executable file
·166 lines (153 loc) · 4.91 KB
/
make_bash_completion.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/env python3
import subprocess
from io import StringIO
import re
import sys
class Option:
def __init__(self, long_opt, short_opt, optional):
self.long_opt = long_opt
self.short_opt = short_opt
self.optional = optional
self.values = []
def get_all_options(cmd):
opt_pattern = re.compile(r' (?:(-.), )?(--[^\s\[=]+)(\[)?')
values_pattern = re.compile(r'\s+Possible Values: (.+)')
proc = subprocess.Popen([cmd, "--help=#all"], stdout=subprocess.PIPE)
stdoutdata, stderrdata = proc.communicate()
cur_option = None
opts = {}
for line in StringIO(stdoutdata.decode('utf-8')):
match = opt_pattern.match(line)
if match:
long_opt = match.group(2)
short_opt = match.group(1)
optional = match.group(3) == '['
if cur_option:
opts[cur_option.long_opt] = cur_option
cur_option = Option(long_opt, short_opt, optional)
else:
match = values_pattern.match(line)
if match:
cur_option.values = match.group(1).split(', ')
if cur_option:
opts[cur_option.long_opt] = cur_option
# for opt in opts.values():
# print(opt.short_opt, opt.long_opt, opt.optional, opt.values)
return opts
def output_value_case(out, key, values):
out.write("""\
{0})
COMPREPLY=( $( compgen -W '{1}' -- "$cur" ) )
return 0
;;
""".format(key, " ".join(values)))
def output_value_case_file_comp(out, key, exts):
out.write("""\
{0})
_filedir '@({1})'
return 0
;;
""".format(key, '|'.join(exts)))
def output_value_case_dir_comp(out, key):
out.write("""\
{0})
_filedir -d
return 0
;;
""".format(key))
def output_case(out, opts):
out.write("""\
_aria2c()
{
local cur prev split=false
COMPREPLY=()
COMP_WORDBREAKS=${COMP_WORDBREAKS//=}
cmd=${COMP_WORDS[0]}
_get_comp_words_by_ref cur prev
""")
bool_opts = []
nonbool_opts = []
for opt in opts.values():
if opt.values == ['true', 'false']:
bool_opts.append(opt)
else:
nonbool_opts.append(opt)
out.write("""\
case $prev in
""")
# Complete pre-defined option arguments
for long_opt in ['--ftp-type',
'--proxy-method',
'--metalink-preferred-protocol',
'--bt-min-crypto-level',
'--follow-metalink',
'--file-allocation',
'--log-level',
'--uri-selector',
'--event-poll',
'--follow-torrent',
'--stream-piece-selector',
'--download-result',
'--min-tls-version',
'--console-log-level']:
opt = opts[long_opt]
output_value_case(out, opt.long_opt, opt.values)
# Complete directory
dir_opts = []
for opt in opts.values():
if opt.values and opt.values[0] == '/path/to/directory':
dir_opts.append(opt)
# Complete file
output_value_case_dir_comp(out,'|'.join([opt.long_opt for opt in dir_opts]))
# Complete specific file type
output_value_case_file_comp(out, '--torrent-file', ['torrent'])
output_value_case_file_comp(out, '--metalink-file', ['meta4', 'metalink'])
out.write("""\
esac
""")
# Complete option name.
out.write("""\
case $cur in
-*)
COMPREPLY=( $( compgen -W '\
""")
bool_values = [ 'true', 'false' ]
for opt in opts.values():
out.write(opt.long_opt)
out.write(' ')
# Options which takes optional argument needs "=" between
# option name and value, so we complete them including "=" and
# value here.
if opt.optional:
if bool_values == opt.values:
# Because boolean option takes true when argument is
# omitted, we just complete additional 'false' option
# only.
out.write('='.join([opt.long_opt, 'false']))
out.write(' ')
else:
for value in opt.values:
out.write('='.join([opt.long_opt, value]))
out.write(' ')
out.write("""\
' -- "$cur" ) )
;;
""")
# If no option found for completion then complete with files.
out.write("""\
*)
_filedir '@(torrent|meta4|metalink|text|txt|list|lst)'
[ ${#COMPREPLY[@]} -eq 0 ] && _filedir
return 0
esac
return 0
}
complete -F _aria2c aria2c
""")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Generates aria2c(1) bash_completion using `aria2c --help=#all'")
print("Usage: make_bash_completion.py /path/to/aria2c")
exit(1)
opts = get_all_options(sys.argv[1])
output_case(sys.stdout, opts)