File: make_bash_completion.py

package info (click to toggle)
aria2 1.37.0%2Bdebian-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 15,128 kB
  • sloc: cpp: 115,006; ansic: 9,140; makefile: 1,466; ruby: 475; python: 373; sh: 260; xml: 176
file content (166 lines) | stat: -rwxr-xr-x 5,023 bytes parent folder | download | duplicates (2)
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)