completion.pl 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env perl
  2. #***************************************************************************
  3. # _ _ ____ _
  4. # Project ___| | | | _ \| |
  5. # / __| | | | |_) | |
  6. # | (__| |_| | _ <| |___
  7. # \___|\___/|_| \_\_____|
  8. #
  9. # Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
  10. #
  11. # This software is licensed as described in the file COPYING, which
  12. # you should have received as part of this distribution. The terms
  13. # are also available at https://curl.se/docs/copyright.html.
  14. #
  15. # You may opt to use, copy, modify, merge, publish, distribute and/or sell
  16. # copies of the Software, and permit persons to whom the Software is
  17. # furnished to do so, under the terms of the COPYING file.
  18. #
  19. # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  20. # KIND, either express or implied.
  21. #
  22. # SPDX-License-Identifier: curl
  23. #
  24. ###########################################################################
  25. use strict;
  26. use warnings;
  27. use Getopt::Long();
  28. use Pod::Usage();
  29. my $curl = 'curl';
  30. my $shell = 'zsh';
  31. my $help = 0;
  32. Getopt::Long::GetOptions(
  33. 'curl=s' => \$curl,
  34. 'shell=s' => \$shell,
  35. 'help' => \$help,
  36. ) or Pod::Usage::pod2usage();
  37. Pod::Usage::pod2usage() if $help;
  38. my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
  39. my @opts = parse_main_opts('--help all', $regex);
  40. if ($shell eq 'fish') {
  41. print "# curl fish completion\n\n";
  42. print qq{$_ \n} foreach (@opts);
  43. } elsif ($shell eq 'zsh') {
  44. my $opts_str;
  45. $opts_str .= qq{ $_ \\\n} foreach (@opts);
  46. chomp $opts_str;
  47. my $tmpl = <<"EOS";
  48. #compdef curl
  49. # curl zsh completion
  50. local curcontext="\$curcontext" state state_descr line
  51. typeset -A opt_args
  52. local rc=1
  53. _arguments -C -S \\
  54. $opts_str
  55. '*:URL:_urls' && rc=0
  56. return rc
  57. EOS
  58. print $tmpl;
  59. } else {
  60. die("Unsupported shell: $shell");
  61. }
  62. sub parse_main_opts {
  63. my ($cmd, $regex) = @_;
  64. my @list;
  65. my @lines = call_curl($cmd);
  66. foreach my $line (@lines) {
  67. my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;
  68. my $option = '';
  69. $arg =~ s/\:/\\\:/g if defined $arg;
  70. $desc =~ s/'/'\\''/g if defined $desc;
  71. $desc =~ s/\[/\\\[/g if defined $desc;
  72. $desc =~ s/\]/\\\]/g if defined $desc;
  73. $desc =~ s/\:/\\\:/g if defined $desc;
  74. if ($shell eq 'fish') {
  75. $option .= "complete --command curl";
  76. $option .= " --short-option '" . strip_dash(trim($short)) . "'"
  77. if defined $short;
  78. $option .= " --long-option '" . strip_dash(trim($long)) . "'"
  79. if defined $long;
  80. $option .= " --description '" . strip_dash(trim($desc)) . "'"
  81. if defined $desc;
  82. } elsif ($shell eq 'zsh') {
  83. $option .= '{' . trim($short) . ',' if defined $short;
  84. $option .= trim($long) if defined $long;
  85. $option .= '}' if defined $short;
  86. $option .= '\'[' . trim($desc) . ']\'' if defined $desc;
  87. if (defined $arg) {
  88. $option .= ":'$arg'";
  89. if ($arg =~ /<file ?(name)?>|<path>/) {
  90. $option .= ':_files';
  91. } elsif ($arg =~ /<dir>/) {
  92. $option .= ":'_path_files -/'";
  93. } elsif ($arg =~ /<url>/i) {
  94. $option .= ':_urls';
  95. } elsif ($long =~ /ftp/ && $arg =~ /<method>/) {
  96. $option .= ":'(multicwd nocwd singlecwd)'";
  97. } elsif ($arg =~ /<method>/) {
  98. $option .= ":'(DELETE GET HEAD POST PUT)'";
  99. }
  100. }
  101. }
  102. push @list, $option;
  103. }
  104. # Sort longest first, because zsh won't complete an option listed
  105. # after one that's a prefix of it.
  106. @list = sort {
  107. $a =~ /([^=]*)/; my $ma = $1;
  108. $b =~ /([^=]*)/; my $mb = $1;
  109. length($mb) <=> length($ma)
  110. } @list if $shell eq 'zsh';
  111. return @list;
  112. }
  113. sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
  114. sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };
  115. sub call_curl {
  116. my ($cmd) = @_;
  117. my $output = `"$curl" $cmd`;
  118. if ($? == -1) {
  119. die "Could not run curl: $!";
  120. } elsif ((my $exit_code = $? >> 8) != 0) {
  121. die "curl returned $exit_code with output:\n$output";
  122. }
  123. return split /\n/, $output;
  124. }
  125. __END__
  126. =head1 NAME
  127. completion.pl - Generates tab-completion files for various shells
  128. =head1 SYNOPSIS
  129. completion.pl [options...]
  130. --curl path to curl executable
  131. --shell zsh/fish
  132. --help prints this help
  133. =cut