completion.pl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use Getopt::Long();
  5. use Pod::Usage();
  6. my $curl = 'curl';
  7. my $shell = 'zsh';
  8. my $help = 0;
  9. Getopt::Long::GetOptions(
  10. 'curl=s' => \$curl,
  11. 'shell=s' => \$shell,
  12. 'help' => \$help,
  13. ) or Pod::Usage::pod2usage();
  14. Pod::Usage::pod2usage() if $help;
  15. my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
  16. my @opts = parse_main_opts('--help', $regex);
  17. if ($shell eq 'fish') {
  18. print "# curl fish completion\n\n";
  19. print qq{$_ \n} foreach (@opts);
  20. } elsif ($shell eq 'zsh') {
  21. my $opts_str;
  22. $opts_str .= qq{ $_ \\\n} foreach (@opts);
  23. chomp $opts_str;
  24. my $tmpl = <<"EOS";
  25. #compdef curl
  26. # curl zsh completion
  27. local curcontext="\$curcontext" state state_descr line
  28. typeset -A opt_args
  29. local rc=1
  30. _arguments -C -S \\
  31. $opts_str
  32. '*:URL:_urls' && rc=0
  33. return rc
  34. EOS
  35. print $tmpl;
  36. } else {
  37. die("Unsupported shell: $shell");
  38. }
  39. sub parse_main_opts {
  40. my ($cmd, $regex) = @_;
  41. my @list;
  42. my @lines = call_curl($cmd);
  43. foreach my $line (@lines) {
  44. my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;
  45. my $option = '';
  46. $arg =~ s/\:/\\\:/g if defined $arg;
  47. $desc =~ s/'/'\\''/g if defined $desc;
  48. $desc =~ s/\[/\\\[/g if defined $desc;
  49. $desc =~ s/\]/\\\]/g if defined $desc;
  50. $desc =~ s/\:/\\\:/g if defined $desc;
  51. if ($shell eq 'fish') {
  52. $option .= "complete --command curl";
  53. $option .= " --short-option '" . strip_dash(trim($short)) . "'"
  54. if defined $short;
  55. $option .= " --long-option '" . strip_dash(trim($long)) . "'"
  56. if defined $long;
  57. $option .= " --description '" . strip_dash(trim($desc)) . "'"
  58. if defined $desc;
  59. } elsif ($shell eq 'zsh') {
  60. $option .= '{' . trim($short) . ',' if defined $short;
  61. $option .= trim($long) if defined $long;
  62. $option .= '}' if defined $short;
  63. $option .= '\'[' . trim($desc) . ']\'' if defined $desc;
  64. $option .= ":'$arg'" if defined $arg;
  65. $option .= ':_files'
  66. if defined $arg and ($arg eq '<file>' || $arg eq '<filename>'
  67. || $arg eq '<dir>');
  68. }
  69. push @list, $option;
  70. }
  71. # Sort longest first, because zsh won't complete an option listed
  72. # after one that's a prefix of it.
  73. @list = sort {
  74. $a =~ /([^=]*)/; my $ma = $1;
  75. $b =~ /([^=]*)/; my $mb = $1;
  76. length($mb) <=> length($ma)
  77. } @list if $shell eq 'zsh';
  78. return @list;
  79. }
  80. sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
  81. sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };
  82. sub call_curl {
  83. my ($cmd) = @_;
  84. my $output = `"$curl" $cmd`;
  85. if ($? == -1) {
  86. die "Could not run curl: $!";
  87. } elsif ((my $exit_code = $? >> 8) != 0) {
  88. die "curl returned $exit_code with output:\n$output";
  89. }
  90. return split /\n/, $output;
  91. }
  92. __END__
  93. =head1 NAME
  94. completion.pl - Generates tab-completion files for various shells
  95. =head1 SYNOPSIS
  96. completion.pl [options...]
  97. --curl path to curl executable
  98. --shell zsh/fish
  99. --help prints this help
  100. =cut