From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id 98178998 for ; Wed, 19 Dec 2018 23:39:01 +0000 (UTC) Received: (qmail 2673 invoked by alias); 19 Dec 2018 23:38:43 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43914 Received: (qmail 29576 invoked by uid 1010); 19 Dec 2018 23:38:43 -0000 X-Qmail-Scanner-Diagnostics: from mail-it1-f176.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.100.2/25112. spamassassin: 3.4.2. Clear:RC:0(209.85.166.176):SA:0(-1.9/5.0):. Processed in 4.374138 secs); 19 Dec 2018 23:38:43 -0000 X-Envelope-From: dana@dana.is X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dana-is.20150623.gappssmtp.com; s=20150623; h=from:content-transfer-encoding:mime-version:subject:date:references :to:in-reply-to:message-id; bh=PH/O+x6YWtFYDaldQ/urlbKWr1OzvHxw44OnyG6XGkg=; b=VBgBrk5ZTNofwj3gfUqWOy60wy5Qm1zZKVthUo8Mto7X+gUucuIt3SLV47HycYz4tq T/v2Ypl2+/OMghUiuaq/uHsQDQu0FgbRwJ7OlarAkq0X0gbEVf4gOpAtFequZhhXeLFA EJnyJhGV87R2evN65Aj4nYuX7HY9G22622iMS/uhA+7sq4xRxMIfzb3bq3vRd6IArcX2 N9L7eSWzvRH6xXS7cwpDaeBkqqTeJn1KDvqY0S17DOXTeNvMEjmpHn+nXJAOSc48QTS9 mDy8kf2RyB87WD2oF1PJYEJx7MOs1/SZb4DzKmD2Wpr9Bq89iJqS/kmrosrfq4gjFoAl 9XlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:content-transfer-encoding:mime-version :subject:date:references:to:in-reply-to:message-id; bh=PH/O+x6YWtFYDaldQ/urlbKWr1OzvHxw44OnyG6XGkg=; b=hkburljqDpfko6bdl5yvY1ApP9VWkXUSHQ+rSDGCmWIQ0pS3IedVj487Id2pmC9Ett 26tA4CReoE8fJvEeMZSmngSIXsWzm45/zX3UqIBM17Z0VKBny9G5QXA75FGWlxU2PzSH gzXcIshbMGgEO/jqMwJ2tlTZDXl1uIlZcFAEGnoRIbsv98FS+EtYTfWhjXP3RJ7ok2VG QmAXMvV6JnVoluKJjUD4B59Y2+kQYdhmygj5Lz0K3a7buBcIntpzIQ5VeUP74svIYNlD K1cjZ/LcVJKrZ7nhqMUqMVD2Ou8Bow3da59VkQQW2yp/UUCrIvz/9YEnW/lSMIc90rp8 El0g== X-Gm-Message-State: AA+aEWY5M7GK9GifeVqPP3EehmSUlvgX5rAWSXc1U2CoobQQYIIH/AzE 8HAJL7hTP1pG3dxlFuRmH8htj00h+NojAg== X-Google-Smtp-Source: AFSGD/VZ4hpg888+0qrMgmGMTSYR/2TFPuZ1s0dx2HcVsTsrL/L3LSXqXVy0q9BsTb4lcxS/kJ3czw== X-Received: by 2002:a24:ce42:: with SMTP id v63mr8198519itg.136.1545262715566; Wed, 19 Dec 2018 15:38:35 -0800 (PST) From: dana Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 12.0 \(3445.100.39\)) Subject: Re: [PATCH/RFC] Completion: Add _composer Date: Wed, 19 Dec 2018 17:38:33 -0600 References: <6D1B6E3C-52B9-4AB6-BC48-3C279FCF33FC@dana.is> <1545092371.600233.1611967824.063830D4@webmail.messagingengine.com> To: Zsh workers In-Reply-To: Message-Id: X-Mailer: Apple Mail (2.3445.100.39) On 17 Dec 2018, at 18:46, dana wrote: >Looking at how APT packages are handled, though, i guess it's actually = kind of >the same situation there =E2=80=94 so maybe i'm overly worried about = it? Hard-coded packages felt too questionable, and i couldn't think of = anything else that would be intuitive to people, so i just made it fetch them all = by default again, with an option to use only the local data In cases like this where there are many thousands of possibilities, and = you try to complete when there's no prefix/suffix yet on the command line, = completion hangs for ages (several minutes here) on the loop in _multi_parts. I = didn't look very hard but i think maybe this could be improved if the empty = argument were treated as a special case? Even then, though, i's still effectively unusable if you add too many possibilities at once; e.g.: _foo() { compadd -2V test - foobarbaz{1..100000} } compdef _foo foo foo (That's the _deb_packages behaviour i mentioned) dana diff --git a/Completion/Unix/Command/_composer = b/Completion/Unix/Command/_composer new file mode 100644 index 000000000..497f6610a --- /dev/null +++ b/Completion/Unix/Command/_composer @@ -0,0 +1,854 @@ +#compdef composer composer.phar + +# Notes: +# - With some re-arranging, this function could be used as a base for = completing +# any Symfony Console application. It's worth mentioning that most = Console +# applications provide their help output in structured XML and JSON = formats, +# and a helper function could be written to parse these and produce = basic +# completion similar to what `_arguments --` does. But it wouldn't be = fully +# featured like this +# - Completing arbitrary package names from Packagist, even with = caching enabled +# (which it is by default here), can be very slow; it may even hang = the shell. +# To disable the fetching of remote packages and use only local = cache/JSON +# information for completion, set the fetch-packages style as = follows: +# zstyle ':completion:*:composer:*' fetch-packages no +# - @todo We don't complete custom commands (including script aliases). = This is +# easy to do in the general case, but it probably requires some = clever caching +# to avoid introducing a noticeable lag to every completion = operation, due to +# the way command resolution works and the fact that discovering = custom +# commands requires making slow calls to Composer +# - @todo We don't complete version constraints + +# Check cache validity +__composer_cache_policy() { + # Invalidate if the cache is over a week old + [[ -n $1(#qmw+1N) ]] && return 0 + + __composer_update_work_dir + + # Invalidate if the current project JSON file is newer than the cache + [[ -e $_composer_work_dir/composer.json ]] && + [[ $_composer_work_dir/composer.json -nt $1 ]] +} + +# _call_program wrapper (same eval/quoting rules applies) +# $1 =3D> tag +# $2 ... =3D> composer arguments +(( $+functions[__composer_call] )) || +__composer_call() { + local -a cmd + + __composer_update_work_dir + + cmd=3D( ${_composer_cmd:-composer} -d${_composer_work_dir:-${(q)PWD}} = ) + (( _composer_is_global )) && cmd+=3D( global ) + + _call_program $1 $cmd "${@[2,-1]}" +} + +# Resolve potentially abbreviated/aliased command name to canonical = name +# $1 =3D> name of scalar parameter to set +# $2 =3D> provided command name +(( $+functions[__composer_resolve_cmd] )) || +__composer_resolve_cmd() { + local __i __ret=3D1 + local -a __cmds __tmp=3D( ${(@)_composer_cmds%%:*} ) + + __cmds=3D( $__tmp[(r)$2] ) + (( $#__cmds )) || __cmds=3D( ${(M)__tmp:#$2*} ) + + if (( $#__cmds =3D=3D 1 )); then + 2=3D$__cmds[1] + (( $+_composer_cmd_aliases[$2] )) && 2=3D$_composer_cmd_aliases[$2] + __ret=3D0 + else + 3=3D$__cmds[1] + + # An ambiguous prefix match isn't ambiguous if all the matches are = aliases + # of each other + for (( __i =3D 2; __i <=3D $#__cmds; __i++ )); do + if [[ $_composer_cmd_aliases[$__cmds[__i]] =3D=3D $3 ]]; then + __cmds[__i]=3D() + elif [[ $_composer_cmd_aliases[$3] =3D=3D $__cmds[__i] ]]; then + 3=3D$__cmds[__i] + __cmds[__i]=3D() + fi + done + + if (( $#__cmds =3D=3D 1 )); then + 2=3D$3 + __ret=3D0 + elif (( $#__cmds )); then + _message -e ambiguous-commands "ambiguous command: $2 ($__cmds)" + else + # @todo Too annoying without handling custom commands + : _message -e unrecognized-commands "unrecognized command: $2" + fi + fi + : ${(P)1::=3D$2} + return __ret +} + +# Remove already-used global options (this is a bit silly admittedly) +# $1 ... =3D> options given (e.g. ${(k)opt_args}) +(( $+functions[__composer_prune_global_opts] )) || +__composer_prune_global_opts() { + local opt + local -a excls specs remove + + for opt in $@; do + specs=3D( ${(M)_composer_global_opts:#(*[\*\)]|)${opt}[=3D+-]#\[*} = ) + excls=3D( ${=3D${(@)${(@M)specs#\([^\)]##\)}//[ \(\)]/ }} ) + remove+=3D( + # Don't remove used options like *-v + ${specs:#(*\)|)\*${opt}[=3D+-]#\[*} + # But do remove them if a used option excludes them + = ${(M)_composer_global_opts:#(*[\*\)]|)(${(j<|>)~${(@b)excls}})[=3D+-]#\[*}= + ) + done + + _composer_global_opts=3D( = ${_composer_global_opts:#(${(j<|>)~${(@b)remove}})} ) +} + +# Update the working directory from opt_args/PWD. This is a little = irritating to +# deal with; for now we're just calling it anywhere it might be = important +(( $+functions[__composer_update_work_dir] )) || +__composer_update_work_dir() { + if [[ -n ${(v)opt_args[(i)(-d|--working-dir)]} ]]; then + eval _composer_work_dir=3D${(v)opt_args[(i)(-d|--working-dir)]} + elif [[ -z $_composer_work_dir ]]; then + _composer_work_dir=3D$PWD + fi +} + +# Complete local/vendored binaries +(( $+functions[__composer_binaries] )) || +__composer_binaries() { + local -a tmp + + tmp=3D( ${(f)"$( __composer_call exec-list exec -l )"} ) + tmp=3D( ${(@)tmp%%[[:space:]]##} ) + [[ $tmp[1] =3D=3D *: ]] && tmp[1]=3D() + tmp=3D( ${(@)tmp##[\*-][[:space:]]#} ) + tmp=3D( ${(@)tmp%%[[:space:]]##\(local\)} ) + + (( $#tmp )) || { + _message -e commands 'binary' + } + _wanted commands expl 'binary' compadd -a "$@" - tmp +} + +# Complete commands +(( $+functions[__composer_commands] )) || +__composer_commands() { + _describe -t commands command _composer_cmds +} + +# Complete package licences +(( $+functions[__composer_licenses] )) || +__composer_licenses() { + # These are just the ones the Composer documentation recommends; the = full list + # of supported identifiers can be found at = https://spdx.org/licenses/. If + # other functions need to complete licences in the future, it might = be wise to + # break this out into a _licenses type + local -a tmp=3D( + 'Apache-2.0:Apache License 2.0' + 'BSD-2-Clause:BSD 2-Clause "Simplified" License' + 'BSD-3-Clause:BSD 3-Clause "New" or "Revised" License' + 'BSD-4-Clause:BSD 4-Clause "Original" or "Old" License' + 'GPL-2.0-only:GNU General Public License v2.0 only' + 'GPL-2.0-or-later:GNU General Public License v2.0 or later' + 'GPL-3.0-only:GNU General Public License v3.0 only' + 'GPL-3.0-or-later:GNU General Public License v3.0 or later' + 'LGPL-2.1-only:GNU Lesser General Public License v2.1 only' + 'LGPL-2.1-or-later:GNU Lesser General Public License v2.1 or later' + 'LGPL-3.0-only:GNU Lesser General Public License v3.0 only' + 'LGPL-3.0-or-later:GNU Lesser General Public License v3.0 or later' + 'MIT:MIT License' + 'proprietary:proprietary/closed-source license' + ) + _describe -t licenses 'package license' tmp +} + +# Complete packages +# --pairs =3D> complete as package:constraint pairs +# --vendor =3D> complete only vendored (installed) packages +(( $+functions[__composer_packages] )) || +__composer_packages() { + local cwd + local -a pairs vendor home_dirs pkgs + + __composer_update_work_dir + + zparseopts -D -E - -vendor=3Dvendor -pairs=3Dpairs + + (( $#pairs )) && compset -P '*[:=3D ]' && { + _message -e versions 'version constraint' + return + } + + home_dirs=3D( + $COMPOSER_HOME(#q/N) + $HOME/.composer(#q/N) + $HOME/.config/composer(#q/N) + ${XDG_CONFIG_HOME:-/@err@}/composer(#q/N) + ) + + pkgs=3D( $_composer_work_dir/vendor/*/*(#q/N) ) + + # Trying to work out the path to the vendor directory when we're = global is + # tedious, so we'll just take everything we can find + (( ! $#vendor || _composer_is_global )) && + pkgs+=3D( $^home_dirs/vendor/*/*(#q/N) ) + + (( $#vendor )) || pkgs+=3D( + ${COMPOSER_CACHE_DIR:-/@err@}/files/*/*(#q/N) + $^home_dirs/cache/files/*/*(#q/N) + ) + + pkgs=3D( ${(@M)pkgs%%[^/]##/[^/]##} ) + + (( $#vendor )) || { + pkgs+=3D( + ${(f)"$( + _call_program packages-json \ + command grep -soE ${(qq):-'"[^/]+\\?/[^/]+"\s*:'} -- \ + ${(qq)_composer_work_dir}/composer.json + )"//[$':" \t\\']/} + ${(@)${(f)"$( + _call_program packages-lock \ + command grep -soE ${(qq):-'"name"\s*:\s*"[^/]+\\?/[^/]+"'} -- = \ + ${(qq)_composer_work_dir}/composer.lock + )"//\"name\"/}//[$':" \t\\']/} + ) + + zstyle -T ":completion:*:*:$service:*" fetch-packages && { + { (( ! $#_composer_cache_pkgs )) || _cache_invalid composer-pkgs = } && + ! _retrieve_cache composer-pkgs && { + _composer_cache_pkgs=3D( ${(f)"$( + __composer_call packages-fetch show -aN | + LC_ALL=3DC tr -d '\t ' # Seems faster than ${...//.../} here + )"} ) + _store_cache composer-pkgs _composer_cache_pkgs + } + pkgs+=3D( $_composer_cache_pkgs ) + } + } + + (( $#pkgs )) || { + _message -e packages package + return + } + + if [[ $PREFIX =3D=3D */* ]]; then + _description packages expl "${PREFIX%%/*}/* package" + else + _description packages expl 'package vendor' + fi + _multi_parts "${(@)expl}" "$@" / pkgs +} + +# Complete package repositories +(( $+functions[__composer_repositories] )) || +__composer_repositories() { + _alternative \ + 'urls:repository URL:_urls' \ + 'files:repository JSON configuration file:_files -g "*.json(#q-.)"' = \ + 'json:repository JSON configuration object:' +} + +# Complete composer.json scripts +(( $+functions[__composer_scripts] )) || +__composer_scripts() { + local -a tmp + + tmp=3D( ${(f)"$( __composer_call run-script-list run-script -l )"} ) + tmp=3D( ${(@)tmp##[[:space:]]##} ) + tmp=3D( ${(@)tmp%%[[:space:]]##} ) + [[ $tmp[1] =3D=3D *: ]] && tmp[1]=3D() + tmp=3D( ${(@)tmp##[\*-][[:space:]]#} ) + tmp=3D( ${(@)tmp%%[[:space:]]*} ) + + (( $#tmp )) || { + _message -e commands 'script' + } + _wanted commands expl 'script' compadd -a "$@" - tmp +} + +# Complete package stabilities +(( $+functions[__composer_stabilities] )) || +__composer_stabilities() { + _wanted stabilities expl 'package stability' compadd "$@" - \ + stable RC beta alpha dev +} + +# Complete package types +(( $+functions[__composer_types] )) || +__composer_types() { + # Only the first four here are official types listed in the = documentation; the + # others are popular custom types + _wanted types expl 'package type' compadd "$@" - \ + composer-plugin library metapackage project \ + cakephp-plugin cantao-module drupal-module magento2-module package = \ + phpcodesniffer-standard silverstripe-module symfony-bundle \ + typo3-cms-extension wordpress-plugin yii2-extension +} + +(( $+functions[_composer_archive] )) || +_composer_archive() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dir=3D[specify output directory]:output directory:_files -/' \ + '--file=3D[specify output file name]:output file name (without = extension):_files' \ + '(-f --format)'{-f+,--format=3D}'[specify archive format]:archive = format:(tar zip)' \ + '1:: :__composer_packages' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_check-platform-reqs] )) || +_composer_check-platform-reqs() { + _arguments -s -S : \ + $_composer_global_opts \ + '--no-dev[do not check require-dev package requirements]' +} + +(( $+functions[_composer_config] )) || +_composer_config() { + local ret=3D1 + local -a context expl line state state_descr cmd tmp + local -A opt_args + + # -a and -f can be used together, in which case auth.json will be = looked up in + # the base directory of the -f file. -f and -g can't be used = together, but -f + # can still be used to specify config.json with `composer global = config` + _arguments -s -S : \ + $_composer_global_opts \ + + '(a)' '(A l u : *)'{-a,--auth}'[edit auth.json (with -e)]' \ + + '(A)' '(a e u)--absolute[display absolute *-dir setting paths]' \ + + '(e)' '(A l u : *)'{-e,--editor}'[open configuration in $EDITOR]' = \ + + '(f)' '(g)'{-f+,--file=3D}'[specify {composer,config}.json = path]:configuration file:_files' \ + + '(g)' {-g,--global}'[use global config.json]' \ + + '(l)' '(a e u : *)'{-l,--list}'[list configuration settings]' \ + + '(u)' '(a A e l *)--unset[unset specified setting key]' \ + + k '(a e l)1: :->key' \ + + v '(a e l u)*:: :->val' \ + && ret=3D0 + __composer_update_work_dir + + case $state in + key) + # `composer config` doesn't seem to actually respect -d... + tmp=3D( ${(v)opt_args[(i)([^-]##-|)(-f|--file)]} ) + cmd=3D( config -f$^tmp $opt_args[(i)([^-]##-|)(-g|--global)] -l ) + + tmp=3D( ${(@M)${(f)"$( __composer_call config-list $cmd = )"}#\[*\]} ) + tmp=3D( ${(@)tmp//[ \]\[]/} ) + + _wanted setting-keys expl 'setting key' compadd -a "$@" - tmp && = ret=3D0 + ;; + val) + case $words[1] in + *[.-]dirs#|home) + _wanted setting-values expl 'setting value' _files -/ "$@" && = ret=3D0 + ;; + *[.-]domains#) + _wanted setting-values expl 'setting value' _hosts "$@" && = ret=3D0 + ;; + *[.-]urls#) + _wanted setting-values expl 'setting value' _urls "$@" && = ret=3D0 + ;; + *) + # If we wanted we could get specific about booleans, etc., = here + _wanted setting-values expl 'setting value' _default "$@" && = ret=3D0 + ;; + esac + ;; + esac + + return ret +} + +(( $+functions[_composer_create-project] )) || +_composer_create-project() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '(--remove-vcs)--keep-vcs[do not remove VCS directory]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-install[skip installation of package dependencies]' \ + '--no-progress[do not display download progress]' \ + '--no-secure-http[do not use HTTPS]' \ + '--no-scripts[prevent execution of scripts defined in root = package]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '(--keep-vcs)--remove-vcs[force removal of VCS directory]' \ + '--repository=3D[specify package repository]: = :__composer_repositories' \ + '!(--repository)--repository_url:repository URL:_urls' \ + '(-s --stability)'{-s+,--stability=3D}'[specify minimum stability]: = :__composer_stabilities' \ + '1: :__composer_packages' \ + '2::project directory:_files -/' \ + '3:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_depends] )) || +_composer_depends() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-r --recursive)'{-r,--recursive}'[resolve recursively up to root = package]' \ + '(-t --tree)'{-t,--tree}'[display in tree format]' \ + '1: :__composer_packages --vendor' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_dump-autoload] )) || +_composer_dump-autoload() { + _arguments -s -S : \ + $_composer_global_opts \ + '--apcu[use APCu to cache found/not-found classes]' \ + '--no-dev[ignore autoload-dev rules]' \ + '--no-scripts[prevent execution of scripts defined in = composer.json]' \ + + '(a)' '(o)'{-a,--classmap-authoritative}'[auto-load from class = maps only (implies -o)]' \ + + '(o)' {-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_exec] )) || +_composer_exec() { + local ret=3D1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '(: * -l --list)'{-l,--list}'[display available binaries]' \ + '1: :__composer_binaries' \ + '*: :->next' \ + && ret=3D0 + + # Can't use *:: here, it won't complete subsequent options + [[ $state =3D=3D next ]] && { + shift words + (( CURRENT-- )) + unset _composer_cmd + _normal && ret=3D0 + } + + return ret +} + +(( $+functions[_composer_global] )) || +_composer_global() { + _composer_is_global=3D1 + _composer "$@" +} + +(( $+functions[_composer_help] )) || +_composer_help() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=3D[specify output format]:output format [txt]:(json md = txt xml)' \ + '--raw[output raw help (with text format)]' \ + '1: :__composer_commands' +} + +(( $+functions[_composer_home] )) || +_composer_home() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-H --homepage)'{-H,--homepage}'[use home page instead of = repository]' \ + '(-s --show)'{-s,--show}'[display URL only (do not open)]' \ + '*: :__composer_packages' \ +} + +(( $+functions[_composer_init] )) || +_composer_init() { + _arguments -s -S : \ + $_composer_global_opts \ + '--author=3D[specify package author]:package author' \ + '--description=3D[specify package description]:package description' = \ + '--homepage=3D[specify package home page]:package home page:_urls' = \ + '(-l --license)'{-l+,--license=3D}'[specify package license]: = :__composer_licenses' \ + '--name=3D[specify package name]: :__composer_packages' \ + '*--repository=3D[specify custom package repository]: = :__composer_repositories' \ + '*--require=3D[specify package to require]: :__composer_packages = --pairs' \ + '*--require-dev=3D[specify package to require for development]: = :__composer_packages --pairs' \ + '(-s --stability)'{-s+,--stability=3D}'[specify minimum stability]: = :__composer_stabilities' \ + '--type=3D-[specify package type]:: :__composer_types' +} + +(( $+functions[_composer_install] )) || +_composer_install() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--dry-run[do not actually install (implies -v)]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in = composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[auto-load from class = maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found = classes]' \ + + '(n)' '(a A o)--no-autoload[skip auto-loader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_licenses] )) || +_composer_licenses() { + _arguments -s -S : \ + $_composer_global_opts \ + '--no-dev[ignore require-dev packages]' \ + '--format=3D[specify output format]:output format [text]:(json = text)' +} + +(( $+functions[_composer_list] )) || +_composer_list() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=3D[specify output format]:output format [txt]:(json md = txt xml)' \ + '--raw[output raw help (with text format)]' \ + '1: :_guard "^-*" namespace' +} + +(( $+functions[_composer_outdated] )) || +_composer_outdated() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=3D[specify output format]:output format [text]:(json = text)' \ + '--strict[return non-zero exit code if there are outdated = packages]' \ + '1: :__composer_packages --vendor' \ + + '(a)' '(D I)'{-a,--all}'[display all installed packages]' \ + + '(D)' '(a)'{-D,--direct}'[display only packages directly required = by root package]' \ + + '(I)' '(a)*--ignore=3D[ignore specified package (with -o)]: = :__composer_packages' \ + + '(m)' '(a)'{-m,--minor-only}'[display only packages with minor = semver updates (with -o)]' \ + + '(o)' '!(a)'{-o,--outdated} +} + +(( $+functions[_composer_prohibits] )) || +_composer_prohibits() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-r --recursive)'{-r,--recursive}'[resolve recursively up to root = package]' \ + '(-t --tree)'{-t,--tree}'[display in tree format]' \ + '1: :__composer_packages --vendor' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_remove] )) || +_composer_remove() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dev[remove package from require-dev]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in = composer.json]' \ + '(--update-no-dev)--no-update[do not update dependencies]' \ + '(--update-with-dependencies)--no-update-with-dependencies[do not = update inherited dependencies]' \ + '(--no-update)--update-no-dev[update dependencies with --no-dev = option]' \ + '(--no-update-with-dependencies)--update-with-dependencies[update = inherited dependencies]' \ + '*: :__composer_packages --vendor' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[auto-load from class = maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found = classes]' \ + + '(n)' '(a A o)--no-autoload[skip auto-loader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_require] )) || +_composer_require() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dev[add package to require-dev]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in = composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '--prefer-lowest[prefer lowest versions of dependencies]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '--prefer-stable[prefer stable versions of dependencies]' \ + '--sort-packages[sort packages when adding/updating dependencies]' = \ + '(--no-update)--update-no-dev[update dependencies with --no-dev = option]' \ + '*: :__composer_packages --pairs' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[auto-load from class = maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found = classes]' \ + + '(n)' '(a A o)--no-autoload[skip auto-loader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' = \ + + '(u)' \ + '(--update-no-dev)--no-update[do not update dependencies]' \ + '--update-with-dependencies[update inherited dependencies, except = root requirements]' \ + '--update-with-all-dependencies[update all inherited dependencies]' +} + +(( $+functions[_composer_run-script] )) || +_composer_run-script() { + local ret=3D1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[enable dev mode]' \ + '(: * -l --list)'{-l,--list}'[display available scripts]' \ + '(--dev)--no-dev[disable dev mode]' \ + '--timeout=3D[specify script time-out]:time-out (seconds)' \ + '1: :__composer_scripts' \ + '*: :->next' \ + && ret=3D0 + + # Can't use *:: here, it won't complete subsequent options + [[ $state =3D=3D next ]] && { + shift words + (( CURRENT-- )) + unset _composer_cmd + _normal && ret=3D0 + } + + return ret +} + +(( $+functions[_composer_search] )) || +_composer_search() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-N --only-name)'{-N,--only-name}'[search package names only]' \ + '(-t --type)'{-t+,--type=3D}'[search for specified package type]: = :__composer_types' \ + '*: :__composer_packages' +} + +(( $+functions[_composer_self-update] )) || +_composer_self-update() { + _arguments -s -S : \ + $_composer_global_opts \ + + '(c)' \ + '--preview[force update to preview channel]' \ + '--snapshot[force update to snapshot channel]' \ + '--stable[force update to stable channel]' \ + + '(C)' '(u)--set-channel-only[set channel as default and exit]' \ + + u \ + '(C -r --rollback)--clean-backups[delete old back-ups during = update]' \ + '(C)--no-progress[do not display download progress]' \ + '(c C -r --rollback)'{-r,--rollback}'[roll back to earlier = installation]' \ + '(C)--update-keys[prompt for key update]' \ + '(C)1: :_guard "^-*" "Composer version"' +} + +(( $+functions[_composer_show] )) || +_composer_show() { + local ret=3D1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '--format=3D[specify output format]:output format [text]:(json = text)' \ + '--strict[return non-zero exit code if there are outdated = packages]' \ + '1: :->pkgs' \ + '2:: :_guard "^-*" "version constraint"' \ + + '(a)' '(D I s t)'{-a,--available}'[display only available = packages]' \ + + '(A)' '(D I s t)--all[display all packages]' \ + + '(D)' '(a A p s)'{-D,--direct}'[display only packages directly = required by root package]' \ + + '(i)' '(s)'{-i,--installed}'[display only installed packages]' \ + + '(I)' '(a A s t)*--ignore=3D[ignore specified package (with -o)]: = :__composer_packages' \ + + '(l)' '(s t)'{-l,--latest}'[display only latest version of = installed packages]' \ + + '(m)' '(a A s t)'{-m,--minor-only}'[display only packages with = minor semver updates (with -o)]' \ + + '(N)' '(P)'{-N,--name-only}'[display package names only]' \ + + '(o)' '(l t s)'{-o,--outdated}'[like -l, but display only = outdated packages]' \ + + '(p)' '(D)'{-p,--platform}'[display only PHP platform packages]' = \ + + '(P)' '(a A N)'{-P,--path}'[display package file paths]' \ + + '(s)' '(a A I l o t)'{-s,--self}'[display root package = information]' \ + + '(t)' '(a A I l o s)'{-t,--tree}'[display in tree format]' \ + && ret=3D0 + + [[ $state =3D=3D pkgs ]] && + if [[ -n $opt_args[(i)[aA]-*] ]]; then + __composer_packages && ret=3D0 + else + __composer_packages --vendor && ret=3D0 + fi + + return ret +} + +(( $+functions[_composer_suggests] )) || +_composer_suggests() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--by-suggestion)--by-package[group results by suggesting = package]' \ + '(--by-package)--by-suggestion[group results by suggested package]' = \ + '--no-dev[exclude suggestions from require-dev packages]' \ + '*: :__composer_packages --vendor' +} + +(( $+functions[_composer_update] )) || +_composer_update() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--dry-run[do not actually update (implies -v)]' \ + '(-i -n --interactive --no-interaction)'{-i,--interactive}'[update = with interactive interface]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '--lock[update composer.lock hash only]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in = composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '--prefer-lowest[prefer lowest versions of dependencies]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '--prefer-stable[prefer stable versions of dependencies]' \ + '--root-reqs[update only root-package dependencies]' \ + '*:: :__composer_packages --vendor' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[auto-load from class = maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found = classes]' \ + + '(n)' '(a A o)--no-autoload[skip auto-loader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' = \ + + '(u)' \ + '--with-dependencies[update dependencies of whitelisted packages, = except root requirements]' \ + '--with-all-dependencies[update all dependencies of whitelisted = packages]' +} + +(( $+functions[_composer_validate] )) || +_composer_validate() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-A --with-dependencies)'{-A,--with-dependencies}'[validate = composer.json of installed dependencies]' \ + '--no-check-all[do not validate completely]' \ + '--no-check-lock[ignore out-of-date composer.lock]' \ + '--no-check-publish[ignore publish errors]' \ + '--strict[return non-zero exit code for warnings as well as = errors]' \ + '1::composer.json file:_files -g "*.json(#q-.)"' +} + +_composer() { + local ret=3D1 tmp helps cmd + local -a context line state state_descr + local -A opt_args + + # These are meant for use by helper functions; we check for set-ness = first in + # case of `composer global` + (( $+_composer_cmd )) || { + local _composer_cmd=3D$words[1] _composer_is_global=3D0 = _composer_work_dir=3D + local -a _composer_cmds _composer_global_opts _composer_cache_pkgs + local -A _composer_cmd_aliases + + # alias -> canonical (mappings derived from source) + _composer_cmd_aliases=3D( + browse home + clearcache clear-cache + dumpautoload dump-autoload + i install + info show + selfupdate self-update + u update + upgrade update + why depends + why-not prohibits + ) + # Official commands (see note at top about custom ones) + _composer_cmds=3D( + about:'display short information about Composer' + archive:'create archive of project/package' + check-platform-reqs:'check that platform requirements are = satisfied' + clear{-,}cache:'clear internal package cache' + config:'set configuration options' + create-project:'create new project from package' + {depends,why}:'display packages depending on package' + diagnose:'diagnose system problems' + dump{-,}autoload:'dump class auto-loader' + exec:'execute vendored binary/script' + global:'run command in global Composer directory' + help:'display help information' + {home,browse}:'open package home page or repository in browser' + init:'create basic composer.json' + {install,i}:'install packages in composer.{lock,json}' + licenses:'display dependency license information' + list:'display supported commands' + outdated:'display outdated packages' + {prohibits,why-not}:'display packages preventing package from = being installed' + remove:'remove and uninstall required package' + require:'add and install required package' + run-script:'run scripts in composer.json' + search:'search packages' + self{-,}update:'upgrade Composer' + {show,info}:'display package information' + status:'display locally modified packages' + suggests:'display package suggestions' + {update,u,upgrade}:'upgrade packages in composer.json and update = composer.lock' + validate:'validate composer.{json,lock}' + ) + # Global options (can be used both before and after a command) + _composer_global_opts=3D( + '(-h --help)'{-h,--help}'[display help information]' + '(: * -)'{-V,--version}'[display version information]' + + # Symfony handles -vv and -vvv specially + '(-q -v --quiet --verbose)'{-q,--quiet}'[reduce output = verbosity]' + '(-q --quiet --verbose)*-v[increase output verbosity]' + '(-q --quiet --verbose)--verbose[increase output verbosity]' + + '(--no-ansi)--ansi[force ANSI (color) output]' + '(--ansi)--no-ansi[disable ANSI (color) output]' + + '(-n --no-interaction)'{-n,--no-interaction}'[run = non-interactively]' + + '--no-plugins[disable plug-ins]' + '--profile[display timing and memory usage information]' + '(-d --working-dir)'{-d+,--working-dir=3D}'[specify working = directory]:working directory:_files -/' + ) + + # Use caching by default; Composer is very slow + zstyle -s ":completion:*:*:$service:*" cache-policy tmp + [[ -n $tmp ]] || + zstyle ":completion:*:*:$service:*" cache-policy = __composer_cache_policy + + zstyle -s ":completion:*:*:$service:*" use-cache tmp + [[ -n $tmp ]] || + zstyle ":completion:*:*:$service:*" use-cache yes + } + + # Symfony's Application class naively intercepts --version and --help = no + # matter where they appear on the command line. --version is handled + # sufficiently by the global option spec above; for --help, we need = to + # simulate the help command + [[ -n $words[(r)(-h|--help)] ]] && { + helps=3D${#${(M)words[2,CURRENT]:#(-h|--help)}} + words=3D( "$words[1]" help "${(@)words[2,CURRENT]:#(-h|--help)}" ) + (( CURRENT -=3D helps - 1 )) + __composer_prune_global_opts -h --help + } + + _arguments -s -S -A '-*' : \ + $_composer_global_opts \ + '1: :__composer_commands' \ + '*:: :->next' \ + && ret=3D0 + __composer_update_work_dir + + [[ $state =3D=3D next ]] && { + # Resolve abbreviated/aliased command names and ensure that = $words[1] works + # as expected in our helper functions + __composer_resolve_cmd cmd $words[1] + words[1]=3D$_composer_cmd + + # Don't offer global options again after they've been given + __composer_prune_global_opts ${(k)opt_args} + # This is intentionally done twice + [[ $cmd =3D=3D help ]] && __composer_prune_global_opts -h --help + + if (( $+functions[_composer_$cmd] )); then + _composer_$cmd "$@" && ret=3D0 + else + _arguments -s -S : $_composer_global_opts && ret=3D0 + fi + } + + return ret +} + +_composer "$@"