From e075b2149b5d287b30718b31bee5ba80aba3da94 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sun, 10 Feb 2019 22:06:42 +0100 Subject: scripts/completion.pl: also generate fish completion file This is the renamed script formerly known as zsh.pl Closes #3545 --- scripts/Makefile.am | 22 +++++++-- scripts/completion.pl | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/zsh.pl | 92 ---------------------------------- 3 files changed, 151 insertions(+), 97 deletions(-) create mode 100755 scripts/completion.pl delete mode 100755 scripts/zsh.pl (limited to 'scripts') diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 15c08828c..297b8d29b 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -20,20 +20,30 @@ # ########################################################################### ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@ +FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@ PERL = @PERL@ ZSH_COMPLETION_FUNCTION_FILENAME = _curl +FISH_COMPLETION_FUNCTION_FILENAME = curl.fish -CLEANFILES = $(ZSH_COMPLETION_FUNCTION_FILENAME) +CLEANFILES = $(ZSH_COMPLETION_FUNCTION_FILENAME) $(FISH_COMPLETION_FUNCTION_FILENAME) -all-local: $(ZSH_COMPLETION_FUNCTION_FILENAME) +all-local: $(ZSH_COMPLETION_FUNCTION_FILENAME) $(FISH_COMPLETION_FUNCTION_FILENAME) -$(ZSH_COMPLETION_FUNCTION_FILENAME): zsh.pl +$(ZSH_COMPLETION_FUNCTION_FILENAME): completion.pl if CROSSCOMPILING @echo "NOTICE: we can't generate zsh completion when cross-compiling!" else # if not cross-compiling: - @if ! test -x "$(PERL)"; then echo "No perl: can't install zsh.pl"; exit 0; fi - $(PERL) $(srcdir)/zsh.pl $(top_builddir)/src/curl$(EXEEXT) > $@ + @if ! test -x "$(PERL)"; then echo "No perl: can't install completion.pl"; exit 0; fi + $(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell zsh > $@ +endif + +$(FISH_COMPLETION_FUNCTION_FILENAME): completion.pl +if CROSSCOMPILING + @echo "NOTICE: we can't generate fish completion when cross-compiling!" +else # if not cross-compiling: + @if ! test -x "$(PERL)"; then echo "No perl: can't install completion.pl"; exit 0; fi + $(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell fish > $@ endif install-data-local: @@ -41,5 +51,7 @@ if CROSSCOMPILING @echo "NOTICE: we can't install zsh completion when cross-compiling!" else # if not cross-compiling: $(MKDIR_P) $(DESTDIR)$(ZSH_FUNCTIONS_DIR) + $(MKDIR_P) $(DESTDIR)$(FISH_FUNCTIONS_DIR) $(INSTALL_DATA) $(ZSH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(ZSH_FUNCTIONS_DIR)/$(ZSH_COMPLETION_FUNCTION_FILENAME) + $(INSTALL_DATA) $(FISH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(FISH_FUNCTIONS_DIR)/$(FISH_COMPLETION_FUNCTION_FILENAME) endif diff --git a/scripts/completion.pl b/scripts/completion.pl new file mode 100755 index 000000000..1c41755b4 --- /dev/null +++ b/scripts/completion.pl @@ -0,0 +1,134 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use Getopt::Long(); +use Pod::Usage(); + +my $curl = 'curl'; +my $shell = 'zsh'; +my $help = 0; +Getopt::Long::GetOptions( + 'curl=s' => \$curl, + 'shell=s' => \$shell, + 'help' => \$help, +) or Pod::Usage::pod2usage(); +Pod::Usage::pod2usage() if $help; + +my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)'; +my @opts = parse_main_opts('--help', $regex); + +if ($shell eq 'fish') { + print "# curl fish completion\n\n"; + print qq{$_ \n} foreach (@opts); +} elsif ($shell eq 'zsh') { + my $opts_str; + + $opts_str .= qq{ $_ \\\n} foreach (@opts); + chomp $opts_str; + +my $tmpl = <<"EOS"; +#compdef curl + +# curl zsh completion + +local curcontext="\$curcontext" state state_descr line +typeset -A opt_args + +local rc=1 + +_arguments -C -S \\ +$opts_str + '*:URL:_urls' && rc=0 + +return rc +EOS + + print $tmpl; +} else { + die("Unsupported shell: $shell"); +} + +sub parse_main_opts { + my ($cmd, $regex) = @_; + + my @list; + my @lines = call_curl($cmd); + + foreach my $line (@lines) { + my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next; + + my $option = ''; + + $arg =~ s/\:/\\\:/g if defined $arg; + + $desc =~ s/'/'\\''/g if defined $desc; + $desc =~ s/\[/\\\[/g if defined $desc; + $desc =~ s/\]/\\\]/g if defined $desc; + $desc =~ s/\:/\\\:/g if defined $desc; + + if ($shell eq 'fish') { + $option .= "complete --command curl"; + $option .= " --short-option '" . strip_dash(trim($short)) . "'" + if defined $short; + $option .= " --long-option '" . strip_dash(trim($long)) . "'" + if defined $long; + $option .= " --description '" . strip_dash(trim($desc)) . "'" + if defined $desc; + } elsif ($shell eq 'zsh') { + $option .= '{' . trim($short) . ',' if defined $short; + $option .= trim($long) if defined $long; + $option .= '}' if defined $short; + $option .= '\'[' . trim($desc) . ']\'' if defined $desc; + + $option .= ":'$arg'" if defined $arg; + + $option .= ':_files' + if defined $arg and ($arg eq '' || $arg eq '' + || $arg eq ''); + } + + push @list, $option; + } + + # Sort longest first, because zsh won't complete an option listed + # after one that's a prefix of it. + @list = sort { + $a =~ /([^=]*)/; my $ma = $1; + $b =~ /([^=]*)/; my $mb = $1; + + length($mb) <=> length($ma) + } @list if $shell eq 'zsh'; + + return @list; +} + +sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }; +sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s }; + +sub call_curl { + my ($cmd) = @_; + my $output = `"$curl" $cmd`; + if ($? == -1) { + die "Could not run curl: $!"; + } elsif ((my $exit_code = $? >> 8) != 0) { + die "curl returned $exit_code with output:\n$output"; + } + return split /\n/, $output; +} + +__END__ + +=head1 NAME + +completion.pl - Generates tab-completion files for various shells + +=head1 SYNOPSIS + +completion.pl [options...] + + --curl path to curl executable + --shell zsh/fish + --help prints this help + +=cut diff --git a/scripts/zsh.pl b/scripts/zsh.pl deleted file mode 100755 index 0f9cbec7d..000000000 --- a/scripts/zsh.pl +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env perl - -# Generate ZSH completion - -use strict; -use warnings; - -my $curl = $ARGV[0] || 'curl'; - -my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)'; -my @opts = parse_main_opts('--help', $regex); - -my $opts_str; - -$opts_str .= qq{ $_ \\\n} foreach (@opts); -chomp $opts_str; - -my $tmpl = <<"EOS"; -#compdef curl - -# curl zsh completion - -local curcontext="\$curcontext" state state_descr line -typeset -A opt_args - -local rc=1 - -_arguments -C -S \\ -$opts_str - '*:URL:_urls' && rc=0 - -return rc -EOS - -print $tmpl; - -sub parse_main_opts { - my ($cmd, $regex) = @_; - - my @list; - my @lines = call_curl($cmd); - - foreach my $line (@lines) { - my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next; - - my $option = ''; - - $arg =~ s/\:/\\\:/g if defined $arg; - - $desc =~ s/'/'\\''/g if defined $desc; - $desc =~ s/\[/\\\[/g if defined $desc; - $desc =~ s/\]/\\\]/g if defined $desc; - $desc =~ s/\:/\\\:/g if defined $desc; - - $option .= '{' . trim($short) . ',' if defined $short; - $option .= trim($long) if defined $long; - $option .= '}' if defined $short; - $option .= '\'[' . trim($desc) . ']\'' if defined $desc; - - $option .= ":'$arg'" if defined $arg; - - $option .= ':_files' - if defined $arg and ($arg eq '' || $arg eq '' - || $arg eq ''); - - push @list, $option; - } - - # Sort longest first, because zsh won't complete an option listed - # after one that's a prefix of it. - @list = sort { - $a =~ /([^=]*)/; my $ma = $1; - $b =~ /([^=]*)/; my $mb = $1; - - length($mb) <=> length($ma) - } @list; - - return @list; -} - -sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }; - -sub call_curl { - my ($cmd) = @_; - my $output = `"$curl" $cmd`; - if ($? == -1) { - die "Could not run curl: $!"; - } elsif ((my $exit_code = $? >> 8) != 0) { - die "curl returned $exit_code with output:\n$output"; - } - return split /\n/, $output; -} -- cgit v1.2.3