Porytiles
Loading...
Searching...
No Matches
command_completion.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <iostream>
4#include <string>
5
6#include "CLI/CLI.hpp"
7
9
10#include "command.hpp"
11
25class CompletionCommand final : public Command {
26 public:
27 explicit CompletionCommand(CLI::App &parent_app) : Command{parent_app, command_name, command_desc, command_group}
28 {
29 CLI::App &cmd = get_app();
30 cmd.add_option("<shell>", shell_, "Shell type: bash, zsh, or fish")->required();
31 }
32
33 private:
34 void Run() override
35 {
36 if (shell_ == "bash") {
37 output_bash_completion();
38 }
39 else if (shell_ == "zsh") {
40 output_zsh_completion();
41 }
42 else if (shell_ == "fish") {
43 output_fish_completion();
44 }
45 else {
46 std::cerr << "Unknown shell type: " << shell_ << "\n";
47 std::cerr << "Supported shells: bash, zsh, fish\n";
48 throw CLI::RuntimeError{1};
49 }
50 }
51
52 static constexpr auto command_name = "completion";
53 static constexpr auto command_desc = "Generate shell completion scripts.";
54 static constexpr auto command_group = "UTILITIES";
55 std::string shell_;
56
63 [[nodiscard]] static std::string escape_fish_single_quoted(const std::string &text)
64 {
65 std::string escaped;
66 escaped.reserve(text.size());
67 for (const char c : text) {
68 if (c == '\'' || c == '\\') {
69 escaped += '\\';
70 }
71 escaped += c;
72 }
73 return escaped;
74 }
75
81 [[nodiscard]] static std::string escape_zsh_single_quoted(const std::string &text)
82 {
83 std::string escaped;
84 escaped.reserve(text.size());
85 for (const char c : text) {
86 if (c == '\'') {
87 escaped += "'\\''";
88 }
89 else {
90 escaped += c;
91 }
92 }
93 return escaped;
94 }
95
96 void output_bash_completion()
97 {
98 std::cout << "# Bash completion for porytiles\n";
99 std::cout << "# Add to ~/.bashrc or ~/.bash_completion:\n";
100 std::cout << "# eval \"$(porytiles completion bash)\"\n";
101 std::cout << "\n";
102
103 // Helper function to extract project root from command line
104 std::cout << "# Helper function to extract --project-root or -C from command line\n";
105 std::cout << "_porytiles_get_project_root() {\n";
106 std::cout << " local i\n";
107 std::cout << " for ((i=1; i < ${#COMP_WORDS[@]}; i++)); do\n";
108 std::cout << " case \"${COMP_WORDS[i]}\" in\n";
109 std::cout << " -C|--project-root)\n";
110 std::cout << " if [[ $((i+1)) -lt ${#COMP_WORDS[@]} ]]; then\n";
111 std::cout << " echo \"${COMP_WORDS[$((i+1))]}\"\n";
112 std::cout << " return\n";
113 std::cout << " fi\n";
114 std::cout << " ;;\n";
115 std::cout << " esac\n";
116 std::cout << " done\n";
117 std::cout << " echo \".\"\n";
118 std::cout << "}\n";
119 std::cout << "\n";
120
121 // Helper function to get tileset completions
122 std::cout << "# Helper function to get tileset completions\n";
123 std::cout << "_porytiles_complete_tilesets() {\n";
124 std::cout << " local filter=\"$1\"\n";
125 std::cout << " local project_root\n";
126 std::cout << " project_root=$(_porytiles_get_project_root)\n";
127 std::cout << " porytiles list-tilesets -C \"$project_root\" --filter \"$filter\" --prefix \"$cur\" "
128 "2>/dev/null\n";
129 std::cout << "}\n";
130 std::cout << "\n";
131
132 std::cout << "_porytiles_completions() {\n";
133 std::cout << " local cur prev opts subcommand\n";
134 std::cout << " COMPREPLY=()\n";
135 std::cout << " cur=\"${COMP_WORDS[COMP_CWORD]}\"\n";
136 std::cout << " prev=\"${COMP_WORDS[COMP_CWORD-1]}\"\n";
137 std::cout << "\n";
138
139 // Find the subcommand
140 std::cout << " # Find the subcommand\n";
141 std::cout << " subcommand=\"\"\n";
142 std::cout << " for ((i=1; i < COMP_CWORD; i++)); do\n";
143 std::cout << " case \"${COMP_WORDS[i]}\" in\n";
144 std::cout << " compile-tileset|create-tileset|decompile-tileset|dump-attribute-schema|"
145 "dump-tileset-config|import-tileset|list-tilesets|completion)\n";
146 std::cout << " subcommand=\"${COMP_WORDS[i]}\"\n";
147 std::cout << " break\n";
148 std::cout << " ;;\n";
149 std::cout << " esac\n";
150 std::cout << " done\n";
151 std::cout << "\n";
152
153 std::cout << " # Main commands\n";
154 std::cout << " local commands=\"compile-tileset create-tileset decompile-tileset dump-attribute-schema "
155 "dump-tileset-config import-tileset list-tilesets completion\"\n";
156 std::cout << "\n";
157 std::cout << " # Config options\n";
158 std::cout << " local config_opts=\"";
159
161 for (const auto &opt : meta) {
162 std::cout << "--" << opt.long_name << " ";
163 if (opt.has_negation) {
164 std::cout << "--no-" << opt.long_name << " ";
165 }
166 }
167
168 std::cout << "\"\n";
169 std::cout << "\n";
170
171 // Handle completion based on context
172 std::cout << " # If no subcommand yet, complete commands\n";
173 std::cout << " if [[ -z \"$subcommand\" ]]; then\n";
174 std::cout << " if [[ ${cur} == -* ]]; then\n";
175 std::cout << " COMPREPLY=( $(compgen -W \"-C --project-root\" -- ${cur}) )\n";
176 std::cout << " else\n";
177 std::cout << " COMPREPLY=( $(compgen -W \"${commands}\" -- ${cur}) )\n";
178 std::cout << " fi\n";
179 std::cout << " return 0\n";
180 std::cout << " fi\n";
181 std::cout << "\n";
182
183 std::cout << " case \"${prev}\" in\n";
184
185 // Add case statements for enum options
186 for (const auto &opt : meta) {
187 if (!opt.choices.empty()) {
188 std::cout << " --" << opt.long_name << ")\n";
189 std::cout << " COMPREPLY=( $(compgen -W \"";
190 for (std::size_t i = 0; i < opt.choices.size(); ++i) {
191 if (i > 0) {
192 std::cout << " ";
193 }
194 std::cout << opt.choices[i];
195 }
196 std::cout << "\" -- ${cur}) )\n";
197 std::cout << " return 0\n";
198 std::cout << " ;;\n";
199 }
200 }
201
202 std::cout << " -C|--project-root)\n";
203 std::cout << " # Complete directories\n";
204 std::cout << " COMPREPLY=( $(compgen -d -- ${cur}) )\n";
205 std::cout << " return 0\n";
206 std::cout << " ;;\n";
207 std::cout << " *)\n";
208 std::cout << " ;;\n";
209 std::cout << " esac\n";
210 std::cout << "\n";
211
212 // Options completion (include command-specific flags)
213 std::cout << " if [[ ${cur} == -* ]]; then\n";
214 std::cout << " local extra_opts=\"\"\n";
215 std::cout << " if [[ \"$subcommand\" == \"create-tileset\" ]]; then\n";
216 std::cout << " extra_opts=\"--secondary\"\n";
217 std::cout << " elif [[ \"$subcommand\" == \"dump-attribute-schema\" || \"$subcommand\" == "
218 "\"dump-tileset-config\" ]]; then\n";
219 std::cout << " extra_opts=\"--allow-missing-tileset\"\n";
220 std::cout << " fi\n";
221 std::cout << " COMPREPLY=( $(compgen -W \"${config_opts} ${extra_opts}\" -- ${cur}) )\n";
222 std::cout << " return 0\n";
223 std::cout << " fi\n";
224 std::cout << "\n";
225
226 // Tileset name completion based on subcommand
227 std::cout << " # Complete tileset names based on subcommand\n";
228 std::cout << " case \"$subcommand\" in\n";
229 std::cout << " compile-tileset|decompile-tileset)\n";
230 std::cout << " # Only managed tilesets\n";
231 std::cout << " COMPREPLY=( $(_porytiles_complete_tilesets managed) )\n";
232 std::cout << " ;;\n";
233 std::cout << " dump-attribute-schema|dump-tileset-config)\n";
234 std::cout << " # All tilesets (config and schema can be dumped for any)\n";
235 std::cout << " COMPREPLY=( $(_porytiles_complete_tilesets all) )\n";
236 std::cout << " ;;\n";
237 std::cout << " import-tileset)\n";
238 std::cout << " # Only unmanaged tilesets\n";
239 std::cout << " COMPREPLY=( $(_porytiles_complete_tilesets unmanaged) )\n";
240 std::cout << " ;;\n";
241 std::cout << " create-tileset)\n";
242 std::cout << " # No completion - user provides new name\n";
243 std::cout << " ;;\n";
244 std::cout << " list-tilesets)\n";
245 std::cout << " # Complete --filter values\n";
246 std::cout << " if [[ \"${prev}\" == \"--filter\" ]]; then\n";
247 std::cout << " COMPREPLY=( $(compgen -W \"all managed unmanaged\" -- ${cur}) )\n";
248 std::cout << " fi\n";
249 std::cout << " ;;\n";
250 std::cout << " completion)\n";
251 std::cout << " COMPREPLY=( $(compgen -W \"bash zsh fish\" -- ${cur}) )\n";
252 std::cout << " ;;\n";
253 std::cout << " esac\n";
254 std::cout << "\n";
255 std::cout << " return 0\n";
256 std::cout << "}\n";
257 std::cout << "\n";
258 std::cout << "complete -F _porytiles_completions porytiles\n";
259 }
260
261 void output_zsh_completion()
262 {
263 std::cout << "#compdef porytiles\n";
264 std::cout << "# Zsh completion for porytiles\n";
265 std::cout << "# Add to ~/.zshrc or place in a directory in your $fpath:\n";
266 std::cout << "# eval \"$(porytiles completion zsh)\"\n";
267 std::cout << "\n";
268
269 // Helper function to extract project root
270 std::cout << "# Helper function to extract --project-root or -C from command line\n";
271 std::cout << "_porytiles_get_project_root() {\n";
272 std::cout << " local i\n";
273 std::cout << " for ((i=1; i <= ${#words[@]}; i++)); do\n";
274 std::cout << " case \"${words[i]}\" in\n";
275 std::cout << " -C|--project-root)\n";
276 std::cout << " if [[ $((i+1)) -le ${#words[@]} ]]; then\n";
277 std::cout << " echo \"${words[$((i+1))]}\"\n";
278 std::cout << " return\n";
279 std::cout << " fi\n";
280 std::cout << " ;;\n";
281 std::cout << " esac\n";
282 std::cout << " done\n";
283 std::cout << " echo \".\"\n";
284 std::cout << "}\n";
285 std::cout << "\n";
286
287 // Helper function for tileset completion
288 std::cout << "# Helper function to complete tileset names\n";
289 std::cout << "_porytiles_complete_tilesets() {\n";
290 std::cout << " local filter=\"$1\"\n";
291 std::cout << " local project_root\n";
292 std::cout << " project_root=$(_porytiles_get_project_root)\n";
293 std::cout << " local -a tilesets\n";
294 std::cout << " tilesets=(${(f)\"$(porytiles list-tilesets -C \"$project_root\" --filter \"$filter\" "
295 "2>/dev/null)\"})\n";
296 std::cout << " _describe 'tileset' tilesets\n";
297 std::cout << "}\n";
298 std::cout << "\n";
299
300 std::cout << "_porytiles() {\n";
301 std::cout << " local state line\n";
302 std::cout << " local -a commands\n";
303 std::cout << " commands=(\n";
304 std::cout << " 'compile-tileset:Compile a tileset'\n";
305 std::cout << " 'create-tileset:Create a new tileset'\n";
306 std::cout << " 'decompile-tileset:Decompile a tileset'\n";
307 std::cout << " 'dump-attribute-schema:Dump the resolved metatile attribute schema for a tileset'\n";
308 std::cout << " 'dump-tileset-config:Dump the full configuration provenance chain for a tileset'\n";
309 std::cout << " 'import-tileset:Import a pre-existing tileset'\n";
310 std::cout << " 'list-tilesets:List tileset names in the project'\n";
311 std::cout << " 'completion:Generate shell completion scripts'\n";
312 std::cout << " )\n";
313 std::cout << "\n";
314 std::cout << " local -a config_opts\n";
315 std::cout << " config_opts=(\n";
316 std::cout << " '-C[Set project root directory]:directory:_files -/'\n";
317 std::cout << " '--project-root[Set project root directory]:directory:_files -/'\n";
318
320 for (const auto &opt : meta) {
321 const std::string description = escape_zsh_single_quoted(opt.description);
322 std::cout << " '--" << opt.long_name << "[" << description << "]";
323 if (!opt.choices.empty()) {
324 std::cout << ":choice:(";
325 for (std::size_t i = 0; i < opt.choices.size(); ++i) {
326 if (i > 0) {
327 std::cout << " ";
328 }
329 std::cout << opt.choices[i];
330 }
331 std::cout << ")";
332 }
333 std::cout << "'\n";
334 if (opt.has_negation) {
335 std::cout << " '--no-" << opt.long_name << "[Disable " << description << "]'\n";
336 }
337 }
338
339 std::cout << " )\n";
340 std::cout << "\n";
341 std::cout << " _arguments -C \\\n";
342 std::cout << " '-C[Set project root directory]:directory:_files -/' \\\n";
343 std::cout << " '--project-root[Set project root directory]:directory:_files -/' \\\n";
344 std::cout << " '1:command:->command' \\\n";
345 std::cout << " '*::arg:->args'\n";
346 std::cout << "\n";
347 std::cout << " case \"$state\" in\n";
348 std::cout << " command)\n";
349 std::cout << " _describe 'command' commands\n";
350 std::cout << " ;;\n";
351 std::cout << " args)\n";
352 std::cout << " case \"$line[1]\" in\n";
353 std::cout << " compile-tileset|decompile-tileset)\n";
354 std::cout << " _arguments \\\n";
355 std::cout << " $config_opts \\\n";
356 std::cout << " '1:tileset:->tileset_managed'\n";
357 std::cout
358 << " [[ \"$state\" == tileset_managed ]] && _porytiles_complete_tilesets managed\n";
359 std::cout << " ;;\n";
360 std::cout << " dump-attribute-schema|dump-tileset-config)\n";
361 std::cout << " _arguments \\\n";
362 std::cout << " $config_opts \\\n";
363 std::cout << " '--allow-missing-tileset[Do not error when the tileset does not "
364 "exist]' \\\n";
365 std::cout << " '1:tileset:->tileset_all'\n";
366 std::cout << " [[ \"$state\" == tileset_all ]] && _porytiles_complete_tilesets all\n";
367 std::cout << " ;;\n";
368 std::cout << " import-tileset)\n";
369 std::cout << " _arguments \\\n";
370 std::cout << " $config_opts \\\n";
371 std::cout << " '1:tileset:->tileset_unmanaged'\n";
372 std::cout
373 << " [[ \"$state\" == tileset_unmanaged ]] && _porytiles_complete_tilesets unmanaged\n";
374 std::cout << " ;;\n";
375 std::cout << " create-tileset)\n";
376 std::cout << " _arguments \\\n";
377 std::cout << " $config_opts \\\n";
378 std::cout << " '--secondary[Create a secondary tileset instead of a primary]' \\\n";
379 std::cout << " '1:tileset name:'\n";
380 std::cout << " ;;\n";
381 std::cout << " list-tilesets)\n";
382 std::cout << " _arguments \\\n";
383 std::cout << " '-C[Set project root directory]:directory:_files -/' \\\n";
384 std::cout << " '--project-root[Set project root directory]:directory:_files -/' \\\n";
385 std::cout << " '--filter[Filter mode]:filter:(all managed unmanaged)' \\\n";
386 std::cout << " '--prefix[Only show tilesets starting with this prefix]:prefix:'\n";
387 std::cout << " ;;\n";
388 std::cout << " completion)\n";
389 std::cout << " _arguments '1:shell:(bash zsh fish)'\n";
390 std::cout << " ;;\n";
391 std::cout << " *)\n";
392 std::cout << " _values 'config options' $config_opts\n";
393 std::cout << " ;;\n";
394 std::cout << " esac\n";
395 std::cout << " ;;\n";
396 std::cout << " esac\n";
397 std::cout << "}\n";
398 std::cout << "\n";
399 std::cout << "compdef _porytiles porytiles\n";
400 }
401
402 void output_fish_completion()
403 {
404 std::cout << "# Fish completion for porytiles\n";
405 std::cout << "# Save to completions directory (eval does not work reliably in fish):\n";
406 std::cout << "# porytiles completion fish > ~/.config/fish/completions/porytiles.fish\n";
407 std::cout << "# Then restart fish or run: source ~/.config/fish/completions/porytiles.fish\n";
408 std::cout << "\n";
409 std::cout << "# Erase any existing completions for porytiles\n";
410 std::cout << "complete -e -c porytiles\n";
411 std::cout << "\n";
412 std::cout << "# Disable file completions globally for porytiles\n";
413 std::cout << "complete -c porytiles -f\n";
414 std::cout << "\n";
415
416 // Helper function to get project root
417 std::cout << "# Helper function to extract --project-root or -C from command line\n";
418 std::cout << "function __porytiles_get_project_root\n";
419 std::cout << " set -l tokens (commandline -opc)\n";
420 std::cout << " for i in (seq 1 (count $tokens))\n";
421 std::cout << " switch $tokens[$i]\n";
422 std::cout << " case -C --project-root\n";
423 std::cout << " set -l next (math $i + 1)\n";
424 std::cout << " if test $next -le (count $tokens)\n";
425 std::cout << " echo $tokens[$next]\n";
426 std::cout << " return\n";
427 std::cout << " end\n";
428 std::cout << " end\n";
429 std::cout << " end\n";
430 std::cout << " echo \".\"\n";
431 std::cout << "end\n";
432 std::cout << "\n";
433
434 // Helper function for tileset completion
435 std::cout << "# Helper function to complete tileset names\n";
436 std::cout << "function __porytiles_complete_tilesets\n";
437 std::cout << " set -l filter $argv[1]\n";
438 std::cout << " set -l project_root (__porytiles_get_project_root)\n";
439 std::cout << " porytiles list-tilesets -C \"$project_root\" --filter \"$filter\" 2>/dev/null\n";
440 std::cout << "end\n";
441 std::cout << "\n";
442
443 // Helper functions for context detection
444 std::cout << "# Helper functions for detecting current subcommand\n";
445 std::cout << "function __porytiles_needs_subcommand\n";
446 std::cout << " set -l cmd (commandline -opc)\n";
447 std::cout << " for word in $cmd[2..-1]\n";
448 std::cout << " switch $word\n";
449 std::cout << " case compile-tileset create-tileset decompile-tileset dump-attribute-schema "
450 "dump-tileset-config import-tileset list-tilesets completion\n";
451 std::cout << " return 1\n";
452 std::cout << " end\n";
453 std::cout << " end\n";
454 std::cout << " return 0\n";
455 std::cout << "end\n";
456 std::cout << "\n";
457
458 std::cout << "function __porytiles_using_subcommand\n";
459 std::cout << " set -l cmd (commandline -opc)\n";
460 std::cout << " for word in $cmd[2..-1]\n";
461 std::cout << " if test \"$word\" = \"$argv[1]\"\n";
462 std::cout << " return 0\n";
463 std::cout << " end\n";
464 std::cout << " end\n";
465 std::cout << " return 1\n";
466 std::cout << "end\n";
467 std::cout << "\n";
468
469 // Commands
470 std::cout << "# Commands\n";
471 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a compile-tileset -d 'Compile a "
472 "tileset'\n";
473 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a create-tileset -d 'Create a new "
474 "tileset'\n";
475 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a decompile-tileset -d 'Decompile a "
476 "tileset'\n";
477 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a dump-attribute-schema -d 'Dump the "
478 "resolved metatile attribute schema for a tileset'\n";
479 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a dump-tileset-config -d 'Dump the "
480 "full configuration provenance chain for a tileset'\n";
481 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a import-tileset -d 'Import a "
482 "pre-existing tileset'\n";
483 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a list-tilesets -d 'List tileset "
484 "names in the project'\n";
485 std::cout << "complete -c porytiles -f -n __porytiles_needs_subcommand -a completion -d 'Generate shell "
486 "completion scripts'\n";
487 std::cout << "\n";
488
489 // Global options
490 std::cout << "# Global options\n";
491 std::cout << "complete -c porytiles -f -s C -l project-root -d 'Set project root directory' -xa '(__fish_"
492 "complete_directories)'\n";
493 std::cout << "\n";
494
495 // Tileset name completions per subcommand
496 std::cout << "# Tileset name completions\n";
497 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand compile-tileset' -a "
498 "'(__porytiles_complete_tilesets managed)'\n";
499 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand decompile-tileset' -a "
500 "'(__porytiles_complete_tilesets managed)'\n";
501 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand dump-attribute-schema' -a "
502 "'(__porytiles_complete_tilesets all)'\n";
503 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand dump-tileset-config' -a "
504 "'(__porytiles_complete_tilesets all)'\n";
505 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand import-tileset' -a "
506 "'(__porytiles_complete_tilesets unmanaged)'\n";
507 std::cout << "\n";
508
509 // Options for create-tileset subcommand
510 std::cout << "# Options for create-tileset subcommand\n";
511 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand create-tileset' -l secondary -d "
512 "'Create a secondary tileset instead of a primary'\n";
513 std::cout << "\n";
514
515 // Options for the dump subcommands
516 std::cout << "# Options for the dump subcommands\n";
517 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand dump-attribute-schema' -l "
518 "allow-missing-tileset -d 'Do not error when the tileset does not exist'\n";
519 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand dump-tileset-config' -l "
520 "allow-missing-tileset -d 'Do not error when the tileset does not exist'\n";
521 std::cout << "\n";
522
523 // Options for list-tilesets subcommand
524 std::cout << "# Options for list-tilesets subcommand\n";
525 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand list-tilesets' -l filter -d "
526 "'Filter mode' -xa 'all managed unmanaged'\n";
527 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand list-tilesets' -l prefix -d "
528 "'Only show tilesets starting with this prefix'\n";
529 std::cout << "\n";
530
531 // Completion for the 'completion' subcommand
532 std::cout << "# Shell type completion for 'completion' subcommand\n";
533 std::cout << "complete -c porytiles -f -n '__porytiles_using_subcommand completion' -a 'bash zsh fish'\n";
534 std::cout << "\n";
535
536 // Config options
537 std::cout << "# Config options\n";
538
540 for (const auto &opt : meta) {
541 const std::string description = escape_fish_single_quoted(opt.description);
542 std::cout << "complete -c porytiles -f -l " << opt.long_name << " -d '" << description << "'";
543 if (!opt.choices.empty()) {
544 std::cout << " -xa '";
545 for (std::size_t i = 0; i < opt.choices.size(); ++i) {
546 if (i > 0) {
547 std::cout << " ";
548 }
549 std::cout << opt.choices[i];
550 }
551 std::cout << "'";
552 }
553 std::cout << "\n";
554 if (opt.has_negation) {
555 std::cout << "complete -c porytiles -f -l no-" << opt.long_name << " -d 'Disable " << description
556 << "'\n";
557 }
558 }
559 }
560};
Command is an abstract class that provides basic command functionality for the Porytiles CLI driver.
Definition command.hpp:16
CLI::App & get_app() const
Definition command.hpp:42
Command that outputs shell completion scripts.
CompletionCommand(CLI::App &parent_app)
std::vector< CliOptionMeta > get_cli_option_metadata()
Get all CLI option metadata for completion generation.