From 204f6c24489a6000483a6c95c7d6291efa0cc67f Mon Sep 17 00:00:00 2001 From: Paul Ackersviller Date: Thu, 12 Apr 2007 02:58:13 +0000 Subject: Merge from trunk of workers/{21154,21257} and many unposted. --- Completion/Unix/Command/_perforce | 2309 +++++++++++++++++++++++++++++++++++++ 1 file changed, 2309 insertions(+) create mode 100644 Completion/Unix/Command/_perforce diff --git a/Completion/Unix/Command/_perforce b/Completion/Unix/Command/_perforce new file mode 100644 index 000000000..d2f2dda9d --- /dev/null +++ b/Completion/Unix/Command/_perforce @@ -0,0 +1,2309 @@ +#compdef p4 p4d -value-,P4CLIENT,-default- -value-,P4PORT,-default- -value-,P4MERGE,-default- -value-,P4USER,-default- + +# Maintainer: Peter Stephenson . + +# Increasingly loosely based on _cvs version 1.17. +# Completions currently based on Perforce release 2006.2. + +# Styles, tags and contexts +# ========================= +# +# If the `verbose' style is set (it is assumed by default), verbose +# descriptions are provided for many completed quantities derived +# dynamically such as subcommand names, labels, changes -- in fact, +# just about anything for which Perforce itself produces a verbose, +# one-line description. It may be turned off in the context of each +# subcommand e.g. +# zstyle ':completion:*:p4-labelsync:*' verbose false +# or for a particular tag, e.g. changes, +# zstyle ':completion:*:changes' verbose false +# or just for top-level completion (i.e. up to and including completion +# of the subcommand): +# zstyle ':completion:*:p4:*' verbose false +# or for p4 as a whole, +# zstyle ':completion:*:p4(-*|):*' verbose false +# This is actually handled by the `_describe' function underneath the +# Perforce completion system; it's mentioned here as verbosity adds +# significantly to a lot of the Perforce completions. +# +# Note that completing change numbers is not very useful if `verbose' is +# turned off. There is no speed advantage for turning it off, either. +# (Changes are also known as changelists or changesets. The functions +# and tags here all consistently use `changes'.) +# +# The style `max' can be set to a number which limits how many +# possibilities can be shown when selecting changes or jobs. This is +# handled within Perforce, so the completion code may limit the number even +# further. If not set explicitly, the value is taken to be 20 to avoid a +# huge database being output. Set it to a larger number if necessary. +# Setting it explicitly to zero removes the maximum. Because you see only +# the most recent, changes and jobs are shown in the order given by +# Perforce without further sorting. +# +# Completion of jobs can also be controlled by the `jobview' style. +# This uses the standard Perforce JobView syntax, and is applied +# in connection with the `max' style. In other words, +# if you set +# zstyle ':completion:*:p4-*:jobs' max 0 +# zstyle ':completion:*:p4-*:jobs' jobview 'user=pws' +# then jobs to be completed will be those from the output of +# p4 jobs -e 'user=pws' +# i.e. those assigned to Perforce user `pws'. +# +# Completion of changes can be controlled by the `changes' style. +# This takes additional arguments to be passed to `p4 changes'. +# An obvious example is: +# zstyle ':completion:*:p4-*:changes' changes -u $USER +# to limit changes to the present user. +# +# The style `all-files' is used to tell the completion system to +# complete any file in a given context. This is for use in places +# where it would, for example, only complete files opened for editing. +# See the next section for more. +# +# The style `depot-files' tells the system to complete files by asking +# Perforce for a list where it would otherwise complete files locally by +# the standard mechanism --- basically any time you don't use // notation +# and there is no restriction e.g. to opened files only. There is likely +# to be a significant speed penalty for this; it is turned off by default +# in all contexts. The advantage is that it cuts out files not maintained +# by Perforce. (Again, note this is a style, not a tag.) Contexts +# where this might be particularly useful include p4-diff or p4-diff2. +# +# The tags depot-files and depot-dirs also exist; they are used whenever +# the system is completing files or directories by asking Perforce +# to list them, rather than by using normal file completion. +# +# The tag subdirs is used to complete the special `...' which tells +# Perforce to search all subdirectories. Hence you can turn this +# feature off by suitably manipulating your tags. +# +# The function will usually try to limit the files it lists by +# context; for example, to just opened files. By default it does +# this by retrieving the complete list from Perforce and then +# relying on the completion system to do the matching. If this is +# slow, it is possible to set the style "glob", in which case the +# matching is done within Perforce, potentially reducing the amount of +# searching of Perforce's internal database. The tag used for +# this is the same as the command used to retrieve the file name: +# integrated, opened, resolved, dirs, files. The disadvantage +# of doing the matching within Perforce is that no matcher specification +# is applied; for example, it's not possible to match a_u.c against +# admin_utils.c. +# +# Actually, a hybrid strategy is used when the glob style is not set: the +# directory is passed literally to Perforce, but the file or directory +# being matched is passed as "*", so that matching on the contents of the +# directory is performed by the completion system. +# +# Experiment suggests that the glob style isn't usually needed: only +# "p4 integrated" is likely to be significantly slowed if no limiting +# pattern is applied, and completing only integrated files is uncommon. +# +# Completion of files and their revisions +# ======================================= +# +# File completion handles @ and # suffixes. If the filename is completed, +# typing @ or # removes the space which was automatically added. +# The context used has `at-suffix' or `hash-suffix' in the position +# before the tag to indicate suffix completion (as always, ^Xh will +# show you all possible contexts). This makes it possible +# to select changes, dates, labels and clients using the tag-order +# style. For example, +# zstyle ':completion:*:p4-*:at-suffix:*' tag-order changes '*' +# will force all completion after `@' to show changes first. Executing +# _next_tags (usually ^x^n) will cycle between that and the remaining +# tags (dates, labels, clients). I recommend, at least, keeping labels +# later than changes since the former are less useful and can take a long +# time to complete. +# +# A # is automatically quoted when handled in this way; if the file is +# typed by hand or the completion didn't finish (e.g. you typed a character +# in the middle of menu completion), you probably need to type `\#' by +# hand. The problem is that the completion system uses extended globbing +# and hence a pattern of the form `filename#' always matches `filename' +# (since e# matches any number of e's including one). Hence this can look +# like an expansion which expands to `filename'. +# +# After @, you can complete changes (note the use of the style `max' +# above), labels, clients or even dates, while after `#' you can +# complete numeric revisions or the special revision names head, none, +# have. These are available whether or not you completed the filename; if +# the file doesn't exist, numeric revisions won't work, but the rest will +# (though what Perforce will do with the resulting command is another matter). +# +# In addition, when completing after `file@', only changes specific to `file' +# will be shown (exactly the list of changes Perforce shows from the +# command `p4 changes file'). If this doesn't work, chances are that +# `file' does not exist. Having a multi-directory match (literal `...') +# in `file' should work fine, since `p4 changes' recognises all normal +# Perforce file syntax. +# +# Some perforce commands allow you to specify a range of revisions or +# changes as `file@1,@2' or `file#1,#2'. Currently, the second part of the +# revision range can always be completed (whether the command accepts them +# or not), but the comma after the first part of the range is only added +# automatically if the documentation suggests the command accepts ranges at +# that point. This is an auto-removable suffix, so it will disappear if +# you hit space or return. Typing a `#' at this point will insert a +# backslash, as before. The # and @ are never added automatically; you +# have to select one by hand. +# +# Perforce allows change and revision numbers to be preceded by =, <, <=, > +# or >=. See `p4 help undoc' for details. (In particular, `=' is +# an extremely useful shortcut when integrating single changes.) +# This syntax is handled, but currently the < and > must be quoted +# with a backslash, not by any other mechanism. For example, +# p4 files myfile@\>=3 +# will complete a change number. The valid syntax where the second +# change or revision in a range does not have the @ or # in front +# (for example `file@32183,32185') is not currently handled; the @ +# must be repeated. +# +# File completion for some functions is restricted by the Perforce +# status of the file; for example, `p4 opened' only completes opened +# files (surprised?) However, you can set the style (N.B. not tag) +# all-files; so, for example, you can turn off the limit in this case by +# zstyle ':completion:*:p4-opened:*' all-files true +# Normally the file-patterns style would be used to control matching, +# but as the file types are not selected by globbing it doesn't work here +# However, if you set the all-files style, all filename completion is done +# by the standard mechanism; in this case, the file-patterns style works +# as usual. The style ignored-patterns is available in any case, even +# without all-files; this is therefore generally the one to use. +# +# With `p4 diff', the shell will spot if you have used an option that +# allows you to diff unopened files (such as -f) and in that case offer +# all files; otherwise, it just offers opened files. +# +# Completion of changes +# ===================== +# +# There is various extra magic available any time change numbers +# are completed, regardless of how this was reached, i.e. +# `p4 fixes -c ...' and `p4 diff filename.c@...' are treated the same way. +# Note, however, these only work if you are at the point where a change +# number would be completed. +# +# Firstly, as mentioned above there is a maximum for the number of +# changes which will be shown, given by the style max, or defaulting to 20. +# Only the most recent changes will be shown. This is to avoid a speed +# penalty or clumsy output. If a positive numeric argument is given +# when changes are being completed, the maximum is set (unconditionally) +# to that number instead. +# +# It is also possible to give a negative numeric prefix to a listing widget +# (i.e. typically whatever is bound to ^D). If there is already a change +# number on the line, e.g. from cycling through a menu of choices, the full +# description for that change is shown in the format of a completion +# listing. [TODO: this could be made configurable with a style.] +# +# It may be necessary to abandon the current completion attempt before +# typing this to force the completion system to display the new text. +# Replacing delete-char-or-list with the following user defined widget +# (create with `zle -N ...') will force this for any negative prefix argument. +# (( ${NUMERIC:-0} < 0 )) && (( CURSOR = CURSOR )) +# zle delete-char-or-list +# +# Completion of jobs +# ================= +# +# Completing jobs uses the same logic for the numeric prefix as completing +# changes: a positive prefix changes the maximum number of jobs which +# will be shown, and a negative prefix when listing shows the full +# text for the job whose name is currently inserted on the command line. +# In this case, the entire text of the word being completed is assumed +# to constitute the job name (which is almost certainly correct). +# +# Completion of dates +# =================== +# +# In a file revision specification it is possible to give a date +# in the form file@YYYY/MM/DD:hh:mm:ss, which may be completed. This +# is ever so slightly less silly than it sounds. Any component entered +# by hand with the appropriate suffix will be ignored; any component +# completed will be set to the current value. Hence you can easily +# specify, say, one month ago by using the completed value for all +# components except the month and setting that to one less. The shell +# will also happily append the appropriate suffix if you try to complete +# after anything which is already the appropriate width. (Perforce +# supports two-digit years, but these are confusing and no longer +# particularly useful as they refer to the twentieth century, so +# the shell does not.) +# +# Calls to p4 +# =========== +# +# Much of the information from Perforce is provided by calls to p4 +# commands. This is done via the _call_program interface, as described +# in the zshcompsys manual page. Hence a suitable context with the +# `command' style allows the user to take control of this call. +# The tags used are the name of the p4 command, or in the case of +# calls to help subcommands, `help-'. Note that if the +# value of the style begins with `-', the arguments to the perforce +# command are appended to the remaining words of the style before calling +# the command. +# +# Programmes taking p4-style arguments +# ==================================== +# +# It is possible to use the _perforce completion with other commands +# which behave like a subcommand of p4 by setting the service type +# to p4-. For example, +# compdef _perforce p4cvsmap=p4-files +# says that the command `p4cvsmap' takes arguments like `p4 files'. +# Often the options will be different; if this is a problem, you +# will need to write your own completer which loads _perforce and +# calls its functions directly. You can add -global to the end +# of the service to say that the command also handles global +# Perforce options, comme ca: +# compdef _perforce p4reopen=p4-job-global +# +# Anything more complicated should be modelled on one of the +# _perforce_cmd_* handlers below. To get this to work, the full +# set of _perforce functions must be loaded; because of the way +# autoloading works, a trick is required: call "_perforce -l" which +# causes the function to be executed, loading all the associated +# functions as a side effect, but tells _perforce to return without +# generating any completions. For example, here is the completion +# for my `p4desc' function which is an enhanced version of `p4 describe' +# (without any handling for global Perforce arguments): +# +# #compdef p4desc +# +# _perforce -l +# +# _arguments -s : \ +# '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context d\:basic\ diff n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \ +# '-s[short form]' \ +# '-j[select by job]:job:_perforce_jobs' \ +# '*::change:_perforce_changes' +# +# To add handling of global options to this, see the end of the _perforce +# function below. Something like: +# +# local -a _perforce_global_options _perforce_option_dispatch +# if _perforce_global_options; then +# _arguments -s : $_perforce_option_dispatch \ +# '' +# fi +# +# TODO +# ==== +# +# No mechanism is provided for completely ignoring certain files not +# handled by Perforce as with .cvsignore. This could be done ad hoc. +# However, the ignored-patterns style and the parameter $fignore are +# of course applied as usual, so setting ignored-patterns for the +# context `:completion:*:p4[-:]*' should work. + +_perforce() { + # rely on localoptions + setopt nonomatch + local p4cmd==p4 match mbegin mend + integer _perforce_cmd_ind + + if [[ $1 = -l ]]; then + # Run to load _perforce and associated functions but do + # nothing else. + return + fi + + if [[ $service = -value-* ]]; then + # Completing parameter value. + # Some of these --- in particular P4PORT --- don't need + # the perforce server. + case $compstate[parameter] in + (P4PORT) + _perforce_hosts_ports + ;; + + (P4CLIENT) + _perforce_clients + ;; + + (P4MERGE) + _command_names -e + ;; + + (P4USER) + _perforce_users + ;; + esac + # We do not handle values anywhere else. + return + fi + + if [[ $p4cmd = '=p4' ]]; then + _message "p4 executable not found: completion not available" + return + fi + + # If we are at or after the command word, remember the + # global arguments to p4 as we will need to pass these down + # when generating completion lists. + # This is both an array and a function, but luckily I never + # get confused... + local -a _perforce_global_options + local -a _perforce_option_dispatch + + # If we are given a service of the form p4-cmd, treat this + # as if it was after `p4 cmd'. This provides an easy way in + # for scripts and functions that emulate the behaviour of + # p4 subcommands. Note we don't shorten the command line arguments. + if [[ $service = p4-(#b)(*) ]]; then + local curcontext="$curcontext" + local p4cmd=$words[1] cmd=$match[1] gbl + + if [[ $cmd = (#b)(*)-global ]]; then + # Handles global options. + cmd=$match[1] + _perforce_global_options && gbl=1 + fi + if (( $+functions[_perforce_cmd_$cmd] )); then + curcontext="${curcontext%:*:*}:p4-${cmd}:" + if [[ -n $gbl ]]; then + # We are handling global Perforce options as well as the + # arguments to the specific command. + # To handle the latter, we need the command name, plus + # all the arguments for the command with the global options + # removed. The function _perforce_service_dispatch handles + # this by unshifting the command ($p4cmd) into words, + # then dispatching for the Perforce subcommand $cmd. + # + # Has anyone noticed this is getting rather complicated? + _arguments -s : $_perforce_option_dispatch \ + "*::p4-$cmd arguments: _perforce_service_dispatch $p4cmd $cmd" + else + _perforce_cmd_$cmd + fi + # Don't try to do full command handling. + return + else + _message "unhandled _perforce service: $service" + return 1 + fi + fi + + if [[ $service = p4d ]]; then + _arguments -s : \ + '-d[run as daemon]' \ + '-f[run as single threaded server]' \ + '-i[run for inetd using sockets]' \ + '-q[suppress startup message]' \ + '-s[run as NT service]' \ + '-xi[switch server database to unicode mode and quit]' \ + '-xu[run database upgrade and quit]' \ + '-c[run command and exit]:command of some sort: ' \ + '-jc[checkpoint, save and truncate journal]::optional prefix: ' \ + '-jd[checkpoint, not saving journal]::optional file:_files' \ + '-jj[save and truncate journal]::optional prefix: ' \ + '-jr[incremental restore from checkpoint/journal]:'\ +'file:_files:file:_files' \ + '-z[gzip checkpoint and journal files]' \ + '-h[show help]' \ + '-V[print server version]' \ + '-A[set audit log ($P4AUDIT)]:audit file:_files' \ + '-J[set journal file ($P4JOURNAL) or "off"]:journal file:_files' \ + '-L[set error log ($P4LOG or stderr)]:error log:_files' \ + '-p[set port ($P4PORT o perforce:1666)]:port:_perforce_hosts_ports' \ + '-r[set root directory ($P4ROOT)]:root directory:_path_files -g "*(/)"' \ + '-v[debug level]:level: ' + elif _perforce_global_options; then + _arguments -s : $_perforce_option_dispatch \ + '1:perforce command:_perforce_commands' + else + (( _perforce_cmd_ind-- )) + (( CURRENT -= _perforce_cmd_ind )) + shift $_perforce_cmd_ind words + _perforce_command_args + fi +} + + +# +# Command and argument dispatchers +# + +# Front end to _call_program to add in the global arguments +# passed to p4. The first argument is the tag, the remaining +# arguments are passed to p4. Typically the tag is the same +# as the first p4 argument. +(( $+functions[_perforce_call_p4] )) || +_perforce_call_p4() { + local cp_tag=$1 + shift + # This is for our own use for parsing, and we need English output, + # so... + local +x P4LANGUAGE + _call_program $cp_tag p4 "${_perforce_global_options[@]}" "$@" +} + + +# The list of commands is cached in _perforce_cmd_list, but we +# only generate it via this function when we need it. +(( $+functions[_perforce_gen_cmd_list] )) || +_perforce_gen_cmd_list() { + (( ${+_perforce_cmd_list} )) || typeset -ga _perforce_cmd_list + local hline line match mbegin mend + # Output looks like command-namedescription in words... + # Ignore blank lines and the heading line beginning `Perforce...' + # Just gets run once, then cached, so don't bother optimising + # this to a grossly unreadable parameter substitution. + _perforce_call_p4 help-commands help commands | while read -A hline; do + (( ${#hline} < 2 )) && continue + [[ $hline[1] = (#i)perforce ]] && continue + _perforce_cmd_list+=("${hline[1]}:${hline[2,-1]}") + done + # Also cache the server version for nefarious purposes. + _perforce_call_p4 info info | while read line; do + if [[ $line = (#b)"Server version: "*/*/(<->.<->)(.[^/]|)/*" "* ]]; then + _perforce_server_version=$match[1] + fi + done + + # Unsupported commands: we could look through p4 help undoc, I suppose. + # I can't be bothered to check the date when they appeared any more, + # but let's at least check they're not already there. + local -a unsup + unsup=( + "attribute:Set attributes for open file (EXPERIMENTAL)" + "interchanges:Report changes not yet integrated between branches" + "spec:Allows limited changes to form specifications (admin)" + ) + for line in $unsup; do + if [[ ${_perforce_cmd_list[(r)${line%%:*}:*]} = '' ]]; then + _perforce_cmd_list+=($line) + fi + done +} + + +(( $+functions[_perforce_commands] )) || +_perforce_commands() { + (( ${#_perforce_cmd_list} )) || _perforce_gen_cmd_list + _describe -t p4-commands 'Perforce command' _perforce_cmd_list +} + + +(( $+functions[_perforce_command_args] )) || +_perforce_command_args() { + local curcontext="$curcontext" cmd=${words[1]} + if (( $+functions[_perforce_cmd_$cmd] )); then + curcontext="${curcontext%:*:*}:p4-${cmd}:" + _perforce_cmd_$cmd + else + _message "unhandled perforce command: $cmd" + fi +} + + +(( $+functions[_perforce_service_dispatch] )) || +_perforce_service_dispatch() { + # Put the original command name back, then dispatch for + # our Perforce handler. + words=($1 "$words[@]") + (( CURRENT++ )) + _perforce_cmd_$2 +} + + +# +# Helper functions +# + +(( $+functions[_perforce_global_options] )) || +_perforce_global_options() { + # Options with arguments we need to pass down when calling + # p4 from completers. There are no options without arguments + # we need to pass. (Don't pass down -L language since we + # parse based on English output.) + local argopts_pass="cCdHpPu" + # Other options which have arguments but we shouldn't pass down. + # There are some debugging options, but they tend to get used + # with the argument in the same word as the option, in which + # case they will be handled OK anyway. + local argopts_ignore="Lx" + + # The options we support in the form for _arguments. + # This is here for modularity and convenience, but note that since the + # actual dispatch takes place later, this is not local to this + # function and so must be made local in the caller. + _perforce_option_dispatch=( + '-c+[client]:client:_perforce_clients' \ + '-C+[charset]:charset:_perforce_charsets' \ + '-d+[current directory]:directory:_path_files -g "*(/)"' \ + '-H+[hostname]:host:_perforce_hosts' \ + '-G[python output]' \ + '-L+[message language]:language: ' \ + '-p+[server port]:port:_perforce_hosts_ports' \ + '-P+[password on server]:password: ' \ + '-s[output script tags]' \ + '-u+[user]:user name:_perforce_users' \ + '-x+[filename or -]:file:_perforce_files_or_minus' \ + ) + + integer i + + # We need to try and check if we are before or after the + # subcommand, since some of the options with arguments, in particular -c, + # work differently. It didn't work if I just added '*::...' to the + # end of the arguments list, anyway. + for (( i = 2; i < CURRENT; i++ )); do + if [[ $words[i] = -[$argopts_pass$argopts_ignore] ]]; then + # word with following argument --- check this + # is less than the current word, else we are completing + # this and shouldn't pass it down + if [[ $(( i + 1 )) -lt $CURRENT && \ + $words[i] = -[$argopts_pass] ]]; then + _perforce_global_options+=(${words[i,i+1]}) + fi + (( i++ )) + elif [[ $words[i] = -[$argopts_pass]* ]]; then + # word including argument which we want to keep + _perforce_global_options+=(${words[i]}) + elif [[ $words[i] != -* ]]; then + break + fi + done + + (( _perforce_cmd_ind = i )) + (( _perforce_cmd_ind >= CURRENT )) +} + +(( $+functions[_perforce_branches] )) || +_perforce_branches() { + local bline match mbegin mend + local -a bl + bl=(${${${(f)"$(_perforce_call_p4 branches branches 2>/dev/null)"}##Branch }/ /:}) + [[ $#bl -eq 1 && $bl[1] = '' ]] && bl=() + (( $#bl )) && _describe -t branches 'Perforce branch' bl +} + + +(( $+functions[_perforce_changes] )) || +_perforce_changes() { + local cline match mbegin mend max ctype num comma file + local -a cl cstatus amax xargs + + zstyle -s ":completion:${curcontext}:changes" max max || max=20 + zstyle -a ":completion:${curcontext}:changes" changes xargs + if [[ ${NUMERIC:-0} -lt 0 && -z $compstate[insert] ]]; then + # Not inserting (i.e. just listing) and given a negative + # prefix argument. Instead of listing possible completions, + # show the full description for the change number on the line at + # the moment. + [[ $PREFIX = (|*[^[:digit:]])(#b)(<->) ]] && num+=$match[1] + [[ $SUFFIX = (#b)(<->)* ]] && num+=$match[1] + if [[ -n $num ]]; then + _message -r "$(_perforce_call_p4 describe describe $num)" + return 0 + fi + elif [[ ${NUMERIC:-0} -gt 0 ]]; then + max=$NUMERIC + fi + + (( max )) && amax=(-m $max) + + # Hack: assume the arguments we want are at the end. + while [[ $argv[-1] = -t? ]]; do + case $argv[-1] in + # Change embedded in filename; extract that and remove + # the corresponding prefix. Remove possible `#'s, too, + # in case we are looking at a range. + (-tf) + file=${${(Q)PREFIX}%%[\#@]*} + compset -P '*@(|\\\<|\\\>)(|=)' + ;; + + # Changes already submitted + (-ts) + cstatus=(-s submitted) + ctype="submitted " + ;; + + # Changes still pending + (-tp) + cstatus=(-s pending) + ctype="pending " + ;; + + # Changes still pending and on the current client. + # Many uses of pending changes must be on the current client + # to be meaningful, in particular submitting a change or + # associating a file or operation with a particular change. + (-tc) + # Don't like this way of extracting the client to use, + # but I don't see a simpler one. We do at least make sure + # we call p4 with the same arguments as we will use with p4 changes. + cstatus=(-s pending -c "$(_perforce_call_p4 client client -o | +awk '/^Client:/ { print $2 }')") + ctype="local pending " + ;; + + # Range allowed: append comma and supply rules for + # removing and handling subsequent `#'. + (-tR) + comma=(-S, -R _perforce_file_suffix) + esac + argv=($argv[1,-2]) + done + # Limit to the 20 most recent changes by default to avoid huge + # output. + cl=( +${${${${(f)"$(_perforce_call_p4 changes changes $amax $xargs $cstatus \$file)"}##Change\ }//\ on\ /:}/\ by\ /\ } +"default:change not yet numbered") + [[ $#cl -eq 1 && $cl[1] = '' ]] && cl=() + _describe -t changes "${ctype}change" cl -V changes-unsorted $comma +} + + +(( $+functions[_perforce_charsets] )) || +_perforce_charsets() { + local expl + _wanted charset expl 'character set' \ + compadd eucjp iso8859-1 shiftjis utf8 winansi +} + + +(( $+functions[_perforce_clients] )) || +_perforce_clients() { + local -a slash cl + + # Are we completing after an @, or a client view in a filespec? + if ! compset -P '*@'; then + compset -P '//' && slash=(-S/ -q) + fi + + cl=(${${${(f)"$(_perforce_call_p4 clients clients)"}##Client\ }/\ /:}) + [[ $#cl -eq 1 && $cl[1] = '' ]] && cl=() + _describe -t clients 'Perforce client' cl $slash +} + + +(( $+functions[_perforce_counters] )) || +_perforce_counters() { + local cline match mbegin mend + local -a cl + + cl=(${${${(f)"$(_perforce_call_p4 counters counters)"}/\ /:}/\=/current value}) + [[ $#cl -eq 1 && $cl[1] = '' ]] && cl=() + _describe -t counters 'Perforce counter' cl +} + + +(( $+functions[_perforce_counter_values] )) || +_perforce_counter_values() { + if [[ -n $words[CURRENT-1] ]]; then + local value="$(_perforce_call_p4 counter counter $words[CURRENT-1] 2>/dev/null)" + if [[ -n $value ]]; then + # No space. This allows stuff like incarg and decarg. + compstate[insert]=1 + _wanted value expl 'counter value' compadd $value + fi + fi +} + + +(( $+functions[_perforce_dates] )) || +_perforce_dates() { + # Only useful in a file spec after `@'. + compset -P '*@' + + # Date/time now in format required by Perforce. + local now="$(date +%Y:%m:%d:%T)" name prefix + local -a nowarray offer opts matchpats suffixes names + nowarray=(${(s.:.)now}) + + names=( year month day\ of\ month hour minute second) + suffixes=( / / : : : '' ) + + integer i + prefix=${(Q)PREFIX} + for (( i = 6; i >= 1; i-- )); do + # Match from the most specific back. + # The following is one of those occasions where zsh + # substitution skips to the right answer without ever + # passing through the real world on the way. + if [[ $prefix = *${(j.*.)~suffixes[1,i-1]}* ]]; then + (( i > 1 )) && compset -P "*$suffixes[i-1]" + # If what's there already is the right length, + # just accept it and add the suffix. + prefix=${(Q)PREFIX} + if [[ ${#prefix} = ${#nowarray[i]} ]]; then + offer=($prefix) + else + offer=($nowarray[i]) + fi + [[ -n $suffixes[i] ]] && opts=(-S $suffixes[i] -q) + name=$names[i] + break + fi + done + + _describe -t dates $name offer $opts +} + + +(( $+functions[_perforce_depots] )) || +_perforce_depots() { + local dline match mbegin mend + local -a dl + + dl=(${${${(f)"$(_perforce_call_p4 depots depots)"}##Depot\ }/\ /:}) + [[ $#dl -eq 1 && $dl[1] = '' ]] && dl=() + _describe -t depots 'depot name' dl +} + + +(( $+functions[_perforce_files_or_minus] )) || +_perforce_files_or_minus() { + _alternative 'minus:minus sign:(-)' 'files:file name:_files' +} + + +(( $+functions[_perforce_file_suffix] )) || +_perforce_file_suffix() { + # Used with compadd -R to handle @ or # after a file name. + # Differs from compadd -r '...' in that it quotes `#' if typed. + [[ $1 = 1 ]] || return + + if [[ $LBUFFER[-1] = [\ ,] ]]; then + if [[ $KEYS = '#' ]]; then + if [[ $LBUFFER[-1] = , ]]; then + # Range: no suffix removal but add a backslash + LBUFFER+=\\ + else + # Suffix removal with an added backslash + LBUFFER="$LBUFFER[1,-2]\\" + fi + elif [[ $KEYS = (*[^[:print:]]*|[[:blank:]\;\&\|]) || \ + ( $KEYS = @ && $LBUFFER[-1] = ' ' ) ]] ; then + # Normal suffix removal + LBUFFER="$LBUFFER[1,-2]" + fi + elif [[ $LBUFFER[-1] = / ]]; then + # Normal suffix removal for directories. + if [[ $KEYS = (*[^[:print:]]*|[[:blank:]\;\&\|/]) ]]; then + LBUFFER="$LBUFFER[1,-2]" + fi + fi +} + + +# +# Helper function for the helper functions for the helper function +# _perforce_files. This is common code to retrieve a list of files +# from Perforce. +# +# First argument is the p4 subcommand used to list the files. +# This is also used as a tag for the style to decide whether +# to limit the list within Perforce. +# Remaining arguments are additional arguments to compadd. +# +(( $+functions[_perforce_retrieve_files] )) || +_perforce_retrieve_files() { + local pfx + local -a files + + if zstyle -t ":completion:${curcontext}:$1" glob; then + # Limit the list by using Perforce to glob the pattern. + # This may be faster, but won't use matcher specs etc. + pfx=${(Q)PREFIX} + compset -P '*/' + files=(${${${(f)"$(_perforce_call_p4 $1 $1 \"\$pfx\*\$\{\(Q\)SUFFIX\}\" 2>/dev/null)"}%%\#*}##*/}) + else + # We need to limit the list to a directory. + if [[ $PREFIX = */* ]]; then + pfx="${(Q)${PREFIX%/*}}/*" + else + pfx="*" + fi + compset -P '*/' + files=(${${${(f)"$(_perforce_call_p4 $1 $1 \$pfx 2>/dev/null)"}%%\#*}##*/}) + fi + [[ $#files -eq 1 && $files[1] = '' ]] && files=() + shift + compadd "$@" -a files +} + + +# +# Helper functions for the helper function _perforce_files. These files +# are low-level enough that they don't handle tags; this is done +# by the _alternative handler in _perforce_files. +# + +(( $+functions[_perforce_integrated_files] )) || +_perforce_integrated_files() { + local pfx=${(Q)PREFIX} type + local -a files + + _perforce_retrieve_files integrated "$@" +} + + +(( $+functions[_perforce_opened_files] )) || +_perforce_opened_files() { + local type + local -a files + + _perforce_retrieve_files opened "$@" +} + + +(( $+functions[_perforce_resolved_files] )) || +_perforce_resolved_files() { + _perforce_retrieve_files resolved "$@" +} + + +(( $+functions[_perforce_subdirs] )) || +_perforce_subdirs() { + # This has no other function than to offer to add the `...' used + # by Perforce to indicate a recursive search of directories. + # Bit pathetic, really. + compset -P '*/' + compadd "$@" '...' +} + + +(( $+functions[_perforce_depot_dirs] )) || +_perforce_depot_dirs() { + # Normal completion of directories in depots + + _perforce_retrieve_files dirs "$@" -S / -q +} + + +(( $+functions[_perforce_depot_files] )) || +_perforce_depot_files() { + # Normal completion of files in depots + + _perforce_retrieve_files files "$@" -R _perforce_file_suffix +} + + +(( $+functions[_perforce_client_dirs] )) || +_perforce_client_dirs() { + # This is a slightly odd addition which isn't often necessary. + # When completing directories in a client specification, Perforce + # doesn't tell you about intermediate directories which are in + # the client, but not in the depot. (Well... sometimes. I've + # had some odd results with this. I suspect there may be a bug + # but I don't really know enough to be sure.) + # + # For example, if my view contains + # //depot/branches/rev1.2/... //pws_client/branches/rev1.2/... + # then `p4 dirs "//pws_client/*"' won't mention the `branches' + # directory because the view actually starts lower down. So + # we add it by hand when necessary. + # + # We don't want to waste time on this, since it's not the usual + # case, so we cache the results where necessary. This means + # recording all the clients that we can later ask about if necessary. + # To flush the cache, `unset _perforce_client_list _perforce_client_dirs'. + if (( ! ${+_perforce_client_list} )); then + # Retrieve the list of clients. + typeset -gA _perforce_client_list + local -a tmplist + local tmpelt + tmplist=(${${${(f)"$(_perforce_call_p4 clients clients)"}##Client\ }%%\ *}) + [[ $#tmplist -eq 1 && $tmplist[1] = '' ]] && tmplist=() + for tmpelt in $tmplist; do + _perforce_client_list[$tmpelt]=1 + done + fi + + # See if the first path element is a client. Very often it + # will actually be a depot, so we test this as quickly as possible. + local client=${${PREFIX##//}%%/*} + [[ -z ${_perforce_client_list[$client]} ]] && return 1 + + local oldifs=$IFS IFS= type dir line dirs + + (( ${+_perforce_client_dirs} )) || typeset -gA _perforce_client_dirs + + if (( ${+_perforce_client_dirs[$client]} )); then + # Already cached, although may be empty. + dirs=${_perforce_client_dirs[$client]} + else + # We need to look at the View stanza of the client record + # to see what directories exist in the client view. + _perforce_call_p4 client "client -o $client" 2>/dev/null | while read line + do + case $line in + ([[:blank:]]##) + type= + ;; + + ((#b)([[:alpha:]]##):*) + type=${match[1]} + ;; + + (*) + if [[ $type = View ]]; then + dir=${${line##[[:blank:]]##//*[[:blank:]]//$client}%%/...(/*|)} + if [[ $#dir -gt 1 ]]; then + dirs+="${dirs:+ }${(q)dir##/}" + fi + fi + ;; + esac + done + fi + + (( ${#dirs} )) || return 1 + + # Turn our string of space-separated backquoted elements into an array. + dirs=(${(z)dirs}) + # Get the current prefix also as an array of elements + compset -P '//[^/]##/' + pfx=(${(s./.)${(Q)PREFIX}}) + + local -a ndirs + local match mbegin mend + # Check matching path segments + while (( ${#pfx} > 1 )); do + ndirs=() + for dir in $dirs; do + if [[ $dir = $pfx/(#b)(*) ]]; then + ndirs+=($match[1]) + fi + done + (( ${#ndirs} )) || return 1 + dirs=($ndirs) + shift pfx + compset -P '[^/]' + done + compadd -S / -q "$@" -- ${dirs%%/*} +} + +(( $+functions[_perforce_files] )) || +_perforce_files() { + local pfx fline expl opt match mbegin mend range type + local -a files types + + local dodirs unmaintained + # Suffix operations can modify context + local curcontext="$curcontext" + + while (( $# )); do + if [[ $1 = -t(#b)(?) ]]; then + case $match[1] in + (d) + dodirs=-/ + ;; + + (u) + unmaintained=1 + ;; + + (i) + types+=(integrated) + ;; + + (o) + types+=(opened) + ;; + + (r) + types+=(resolved) + ;; + + (R) + range="-tR" + ;; + esac + fi + shift + done + + # Remove the quotes present in the word on the command line, + # since we will treat this as a literal string from now on. + # We might get into problems with characters recognised as + # special by p4 files and p4 dirs, but worry about that later. + pfx=${(Q)PREFIX} + if [[ -prefix *@ ]]; then + # Modify context to indicate we are in a suffix. + curcontext="${curcontext%:*}:at-suffix" + # Check for existing range syntax + [[ $PREFIX = *[@\#]*,* ]] && range= + # After @ you can specify changes, clients, labels or dates. + # Note we don't remove the prefix here; we leave it to the + # subcommand. This is in case it needs information from + # the prefix; _perforce_changes uses this to limit the + # output to relevant changes. + _alternative \ + "changes:change:_perforce_changes $range -tf" \ + clients:client:_perforce_clients \ + "labels:label:_perforce_labels -tf" \ + 'dates:date (+ time):_perforce_dates' + elif [[ -prefix *\# ]]; then + # Modify context to indicate we are in a suffix. + curcontext="${curcontext%:*}:hash-suffix" + # Check for existing range syntax + [[ $PREFIX = *[@\#]*,* ]] && range= + # Remove longest possible tail match to get name --- this + # automatically handles filenames in ranges e.g. `foo#1,#3'. + # (Note the compset removes the maximum possible head match, + # so we only complete the second part of the range in that case.) + _perforce_revisions $range + elif [[ $PREFIX = //* ]]; then + # This specifies files already handled by Perforce, so there's + # no point trying to look for unmaintained files. Assume + # the user knows what they're doing. + local -a altfiles + + if [[ $PREFIX = //[^/]# ]]; then + # Complete //clientname spec. Don't complete non-directories... + # I don't actually know if they are valid here. + altfiles+=("clients:Perforce client:_perforce_clients") + else + local donefiles=1 + if [[ -z $dodirs ]]; then + if [[ ${#types} -gt 0 ]] && + ! zstyle -t ":completion:${curcontext}:" all-files; then + for type in $types; do + altfiles+=("$type-files:$type file:_perforce_${type}_files") + done + else + altfiles+=("depot-files:file in depot:_perforce_depot_files") + fi + fi + # Intermediate directories in a client view. + # See function for notes. + altfiles+=("client-dirs:client directory:_perforce_client_dirs") + fi + altfiles+=("depot-dirs:directory in depot:_perforce_depot_dirs" + "subdirs:subdirectory search:_perforce_subdirs") + _alternative $altfiles + elif [[ -n $unmaintained && -z $dodirs ]]; then + # a la _cvs_nonentried_files: directories are never maintained, + # so skip 'em. Unmaintained files can't be integrated, opened + # or resolved, so treat as exclusive (just as well, since + # this bit's messy). + local MATCH MBEGIN MEND + local -a omitpats + + match=() + : ${PREFIX:#(#b)(*/)(*)} + pfx="$match[1]" + pfx=${(e)~pfx} + # Exclude both files already known to perforce, plus + # those opened. There will be some overlap but we need + # to exclude files that are already opened for add. + omitpats=( + ${${${${(f)"$(_perforce_call_p4 files files \"\$pfx\*\$\{\(Q\)SUFFIX\}\" 2>/dev/null)"}%\#*}##*/}//(#m)[][*?()<|^~#\\]/\\$MATCH} + ${${${${(f)"$(_perforce_call_p4 opened opened \"\$pfx\*\$\{\(Q\)SUFFIX\}\" 2>/dev/null)"}%\#*}##*/}//(#m)[][*?()<|^~#\\]/\\$MATCH} + ) + + [[ $#omitpats -eq 1 && $omitpats[1] = '' ]] && omitpats=() + if (( ${#omitpats} )); then + _path_files -g "*~(*/|)(${(j:|:)~omitpats})(D.)" + else + _path_files + fi + # Don't handle suffixes for non-entried files + elif (( ${#types} )) && ! zstyle -t ":completion:${curcontext}:" all-files + then + local -a altfiles + + for type in $types; do + altfiles+=("$type-files:$type file:_perforce_${type}_files") + done + + altfiles+=("subdirs:subdirectory search:_perforce_subdirs") + if zstyle -t ":completion:${curcontext}:" depot-files; then + altfiles+=("depot-dirs:directory in depot:_perforce_depot_dirs") + else + altfiles+=("directories:directory:_path_files -/") + fi + _alternative $altfiles + elif zstyle -t ":completion:${curcontext}:" depot-files; then + local -a altfiles + if [[ -z $dodirs ]]; then + altfiles+=("depot-files:file in depot:_perforce_depot_files") + fi + altfiles+=("depot-dirs:directory in depot:_perforce_depot_dirs" + "subdirs:subdirectory search:_perforce_subdirs") + _alternative $altfiles + else + # Look locally. + _alternative \ + "files:file:_path_files -R _perforce_file_suffix $dodirs" \ + "subdirs:subdirectory search:_perforce_subdirs" + fi +} + + +# +# Remaining helpers for other types of Perforce metadata. +# + +(( $+functions[_perforce_filetypes] )) || +_perforce_filetypes() { + local -a values + if compset -P '*+*'; then + # That second `*' is deliberate --- only complete the last + # letter since we can have a whole string of them. + values=( + "m:always set modtime on client" + "w:always writeable on client" + "x:set exec bit on client" + "k:full RCS keyword expansion" + "k:RCS expansion only for Id, Header" + "l:exclusive open, disallow multiple opens" + "C:server stores compress file per revision" + "D:server stores deltas in RCS format" + "F:server stores full file per revision" + "S:server stores only head revision") + _describe -t file-modifiers 'Perforce file modifier' values + else + values=( + "text:text, translate newlines" + "binary:raw bytes" + "symlink:symbolic link" + "apple:Mac resource + data" + "unicode:text, translate newlines, store as UTF-8") + _describe -t file-types 'Perforce file type' values -S+ -q + fi +} + + +(( $+functions[_perforce_groups] )) || +_perforce_groups() { + local -a values + values=($(_perforce_call_p4 groups groups)) + _describe -t groups 'Perforce group' values +} + + +(( $+functions[_perforce_hosts] )) || +_perforce_hosts() { + local expl host + # Completion for p4 -H; other forms of host completion + # go through _perforce_hosts_ports. + # From Felix: if the client specifies a hostname, there's + # no point using any other host, since it won't work. + host=$(_perforce_call_p4 client client -o | + awk '$1 ~ /^Host:/ {print $2}' ) + if [[ -n $host ]]; then + _wanted hosts expl host compadd "$@" $host + else + _hosts + fi +} + + +(( $+functions[_perforce_hosts_ports] )) || +_perforce_hosts_ports() { + if compset -P '*:'; then + _ports + local expl + _wanted ports expl port compadd "$@" 1666 + else + # is this -q-able? + _hosts -S : + fi +} + +(( $+functions[_perforce_jobs] )) || +_perforce_jobs() { + # Optional argument is jobview for limiting jobs. + local jline match mbegin mend max jobview + local -a jl amax ajobview + + zstyle -s ":completion:${curcontext}:jobs" max max || max=20 + # Hack: if there is a job view, it is at the end. + # This is nasty, it's really unnecessarily difficult to + # pass arguments within completion functions... + if [[ $argv[-2] = -e ]]; then + ajobview=(-e "${(q)argv[-1]}") + argv=("${(@)argv[1,-3]}") + else + zstyle -s ":completion:${curcontext}:jobs" jobview jobview && + ajobview=(-e $jobview) + fi + if [[ ${NUMERIC:-0} -lt 0 && -z $compstate[insert] ]]; then + # Not inserting (i.e. just listing) and given a negative + # prefix argument. Instead of listing possible completions, + # show the full description for the job which is on the line at + # the moment. + _message -r "$(_perforce_call_p4 jobs jobs -e \"Job=\$PREFIX\$SUFFIX\" -l 2>/dev/null)" + return 0 + elif [[ ${NUMERIC:-0} -gt 0 ]]; then + max=$NUMERIC + fi + + (( max )) && amax=(-m $max) + + _perforce_call_p4 jobs jobs $ajobview $amax | while read jline; do + if [[ $jline = (#b)([^[:blank:]]##)' '[^[:blank:]]##' '(*) ]]; then + jl+=("${match[1]}:${match[2]}") + fi + done + _describe -t jobs 'Perforce job' jl -V jobs-unsorted +} + +(( $+functions[_perforce_jobviews] )) || +_perforce_jobviews() { + # Jobviews (see `p4 help jobview') are ways of interrogating the + # jobs/fixes database. It's basically either a set of strings, + # or a set of key=value pairs, or some combination, separated + # by various logical operators. The `=' could be a comparison, + # but we don't currently bother with that here; it's a bit cumbersome + # to complete. + local line type oldifs=$IFS IFS= key value slash=/ + local match mbegin mend + # This is simply to split out two space-delimited words a backreferences. + local m2words + m2words='(#b)[[:blank:]]##([[:alnum:]]##)[[:blank:]]##([^[:blank:]]##)' + + local -a valuespec + local -A p4fields p4values + + # All the characters which can separate multiple match attempts. + # Ignore up to the last one. We don't try to complete these. + compset -P '*[[:blank:]\^\&\|\(\)]' + + # According to the manual, `p4 jobspec' requires admin privileges. + # If this is true even of `p4 jobspec -o', we are a bit screwed. + _perforce_call_p4 jobspec jobspec -o 2>/dev/null | while read line; do + case $line in + ([[:blank:]]##) + type= + ;; + + ((#b)([[:alpha:]]##):*) + type=${match[1]} + ;; + + (*) + case $type in + # This stanza tells us all the allowed fields. + (Fields) + if [[ $line = [[:blank:]]##<->${~m2words}* ]]; then + p4fields[${(L)match[1]}]=${match[2]} + fi + ;; + + # This stanza gives allowed values for the `select' types. + (Values) + if [[ $line = ${~m2words}* ]]; then + p4values[${(L)match[1]}]=${match[2]} + fi + ;; + esac + + ;; + esac + done + + IFS=$oldifs + + if (( ! ${#p4fields} )); then + # We didn't get anything; add the defaults. + p4fields=( + date date + description text + job word + status select + user word + ) + p4values=( + status open/suspended/closed + ) + fi + + for key in ${(k)p4fields}; do + if [[ -n ${p4values[$key]} ]]; then + valuespec+=("${key}:${p4fields[$key]}:(${p4values[$key]//$slash/ })") + elif [[ $key = job ]]; then + # Nothing special for jobs; add our own completion. + valuespec+=("${key}:Perforce job:_perforce_jobs") + elif [[ $key = user ]]; then + # Nothing provided for user; add our own completion. + valuespec+=("${key}:user:_perforce_users") + else + valuespec+=("${key}:${p4fields[$key]}: ") + fi + done + + _values 'Job specification parameter' $valuespec +} + +(( $+functions[_perforce_labels] )) || +_perforce_labels() { + local lline file + local -a ll match mbegin mend + + if [[ $argv[-1] = -tf ]]; then + argv=($argv[1,-2]) + # Completing after `@'. + file=${${(Q)PREFIX}%%@*} + compset -P '*@' + fi + + ll=(${${(f)"$(_perforce_call_p4 labels labels ${file:+\$file})"}//(#b)Label\ ([^[:blank:]]##)\ (*)/$match[1]:$match[2]}) + _describe -t labels 'Perforce label' ll +} + + +(( $+functions[_perforce_revisions] )) || +_perforce_revisions() { + # Doesn't handle standard completion options; requires space + # in front if used as action in _arguments. + + local rline match mbegin mend comma expl pfx + local -a rl + + if [[ $1 = -tR ]]; then + # handle ranges + comma=(-S, -R _perforce_file_suffix) + shift + fi + + # Beware of @foo,#bar; stupid but valid. + pfx=${${(Q)PREFIX}%%[\#@]*} + compset -P '*\#(|\\\<|\\\>)(|=)' + + # Numerical revision numbers, possibly with text. + if [[ -z $PREFIX || $PREFIX = <-> ]]; then + # always allowed (same as none) + rl+=(0) + _perforce_call_p4 filelog 'filelog $pfx' 2>/dev/null | while read rline; do + if [[ $rline = (#b)'... #'(<->)' change '(*) ]]; then + rl+=("${match[1]}:${match[2]}") + fi + done + fi + # Non-numerical (special) revision names. + if [[ -z $PREFIX || $PREFIX != <-> ]]; then + rl+=('head:head revision' 'none:empty revision' + 'have:current synced revision') + fi + _describe -t revisions 'revision' rl -V revisions-unsorted $comma +} + + +(( $+functions[_perforce_statuses] )) || +_perforce_statuses() { + # Perforce statuses are usually limited to a set of values + # given by the jobspec. + local jline match mbegin mend + local -a statuses + + _perforce_call_p4 jobspec jobspec -o | while read jline; do + if [[ $jline = (#b)Status[[:blank:]]##(*/*) ]]; then + statuses=(${(s./.)match[1]}) + break + fi + done + if (( !${#statuses} )); then + # Couldn't find anything from the jobspec; add defaults. + statuses=(closed open suspended) + fi + _describe -t statuses 'job status' statuses +} + + +(( $+functions[_perforce_submit_options] )) || +_perforce_submit_options() { + local -a soptions + soptions=('submitunchanged:submit all open files (default)' + 'revertunchanged:revert unchanged files' + 'leaveunchanged:move unchanged files to default changelist') + soptions+=(${^${soptions//:/+reopen:}}", leave submitted open") + _describe -t submit-options 'submit option' soptions +} + + +(( $+functions[_perforce_pids] )) || +_perforce_pids() { + local -a ul + + ul=(${${${(f)"$(_perforce_call_p4 monitor monitor show 2>/dev/null)"}# *}/\ /:}) + [[ $#ul -eq 1 && $ul[1] = '' ]] && ul=() + _describe -t id 'process ID' ul +} + + +(( $+functions[_perforce_users] )) || +_perforce_users() { + local -a ul + + ul=(${${(f)"$(_perforce_call_p4 users users)"}/\ /:}) + [[ $#ul -eq 1 && $ul[1] = '' ]] && ul=() + _describe -t users 'Perforce user' ul +} + + +(( $+functions[_perforce_users_or_groups] )) || +_perforce_users_or_groups() { + _alternative 'groups:Perforce group:_perforce_groups' \ + 'users:Perforce user:_perforce_users' +} + +(( $+functions[_perforce_variables] )) || +_perforce_variables() { + local line match mbegin mend expl + local -a vars + + _perforce_call_p4 help-environment help environment | while IFS= read line + do + if [[ $line = $'\t'(#b)([A-Z][A-Z0-9_]##)* ]]; then + vars+=($match[1]) + fi + done + + _wanted variable expl 'environment variable' compadd -S= -q $vars +} + +# +# Completions for p4 commands +# + +(( $+functions[_perforce_cmd_add] )) || +_perforce_cmd_add() { + _arguments -s : \ + '-c+[select by change]:change:_perforce_changes -tc' \ + '-f[allow filenames with wild cards]' \ + '-n[display operation without doing it]' \ + '-t+[set file type]:file type:_perforce_filetypes' \ + '*:file:_perforce_files -tu' +} + + +(( $+functions[_perforce_cmd_admin] )) || +_perforce_cmd_admin() { + if (( CURRENT == 2 )); then + local -a adcmds + adcmds=( + "checkpoint:checkpoint, save copy of journal file" + "journal:save and truncate journal file" + "stop:stop the server") + _describe -t commands 'Perforce admin command' adcmds + elif [[ $words[2] == (checkpoint|journal) ]]; then + shift words + (( CURRENT-- )) + _arguments -s : \ + '-z[gzip journal file]' \ + '1::journal file prefix: ' + fi +} + + +(( $+functions[_perforce_cmd_annotate] )) || +_perforce_cmd_annotate() { + # New in release 2002.2. + # -c was new in about 2003.2. + _arguments -s : \ + '-a[all, show both added and deleted lines]' \ + '-c[output change numbers instead of revisions]' \ + '-i[follow branches (integration records)]' \ + '-q[quiet, suppress one-line file header]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_attribute] )) || +_perforce_cmd_attribute() { + # This is currently (2005.1) an unsupported command. + # See "p4 help undoc". + local limit + # If -f is present, search unopened files, else don't + [[ ${words[(I)-f]} -eq 0 ]] && limit=" -to" + _arguments -s : \ + '-e[value is in hex]' \ + '-f[set the attribute on a submitted file]' \ + '-n[set name of attribute]:attribute: ' \ + '-p[propagate attribute when opened for edit etc.]' \ + '-v[set value of attribute]:value: ' \ + "*::file:_perforce_files$limit" +} + + +(( $+functions[_perforce_cmd_branch] )) || +_perforce_cmd_branch() { + _arguments -s : \ + '(-o)-f[force operation by superuser]' \ + '(-o -i)-d[delete branch]' \ + '(-d -i -f)-o[write specification to standard output]' \ + '(-d -o)-i[read specification from standard input]' \ + '(-i)*::branch name:_perforce_branches' +} + + +(( $+functions[_perforce_cmd_branches] )) || +_perforce_cmd_branches() { + _arguments -s : \ + '-u+[select by user]:user:_perforce_users' \ + '-m+[set maximum to show]:max branches: ' +} + + +(( $+functions[_perforce_cmd_change] )) || +_perforce_cmd_change() { + local ctype + # Unless forcing or outputting, we don't + # complete committed changes since they can't be altered. + # If deleting and not forcing, the change must be on the current client. + if [[ ${words[(I)-*(f|o)*]} -eq 0 ]]; then + if [[ ${words[(I)-d]} -gt 0 ]]; then + ctype=" -tc" + else + ctype=" -tp" + fi + fi + _arguments -s : \ + '(-o)-f[allow force by superuser]' \ + '-s[joblist includes the fix status]' \ + '(-o -i)-d[discard newly created pending change]' \ + '(-d -i -f)-o[output specification to standard output]' \ + '(-d -o)-i[read specification from standard input]' \ + "(-i)1::change:_perforce_changes$ctype" +} + + +(( $+functions[_perforce_cmd_changelist] )) || +_perforce_cmd_changelist() { + _perforce_cmd_change "$@" +} + + +(( $+functions[_perforce_cmd_changes] )) || +_perforce_cmd_changes() { + _arguments -s : \ + '-i[include integrated changes]' \ + '-t[output time as well as date]' \ + '-l[long output, full change text]' \ + '-L[long output, truncated change text]' \ + '-c+[select by client]:client:_perforce_clients' \ + '-m+[most recent N changes]:max changes: ' \ + '-s+[select by status]:status:(pending submitted)' \ + '-u+[select by user]:user:_perforce_users' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_changelists] )) || +_perforce_cmd_changelists() { + _perforce_cmd_changes "$@" +} + + +(( $+functions[_perforce_cmd_client] )) || +_perforce_cmd_client() { + _arguments -s : \ + '(-o)-f[force modification by superuser]' \ + '-t[use template]:template client:_perforce_clients' \ + '(-o -i -t)-d[delete client]' \ + '(-d -i -f)-o[print to standard output]' \ + '(-d -o -t)-i[read from standard input]' \ + '1::file:_perforce_clients' +} + + +(( $+functions[_perforce_cmd_clients] )) || +_perforce_cmd_clients() { + _arguments -s : \ + '-u+[select by user]:user:_perforce_users' \ + '-m+[set maximum to show]:max clients: ' +} + + +(( $+functions[_perforce_cmd_counter] )) || +_perforce_cmd_counter() { + _arguments -s : \ + '-d[delete counter]' \ + '-f[force setting of internal counter]' \ + '1:counter:_perforce_counters' \ + '(-d)2::numeric value:_perforce_counter_values' +} + + +(( $+functions[_perforce_cmd_counters] )) || +_perforce_cmd_counters() { + # No arguments + _arguments -s : +} + + +(( $+functions[_perforce_cmd_delete] )) || +_perforce_cmd_delete() { + _arguments -s : \ + '-c[select change for deletion]:change:_perforce_changes -tc' \ + '-n[show deletions without doing them]' \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_depot] )) || +_perforce_cmd_depot() { + _arguments -s : \ + '-d[delete depot]' \ + '-o[print to stdout]' \ + '-i[read name from stdin]' \ + '(-i)*::depot name:_perforce_depots' +} + + +(( $+functions[_perforce_cmd_depots] )) || +_perforce_cmd_depots() { + # No arguments + _arguments -s : +} + + +(( $+functions[_perforce_cmd_describe] )) || +_perforce_cmd_describe() { + _arguments -s : \ + '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \ + '-s[short form]' \ + '*::change:_perforce_changes' +} + + +(( $+functions[_perforce_cmd_diff] )) || +_perforce_cmd_diff() { + local limit + [[ ${words[(I)-(f|sd|se)]} -eq 0 ]] && limit=" -to" + _arguments -s : \ + '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context l\:ignore\ line\ endings n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \ + '-f[diff every file]' \ + '-m+[set maximum files to show]:max files: ' \ + '(-sd -se -sl -sr)-sa[opened files, different or missing]' \ + '(-sa -se -sl -sr)-sd[unopened files, missing]' \ + '(-sa -sd -sl -sr)-se[unopened files, different]' \ + '(-sa -sd -se -sr)-sl[all unopened files with status]' \ + '(-sa -sd -se -sl)-sr[opened files, same as depot]' \ + '-t[include non-text files]' \ + "*::file:_perforce_files$limit" +} + + +(( $+functions[_perforce_cmd_diff2] )) || +_perforce_cmd_diff2() { + _arguments -s : \ + '-b[specify branch view]:branch name:_perforce_branches' \ + '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \ + '-q[only list different files]' \ + '-t[include non-text files]' \ + '-u[use patch-friendly output]' \ + '1::first file:_perforce_files' \ + '2::second file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_dirs] )) || +_perforce_cmd_dirs() { + _arguments -s : \ + '-C[only dirs on current client]' \ + '-D[include dirs with deleted files]' \ + '-H[only dirs on the `have'\'' list]' \ + '*::directory:_perforce_files -td' +} + + +(( $+functions[_perforce_cmd_edit] )) || +_perforce_cmd_edit() { + _arguments -s : \ + '-c[set change for edit]:change:_perforce_changes -tc' \ + '-n[show files to edit without opening them]' \ + '-t[set filetype]:filetype:_perforce_filetypes' \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_filelog] )) || +_perforce_cmd_filelog() { + _arguments -s : \ + '-i[follow branches]' \ + '-l[long output, full change text]' \ + '-L[long output, truncated change text]' \ + '-m[set maximum number of revisions to show]:max revisions: ' \ + '-t[include time with date]' \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_files] )) || +_perforce_cmd_files() { + _arguments -s : \ + '-a[display all revisions in given range]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_fix] )) || +_perforce_cmd_fix() { + local job + local -a jobs + + if [[ -n $words[(R)-d] && -n $words[(R)-c] && -n $words[$words[(i)-c]+1] ]] + then + # Deleting a fix from a change. We can find out which fixes + # are present. + local -a jobs + jobs=(${${(f)"$(_perforce_call_p4 fixes fixes -c $words[$words[(i)-c]+1])"}%" fixed by change "*}) + if (( ${#jobs} )); then + jobs=("Job="${^jobs}) + job=" -e \"${(j.|.)jobs}\"" + fi + fi + + _arguments -s : \ + '-d[delete the fix]' \ + '-s[set job status]:status:_perforce_statuses' \ + '1::-c required:(-c)' \ + '2::change:_perforce_changes' \ + "3::job:_perforce_jobs$job" +} + + +(( $+functions[_perforce_cmd_fixes] )) || +_perforce_cmd_fixes() { + _arguments -s : \ + '-i[include integrated changes]' \ + '-j[select by job]:job:_perforce_jobs' \ + '-c[select by change]:change:_perforce_changes' \ + '-m[set max fixes to show]:max fixes: ' \ + '*::fixed file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_flush] )) || +_perforce_cmd_flush() { + _arguments -s : \ + '-f[force resynchronisation]' \ + '-k[bypass client file update]' \ + '-n[show operations but don'\''t perform them]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_fstat] )) || +_perforce_cmd_fstat() { + local Oattr Aattr + if [[ ${_perforce_cmd_list[(r)attribute:*]} != '' ]]; then + # Unsupported feature, try not to show if not present + Oattr=' a\:show\ attributes d\:attributes\ digest e\:attributes\ in\ hex' + Aattr='-A[restrict attributes by pattern]:attribute pattern: ' + fi + _arguments -s : \ + '-c+[affected since change]:change:_perforce_changes -ts' \ + '-e+[affected by change]:change:_perforce_changes -ts' \ + '-C[select mapped files (-Rc)]' \ + '-H[select synced files (-Rh)]' \ + '-W[select opened files (-Ro)]' \ + '-l[include fileSize, possibly slow (-Ol)]' \ + '-m[set max files to show]:max files: ' \ + "-O-[select output type]:output type:((f\:all\ revisions l\:fileSize p\:client\ path\ format r\:pending\ integrations s\:exclude\ local\ path$Oattr))" \ + '-P[output clientFile in full Perforce syntax (deprecated: use -Op)]' \ + '-R-[restrict selected files]:restriction:((c\:mapped\ in\ client h\:synced\ to\ client n\:not\ synced\ to\ head o\:opened r\:resolved u\:unresolved))' \ + '-s[shorten, no client-related data (deprecated: use -Os)]' \ + $Aattr \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_group] )) || +_perforce_cmd_group() { + _arguments -s : \ + '-d[delete group]' \ + '-o[output to stdout]' \ + '-i[read from stdin]' \ + '1::perforce group:_perforce_groups' +} + + +(( $+functions[_perforce_cmd_groups] )) || +_perforce_cmd_groups() { + _arguments -s : \ + '-i[show indirect membership by subgroups]' \ + '-m[set max groups to show]:max groups: ' \ + '1::user or group name:_perforce_users_or_groups' +} + + +(( $+functions[_perforce_cmd_have] )) || +_perforce_cmd_have() { + _perforce_files +} + + +(( $+functions[_perforce_cmd_help] )) || +_perforce_cmd_help() { + local hline + if (( ! ${#_perforce_help_list} )); then + (( ${+_perforce_help_list} )) || typeset -ga _perforce_help_list + # All commands have help. + (( ${#_perforce_cmd_list} )) || _perforce_gen_cmd_list + _perforce_help_list=($_perforce_cmd_list) + _perforce_call_p4 help help | while read -A hline; do + if [[ $hline[1] = p4 && $hline[2] = help ]]; then + _perforce_help_list+=("$hline[3]:${hline[4,-1]}") + fi + done + if [[ -z ${_perforce_help_list[(r)undoc:*]} ]]; then + _perforce_help_list+=("undoc:Help for otherwise undocumented features") + fi + fi + _describe -t help-options 'Perforce help option' _perforce_help_list +} + + +(( $+functions[_perforce_cmd_info] )) || +_perforce_cmd_info() { + # No arguments + _arguments -s : +} + + +(( $+functions[_perforce_cmd_integrate] )) || +_perforce_cmd_integrate() { + local range + # If -s is present, the first normal argument can't have revRange. + [[ ${words[(I)-s]} -eq 0 ]] && range=" -tR" + _arguments -s : \ + '-b[select branch]:branch:_perforce_branches' \ + '-c[select change for integration]:change:_perforce_changes -tc' \ + '-f[force reintegration]' \ + '-d[reintegrate deleted files]' \ + '-D-[specify allowed deletions]:deletion type:(( +t\:rebranch\ on\ deleted\ file +s\:delete\ modified\ target\ file +i\:ignore\ readded\ source\ file +))' \ + '-h[integrate to revision had on client]' \ + '-i[integrate if no common file base]' \ + '-I[same as -i from 2004.2]' \ + '-n[no action, dummy run]' \ + '-o[display base file name for subsequent resolve]' \ + '-r[reverse direction of integration with branch]' \ + '-s[select source with -b]:source file:_perforce_files -tR' \ + '-t[propagate type changes]' \ + '-v[leave newly branched files uncopied till sync]' \ + "1:file:_perforce_files$range" \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_integrated] )) || +_perforce_cmd_integrated() { + _arguments -s : \ + '-r[reverse mapping in branch view with -b]' \ + '-b[select files integrated via branch]:branch:_perforce_branches' \ + '*::file:_perforce_files -ti' +} + + +# interchanges is an unsupported but useful command that reports +# changes that haven't been integrated between source and target; +# see "p4 help undoc". +(( $+functions[_perforce_cmd_interchanges] )) || +_perforce_cmd_interchanges() { + local -a fileargs + if [[ ${words[(I)-b*]} -ne 0 ]]; then + if [[ ${words[(I)-*s*]} -eq 0 ]]; then + # with -b and no -s, all files are to-files (but -s may come later) + fileargs=('-s[specify source file]' + '*::to file:_perforce_files -tR') + else + # with -b and -s we have one from-file and any number of to-files + fileargs=('1::from file:_perforce_files -tR' + '*::to file:_perforce_files') + fi + else + # with no -b we have one from-file and one to-file + fileargs=('1::from file:_perforce_files -tR' + '2::to file:_perforce_files') + fi + _arguments -s : \ + '-f[show individual files]' \ + '-l[long changelist description]' \ + '-b[select files integrated via branch]:branch:_perforce_branches' \ + '-r[reverse branch mapping]' \ + $fileargs +} + + +(( $+functions[_perforce_cmd_job] )) || +_perforce_cmd_job() { + _arguments -s : \ + '(-d -o -i)-f[force setting of readonly fields]' \ + '(-f -o -i)-d[delete job]' \ + '(-f -d -i)-o[print to stdout]' \ + '(-d -o)-i[read from stdin]' \ + '(-i)1::job:_perforce_jobs' +} + + +(( $+functions[_perforce_cmd_jobs] )) || +_perforce_cmd_jobs() { + _arguments -s : \ + '-e[select by jobview]:jobview:_perforce_jobviews' \ + '-i[included integrated changes]' \ + '-l[long output, full job descriptions]' \ + '-r[reverse order of job names]' \ + '-m[limit to most recent N jobs]:number of most recent jobs: ' \ + '(-e -i -l -m)-R[rebuild jobs table on upgrade]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_jobspec] )) || +_perforce_cmd_jobspec() { + _arguments -s : \ + '-i[read form from stdin]' \ + '-o[write form from to stdout]' +} + + +(( $+functions[_perforce_cmd_label] )) || +_perforce_cmd_label() { + _arguments -s : \ + '-f[force operation]' \ + '-t+[copy template]:template: ' \ + '(-o -i -t)-d[delete label]' \ + '(-d -f -i)-o[write to standard output]' \ + '(-o -d -t)-i[read from standard input]' \ + '*::label:_perforce_labels' +} + + +(( $+functions[_perforce_cmd_labels] )) || +_perforce_cmd_labels() { + _arguments -s : \ + '-m+[set maximum to show]:max labels: ' \ + '-u+[select by user]:user:_perforce_users' \ + '1::file or revisions which must contain label:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_labelsync] )) || +_perforce_cmd_labelsync() { + _arguments -s : \ + '-a[add files to label]' \ + '-d[delete files from label]' \ + '-n[no effect, dummy run]' \ + '-l[specify label]:label:_perforce_labels' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_lock] )) || +_perforce_cmd_lock() { + _arguments -s : \ + '-c[select by change]:change:_perforce_changes -tc' \ + '*::file:_perforce_files -to' +} + + +(( $+functions[_perforce_cmd_logger] )) || +_perforce_cmd_logger() { + _arguments -s : \ + '-c[limit by counter no]:number: ' \ + '-t[use counter instead of logger]:counter:_perforce_counters' +} + + + +(( $+functions[_perforce_cmd_login] )) || +_perforce_cmd_login() { + _arguments -s : \ + '-a[ticket valid on all machines]' \ + '-p[display ticket, do not store]' \ + '-s[show status of ticket]' \ + '(-s)1::user:_perforce_users' +} + + +(( $+functions[_perforce_cmd_logout] )) || +_perforce_cmd_logout() { + _arguments -s : \ + '-a[invalidate ticket on server]' +} + + +(( $+functions[_perforce_cmd_monitor] )) || +_perforce_cmd_monitor() { + if (( CURRENT > 2 )); then + case $words[2] in + (show) + shift words + (( CURRENT-- )) + _arguments -s : \ + '-a[show command arguments]' \ + '-e[show command environment]' \ + '-l[long output format]' + ;; + + (terminate) + _perforce_pids + ;; + + (clear) + _alternative 'pids:pid:_perforce_pids' 'all:all processes:(all)' + ;; + + (*) + _message "no such monitor command: $words[1]" + ;; + esac + else + local expl + _wanted monitor-command expl 'monitor command' compadd show terminate clear + fi +} + + +(( $+functions[_perforce_cmd_obliterate] )) || +_perforce_cmd_obliterate() { + if [[ ${words[(I)-y]} -gt 0 ]]; then + _message \ +": don't complete after -y; run obliterate without, then add the -y" + else + _arguments -s : \ + '-y[actually perform the operation]' \ + '*::file:_perforce_files -tR' + fi +} + + +(( $+functions[_perforce_cmd_opened] )) || +_perforce_cmd_opened() { + # You might think you could check for files opened on another + # client, and hence the -c completion should have the argument + # -tp, but currently Perforce doesn't allow that, so -tc is correct. + # This is true even if -a is also given. + _arguments -s : \ + '-a[list for all clients]' \ + '-c+[select by change]:change:_perforce_changes -tc' \ + '-C[select by client]:client:_perforce_clients' \ + '-m[max files to show]:max files: ' \ + '*::file:_perforce_files -to' +} + + +(( $+functions[_perforce_cmd_passwd] )) || +_perforce_cmd_passwd() { + _arguments -s : \ + '-O[explicit old password]:old password: ' \ + '-P[explicit new password]:new password: ' \ + '1::user name:_perforce_users' +} + + +(( $+functions[_perforce_cmd_print] )) || +_perforce_cmd_print() { + _arguments -s : \ + '-a[display all revisions in a range]' \ + '-o[select output file]:output file:_files' \ + '-q[suppress header]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_protect] )) || +_perforce_cmd_protect() { + _arguments -s : \ + '-o[write spec to stdout]' \ + '-i[read spec from stdin]' +} + + +(( $+functions[_perforce_cmd_reopen] )) || +_perforce_cmd_reopen() { + _arguments -s : \ + '-c+[select change to reopen on]:change:_perforce_changes -tc' \ + '-t+[set file type]:file type:_perforce_filetypes' \ + '*::file:_perforce_files -to' +} + + +(( $+functions[_perforce_cmd_resolve] )) || +_perforce_cmd_resolve() { + _arguments -s : \ + '-a-[select automatic merge type]:automation type:((f\:force\ acceptance m\:skip\ conflicts s\:safe t\:use\ theirs y\:use\ yours))' \ + '-d-[select diff option]:diff option:((b\:ignore\ blanks w\:ignore\ all\ whitespace))' \ + '-f[force re-resolution]' \ + '-n[no action, just list]' \ + '-o[display base file name and revision for merge]' \ + '-t[force textual merge on binary files]' \ + '-v[verbose, mark all changes]' \ + '*::file:_perforce_files -to' +} + + +(( $+functions[_perforce_cmd_resolved] )) || +_perforce_cmd_resolved() { + _arguments -s : \ + '-o[report revision used as base for resolve]' \ + '*::file:_perforce_files -tr' +} + + +(( $+functions[_perforce_cmd_revert] )) || +_perforce_cmd_revert() { + _arguments -s : \ + '-a[revert unaltered files]' \ + '-c[limit reversions to change]:change:_perforce_changes -tc' \ + '-k[bypass client refresh]' \ + '-n[no action, show effect only]' \ + '*::file:_perforce_files -to' +} + + +(( $+functions[_perforce_cmd_review] )) || +_perforce_cmd_review() { + _arguments -s : \ + '-c[select change for counter]:change:_perforce_changes -ts' \ + '-t[limit change number by counter]:counter:_perforce_counters' +} + + +(( $+functions[_perforce_cmd_reviews] )) || +_perforce_cmd_reviews() { + _arguments -s : \ + '-c[show users by change]:change:_perforce_changes -ts' \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_set] )) || +_perforce_cmd_set() { + # Only works under Windoze but maybe we are on Cygwin. + _arguments -s : \ + '-s[set for whole system]' \ + '-S[set for specified service]:service: ' \ + "*::environment variable:_perforce_variables" +} + + +(( $+functions[_perforce_cmd_spec] )) || +_perforce_cmd_spec() { + _arguments -s : \ + '-d[delete a custom spec]' \ + '-i[read spec from stdin]' \ + '-o[write spec to stdout]' \ + "*::spec type:(branch change client depot group job +label spec trigger typemap user)" +} + + +(( $+functions[_perforce_cmd_submit] )) || +_perforce_cmd_submit() { + _arguments -s : \ + '-r[files open for add or edit remain open]' \ + '-s[include fix status in list]' \ + '-f[override submit option]:submit option:_perforce_submit_options' \ + '(-s -i)-c[submit specific change]:change:_perforce_changes -tc' \ + '(-s -c)-d[specify description on command line]:description: ' \ + '(-c)-i[read change spec from stdin]' \ + '*::file:_perforce_files -to -tr' +} + + +(( $+functions[_perforce_cmd_sync] )) || +_perforce_cmd_sync() { + _arguments -s : \ + '-f[force resynchronisation]' \ + '-n[show operations but don'\''t perform them]' \ + '-k[bypass client file update]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_tag] )) || +_perforce_cmd_tag() { + _arguments -s : \ + '-d[delete association between label and files]' \ + '-n[show what files would be tagged]' \ + '-l[specify label]:label:_perforce_labels' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_triggers] )) || +_perforce_cmd_triggers() { + _arguments -s : \ + '-o[output form to stdout]' \ + '-i[read from from stdin]' +} + + +(( $+functions[_perforce_cmd_tickets] )) || +_perforce_cmd_tickets() { + # No arguments. + _arguments -s : +} + + +(( $+functions[_perforce_cmd_typemap] )) || +_perforce_cmd_typemap() { + _arguments -s : \ + '-o[output table to stdout]' \ + '-i[read table from stdin]' +} + + +(( $+functions[_perforce_cmd_unlock] )) || +_perforce_cmd_unlock() { + _arguments -s : \ + '-c[non-default change to unlock]:change:_perforce_changes -tc' \ + '-f[allow superuser to unlock any file]' \ + '*::file:_perforce_files' +} + + +(( $+functions[_perforce_cmd_user] )) || +_perforce_cmd_user() { + _arguments -s : \ + '(-o)-f[force edit by superuser]' \ + '(-o -i)-d[delete user]' \ + '(-o -d)-i[read form from stdin]' \ + '(-f -i -d)-o[write form to stdout]' \ + '(-i)1::username:_perforce_users' +} + + +(( $+functions[_perforce_cmd_users] )) || +_perforce_cmd_users() { + _arguments -s : \ + '-m[set max users to show]:max users: ' \ + '*::username:_perforce_users' +} + + +(( $+functions[_perforce_cmd_verify] )) || +_perforce_cmd_verify() { + _arguments -s : \ + '-m[limit revisions]:max revisions: ' \ + '-q[operate quietly]' \ + '-u[compute and save digest if missing]' \ + '-v[compute and save all digets]' \ + '*::file:_perforce_files -tR' +} + + +(( $+functions[_perforce_cmd_where] )) || +_perforce_cmd_where() { + _perforce_files +} + + +(( $+functions[_perforce_cmd_workspace] )) || +_perforce_cmd_workspace() { + _perforce_cmd_client "$@" +} + + +(( $+functions[_perforce_cmd_workspaces] )) || +_perforce_cmd_workspaces() { + _perforce_cmd_clients "$@" +} + + +_perforce "$@" -- cgit 1.4.1