|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +""" |
| 4 | +truckersmp-cli completions generator. |
| 5 | +
|
| 6 | +This script generates completions for bash and zsh. |
| 7 | +""" |
| 8 | + |
| 9 | +import argparse |
| 10 | +import json |
| 11 | +import locale |
| 12 | +import sys |
| 13 | +from string import Template |
| 14 | + |
| 15 | +from truckersmp_cli.args import ACTIONS, GAMES, create_arg_parser |
| 16 | +from truckersmp_cli.variables import AppId, File |
| 17 | + |
| 18 | + |
| 19 | +class CompletionTemplate(Template): |
| 20 | + """Template class that uses "%" as delimiter.""" |
| 21 | + |
| 22 | + delimiter = "%" |
| 23 | + |
| 24 | + |
| 25 | +TMPL_BASH = CompletionTemplate( |
| 26 | + """# bash completion for truckersmp-cli -*- shell-script -*- |
| 27 | +
|
| 28 | +_truckersmp_cli() { |
| 29 | + local cur prev |
| 30 | + _get_comp_words_by_ref -n : cur prev |
| 31 | +
|
| 32 | + if [[ "${cur}" == -* ]]; then |
| 33 | + COMPREPLY=( $(compgen -W "%{tmpl_bash_options}" -- "${cur}") ) |
| 34 | + return |
| 35 | + fi |
| 36 | +
|
| 37 | + local diropts="%{tmpl_dir_options_regex}" |
| 38 | + if [[ "${prev}" =~ ${diropts} ]]; then |
| 39 | + local IFS=$'\\n' |
| 40 | + compopt -o filenames |
| 41 | + COMPREPLY=( $(compgen -d -- "${cur}") ) |
| 42 | + return |
| 43 | + fi |
| 44 | + local fileopts="%{tmpl_file_options_regex}" |
| 45 | + if [[ "${prev}" =~ ${fileopts} ]]; then |
| 46 | + local IFS=$'\\n' |
| 47 | + compopt -o filenames |
| 48 | + COMPREPLY=( $(compgen -f -- "${cur}") ) |
| 49 | + return |
| 50 | + fi |
| 51 | +
|
| 52 | + local games="%{tmpl_games_regex}" |
| 53 | + for ((i=1; i < ${COMP_CWORD}; i++)); do |
| 54 | + if [[ "${COMP_WORDS[i]}" =~ ${games} ]]; then |
| 55 | + COMPREPLY=() |
| 56 | + return |
| 57 | + fi |
| 58 | + done |
| 59 | +
|
| 60 | + local acts="%{tmpl_actions_regex}" |
| 61 | + for ((i=1; i < ${COMP_CWORD}; i++)); do |
| 62 | + if [[ "${COMP_WORDS[i]}" =~ ${acts} ]]; then |
| 63 | + COMPREPLY=( $(compgen -W "%{tmpl_games}" -- "${cur}") ) |
| 64 | + return |
| 65 | + fi |
| 66 | + done |
| 67 | +
|
| 68 | + COMPREPLY=( $(compgen -W "%{tmpl_actions}" -- "${cur}") ) |
| 69 | +} && |
| 70 | + complete -F _truckersmp_cli truckersmp-cli |
| 71 | +""") |
| 72 | + |
| 73 | +TMPL_ZSH = CompletionTemplate( |
| 74 | + """#compdef truckersmp-cli -*- shell-script -*- |
| 75 | +# zsh completion for truckersmp-cli |
| 76 | +
|
| 77 | +__truckersmp_cli() { |
| 78 | + typeset -A opt_args |
| 79 | + local prev="${words[CURRENT-1]}" |
| 80 | + local diropts="%{tmpl_dir_options_regex}" |
| 81 | + local fileopts="%{tmpl_file_options_regex}" |
| 82 | +
|
| 83 | + if [[ "${prev}" =~ ${diropts} ]]; then |
| 84 | + _path_files -/ |
| 85 | + return |
| 86 | + elif [[ "${prev}" =~ ${fileopts} ]]; then |
| 87 | + _path_files |
| 88 | + return |
| 89 | + fi |
| 90 | +
|
| 91 | + _arguments -s -S "1: :->action" "2: :->game" %{tmpl_zsh_options} |
| 92 | +
|
| 93 | + case "${state}" in |
| 94 | + action) |
| 95 | + _values action '%{tmpl_zsh_actions}' |
| 96 | + ;; |
| 97 | + game) |
| 98 | + _values game '%{tmpl_zsh_games}' |
| 99 | + ;; |
| 100 | + esac |
| 101 | +} |
| 102 | +
|
| 103 | +__truckersmp_cli |
| 104 | +""") |
| 105 | + |
| 106 | + |
| 107 | +def write_shell_completion_file(shellname, path, content): |
| 108 | + """ |
| 109 | + Write given content to specified completion file. |
| 110 | +
|
| 111 | + If path is None, this function does nothing and returns True. |
| 112 | + If it successfully writes data to the file, |
| 113 | + this function returns True. |
| 114 | + If it fails to write, it prints an error message |
| 115 | + and returns False. |
| 116 | +
|
| 117 | + shellname: Shell name, used in messages |
| 118 | + path: Path to output completion file |
| 119 | + content: Content of completion file |
| 120 | + """ |
| 121 | + if path is None: |
| 122 | + return True |
| 123 | + |
| 124 | + try: |
| 125 | + with open(path, "w") as f_out: |
| 126 | + f_out.write(content) |
| 127 | + except OSError as ex: |
| 128 | + print( |
| 129 | + "Failed to write {} completion file {}: {}".format( |
| 130 | + shellname, path, ex), |
| 131 | + file=sys.stderr) |
| 132 | + return False |
| 133 | + |
| 134 | + print("Wrote {} completion file {}".format(shellname, path)) |
| 135 | + return True |
| 136 | + |
| 137 | + |
| 138 | +def main(): |
| 139 | + """Generate completions for bash and zsh.""" |
| 140 | + try: |
| 141 | + with open(File.proton_json) as f_in: |
| 142 | + AppId.proton = json.load(f_in) |
| 143 | + except (OSError, ValueError) as ex: |
| 144 | + sys.exit("Failed to load proton.json: {}".format(ex)) |
| 145 | + |
| 146 | + parser = argparse.ArgumentParser( |
| 147 | + description="Shell completions generator") |
| 148 | + parser.add_argument( |
| 149 | + "-b", "--bash-completion", metavar="FILE", |
| 150 | + help="write bash completion file.") |
| 151 | + parser.add_argument( |
| 152 | + "-z", "--zsh-completion", metavar="FILE", |
| 153 | + help="write zsh completion file.") |
| 154 | + config = parser.parse_args() |
| 155 | + |
| 156 | + if config.bash_completion is None and config.zsh_completion is None: |
| 157 | + return "At least --bash-completion(-b) or --zsh-completion(-z) option needed." |
| 158 | + |
| 159 | + comp_data = dict( |
| 160 | + action_names=[act[0] for act in ACTIONS], |
| 161 | + bash_options=["-h --help"], |
| 162 | + dir_options=[], |
| 163 | + file_options=[], |
| 164 | + game_names=[game[0] for game in GAMES], |
| 165 | + zsh_actions=[], |
| 166 | + zsh_games=[], |
| 167 | + zsh_options=['{-h,--help}"[show help message and exit]:"', ], |
| 168 | + ) |
| 169 | + for act in create_arg_parser()[1]: |
| 170 | + if act.help.startswith("**DEPRECATED** "): |
| 171 | + continue |
| 172 | + if len(act.option_strings) > 1: |
| 173 | + zsh_arg_optnames = "{" + ",".join(act.option_strings) + "}" |
| 174 | + else: |
| 175 | + zsh_arg_optnames = act.option_strings[0] |
| 176 | + for opt in act.option_strings: |
| 177 | + comp_data["bash_options"].append(opt) |
| 178 | + if opt.endswith("dir"): |
| 179 | + comp_data["dir_options"].append(opt) |
| 180 | + elif opt.endswith("file"): |
| 181 | + comp_data["file_options"].append(opt) |
| 182 | + desc = act.help.replace("[", "\\[").replace("]", "\\]").replace('"', '\\"') |
| 183 | + comp_data["zsh_options"].append( |
| 184 | + '{}"[{}]"'.format(zsh_arg_optnames, " ".join(desc.split()))) |
| 185 | + for name, desc in ACTIONS: |
| 186 | + comp_data["zsh_actions"].append("{}[{}]".format(name, desc)) |
| 187 | + for name, desc in GAMES: |
| 188 | + comp_data["zsh_games"].append("{}[{}]".format(name, desc)) |
| 189 | + |
| 190 | + return ( |
| 191 | + write_shell_completion_file( |
| 192 | + "bash", config.bash_completion, TMPL_BASH.substitute( |
| 193 | + tmpl_actions=" ".join(comp_data["action_names"]), |
| 194 | + tmpl_actions_regex="|".join(comp_data["action_names"]), |
| 195 | + tmpl_bash_options=" ".join(comp_data["bash_options"]), |
| 196 | + tmpl_dir_options_regex="|".join(comp_data["dir_options"]), |
| 197 | + tmpl_file_options_regex="|".join(comp_data["file_options"]), |
| 198 | + tmpl_games=" ".join(comp_data["game_names"]), |
| 199 | + tmpl_games_regex="|".join(comp_data["game_names"]), |
| 200 | + ) |
| 201 | + ) is False or write_shell_completion_file( |
| 202 | + "zsh", config.zsh_completion, TMPL_ZSH.substitute( |
| 203 | + tmpl_dir_options_regex="|".join(comp_data["dir_options"]), |
| 204 | + tmpl_file_options_regex="|".join(comp_data["file_options"]), |
| 205 | + tmpl_zsh_actions="' '".join(comp_data["zsh_actions"]), |
| 206 | + tmpl_zsh_games="' '".join(comp_data["zsh_games"]), |
| 207 | + tmpl_zsh_options=" ".join(comp_data["zsh_options"]), |
| 208 | + ) |
| 209 | + ) is False |
| 210 | + ) |
| 211 | + |
| 212 | + |
| 213 | +if __name__ == "__main__": |
| 214 | + locale.setlocale(locale.LC_MESSAGES, "") |
| 215 | + locale.setlocale(locale.LC_TIME, "C") |
| 216 | + sys.exit(main()) |
0 commit comments