From 2712db8238846f7b4e24052dbe091ea87defc5ae Mon Sep 17 00:00:00 2001 From: Philip Sequeira Date: Wed, 22 Nov 2017 19:09:06 -0500 Subject: zsh completion: move generation to runtime and improve The completion function itself now parses --list-options on the first tab press and caches the results. This does mean a slight delay on that first tab press, but it will only do this if the argument being completed looks like an option (i.e. starts with "-"), so there is never a delay when just completing a file name. I've also put some effort into making it reasonably fast; on my machine it's consistently under 100 ms, more than half of which is mpv itself. Installation of zsh completion is now done unconditionally because it's nothing more than copying a file. If you really don't want it installed, set zshdir to empty: `./waf configure --zshdir= ...` Improvements in functionality compared to the old script: * Produces the right results for mpv binaries other than the one it was installed with (like a dev build for testing changes). * Does not require running mpv at build time, so it won't cause problems with cross compilation. * Handles aliases. * Slightly nicer handling of options that take comma-separated values and/or sub-options: A space is now inserted at the end instead of a comma, allowing you to immediately start typing the next argument, but typing a comma will still remove the automatically added space, and = and : will now do that too, so you can immediately add a sub-option. * More general/flexible handling of values for options that print their possible values with --option=help. The code as is could handle quite a few more options (*scale, demuxers, decoders, ...), but nobody wants to maintain that list here so we'll just stick with what the old completion script already did. --- TOOLS/zsh.pl | 283 ----------------------------------------------------------- 1 file changed, 283 deletions(-) delete mode 100755 TOOLS/zsh.pl (limited to 'TOOLS/zsh.pl') diff --git a/TOOLS/zsh.pl b/TOOLS/zsh.pl deleted file mode 100755 index 482aa5e125..0000000000 --- a/TOOLS/zsh.pl +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/perl - -# Generate ZSH completion - -# -# This file is part of mpv. -# -# mpv is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# mpv is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with mpv. If not, see . -# - -use strict; -use warnings; -use warnings FATAL => 'uninitialized'; - -my $mpv = $ARGV[0] || 'mpv'; - -my @opts = parse_main_opts('--list-options', '^ (\-\-[^\s\*]*)\*?\s*(.*)'); - -die "Couldn't find any options" unless (@opts); - -my @ao = parse_opts('--ao=help', '^ ([^\s\:]*)\s*(.*)'); -my @vo = parse_opts('--vo=help', '^ ([^\s\:]*)\s*(.*)'); - -my @af = parse_opts('--af=help', '^ ([^\s\:]*)\s*(.*)'); -my @vf = parse_opts('--vf=help', '^ ([^\s\:]*)\s*(.*)'); - -my @protos = parse_opts('--list-protocols', '^ ([^\s]*)'); - -my ($opts_str, $ao_str, $vo_str, $af_str, $vf_str, $protos_str); - -$opts_str .= qq{ '$_' \\\n} foreach (@opts); -chomp $opts_str; - -$ao_str .= qq{ '$_' \\\n} foreach (@ao); -chomp $ao_str; - -$vo_str .= qq{ '$_' \\\n} foreach (@vo); -chomp $vo_str; - -$af_str .= qq{ '$_' \\\n} foreach (@af); -chomp $af_str; - -$vf_str .= qq{ '$_' \\\n} foreach (@vf); -chomp $vf_str; - -$protos_str = join(' ', @protos); - -my $runtime_completions = <<'EOS'; - profile|show-profile) - local -a profiles - local current - for current in "${(@f)$($words[1] --profile=help)}"; do - current=${current//\*/\\\*} - current=${current//\:/\\\:} - current=${current//\[/\\\[} - current=${current//\]/\\\]} - if [[ $current =~ $'\t'([^$'\t']*)$'\t'(.*) ]]; then - if [[ -n $match[2] ]]; then - current="$match[1][$match[2]]" - else - current="$match[1]" - fi - profiles=($profiles $current) - fi - done - if [[ $state == show-profile ]]; then - # For --show-profile, only one allowed - if (( ${#profiles} > 0 )); then - _values 'profile' $profiles && rc=0 - fi - else - # For --profile, multiple allowed - profiles=($profiles 'help[list profiles]') - _values -s , 'profile(s)' $profiles && rc=0 - fi - ;; - - audio-device) - local -a audio_devices - local current - for current in "${(@f)$($words[1] --audio-device=help)}"; do - current=${current//\*/\\\*} - current=${current//\:/\\\:} - current=${current//\[/\\\[} - current=${current//\]/\\\]} - if [[ $current =~ ' '\'([^\']*)\'' \('(.*)'\)' ]]; then - audio_devices=($audio_devices "$match[1][$match[2]]") - fi - done - audio_devices=($audio_devices 'help[list audio devices]') - _values 'audio device' $audio_devices && rc=0 - ;; -EOS -chomp $runtime_completions; - -my $tmpl = <<"EOS"; -#compdef mpv - -# For customization, see: -# https://github.com/mpv-player/mpv/wiki/Zsh-completion-customization - -local curcontext="\$curcontext" state state_descr line -typeset -A opt_args - -local -a match mbegin mend -local MATCH MBEGIN MEND - -# By default, don't complete URLs unless no files match -local -a tag_order -zstyle -a ":completion:*:*:\$service:*" tag-order tag_order || \ - zstyle ":completion:*:*:\$service:*" tag-order '!urls' - -local rc=1 - -_arguments -C -S \\ -$opts_str - '*:files:->mfiles' && rc=0 - -case \$state in - ao) - _values -s , 'audio outputs' \\ -$ao_str - && rc=0 - ;; - - vo) - _values -s , 'video outputs' \\ -$vo_str - && rc=0 - ;; - - af) - _values -s , 'audio filters' \\ -$af_str - && rc=0 - ;; - - vf) - _values -s , 'video filters' \\ -$vf_str - && rc=0 - ;; - -$runtime_completions - - files) - compset -P '*,' - compset -S ',*' - _files -r ',/ \\t\\n\\-' && rc=0 - ;; - - mfiles) - local expl - _tags files urls - while _tags; do - _requested files expl 'media file' _files && rc=0 - if _requested urls; then - while _next_label urls expl URL; do - _urls "\$expl[@]" && rc=0 - compadd -S '' "\$expl[@]" $protos_str && rc=0 - done - fi - (( rc )) || return 0 - done - ;; -esac - -return rc -EOS - -print $tmpl; - -sub parse_main_opts { - my ($cmd, $regex) = @_; - - my @list; - my @lines = call_mpv($cmd); - - foreach my $line (@lines) { - my ($name, $desc) = ($line =~ /^$regex/) or next; - - next if ($desc eq 'removed' || $desc eq 'alias'); - - if ($desc =~ /^Flag/) { - - push @list, $name; - - $name =~ /^--(.*)/; - if ($1 !~ /^(\{|\}|v|list-options|really-quiet|no-.*)$/) { - push @list, "--no-$1"; - } - - } elsif ($desc =~ /^Print/) { - - push @list, $name; - - } else { - - # Option takes argument - - my $entry = $name; - - $desc =~ s/\:/\\:/g; - $entry .= "=-:$desc:"; - - if ($desc =~ /^Choices\\: ([^(]*)/) { - my $choices = $1; - $choices =~ s/ +$//; # strip trailing space - $entry .= "($choices)"; - - # If "no" is one of the choices, it can also be - # negated like a flag (--no-whatever). - if ($choices =~ /\bno\b/) { - $name =~ s/^--/--no-/; - push @list, $name; - } - } elsif ($line =~ /\[file\]/) { - $entry .= '->files'; - } elsif ($name =~ /^--(ao|vo|af|vf|profile|show-profile|audio-device)$/) { - $entry .= "->$1"; - } - push @list, $entry; - } - } - - # 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 parse_opts { - my ($cmd, $regex) = @_; - - my @list; - my @lines = call_mpv($cmd); - - foreach my $line (@lines) { - if ($line !~ /^$regex/) { - next; - } - - my $entry = $1; - - if (defined $2) { - my $desc = $2; - $desc =~ s/\:/\\:/g; - $entry .= "[$desc]"; - } - - push @list, $entry - } - - return @list; -} - -sub call_mpv { - my ($cmd) = @_; - my $output = `"$mpv" --no-config $cmd`; - if ($? == -1) { - die "Could not run mpv: $!"; - } elsif ((my $exit_code = $? >> 8) != 0) { - die "mpv returned $exit_code with output:\n$output"; - } - return split /\n/, $output; -} -- cgit v1.2.3