Fix as many as possible checkdoc issues

This commit is contained in:
Tom Willemse 2021-02-27 14:09:44 -08:00
parent 48237437e8
commit dcb212c410
2 changed files with 248 additions and 166 deletions

View file

@ -54,13 +54,15 @@
(defcustom p4-lowlevel-command-messages nil
"*If non-nil, display run messages from P4 commands.
If vc-command-messages is bound and non-nil, it does the same thing."
If vc-command-messages is bound and non-nil, it does the same
thing."
:type 'boolean
:group 'p4-lowlevel)
(defcustom p4-lowlevel-ignore-error-list '("file(s) up-to-date")
"*A list of Perforce errors to ignore. Note that each item in the list can be
a portion of the error string you wish to ignore."
"*A list of Perforce errors to ignore.
Note that each item in the list can be a portion of the error
string you wish to ignore."
:type '(repeat (string :tag "Error to Ignore"))
:group 'p4-lowlevel)
@ -72,8 +74,9 @@ a portion of the error string you wish to ignore."
'p4-error)
(defun p4-lowlevel-locate-p4 ()
"Attempts to locate the p4 command. Used by `vc-p4-registered' to
avoid an error on systems on which the Perforce client is not installed."
"Attempts to locate the p4 command.
Used by `vc-p4-registered' to avoid an error on systems on which
the Perforce client is not installed."
(if (and (boundp 'exec-suffixes) (fboundp 'file-executable-p))
(locate-file p4-lowlevel-p4-program exec-path exec-suffixes 'file-executable-p) ; GNU Emacs
(locate-file p4-lowlevel-p4-program exec-path '("" ".btm" ".bat" ".cmd" ".exe" ".com") 'executable))) ; XEmacs
@ -150,15 +153,16 @@ Returns the buffer containing the program output."
output-buffer)))
(defun p4-lowlevel-buffer-to-alist (&optional buffer)
"Converts the current buffer containing p4 output to an alist. If
optional argument BUFFER is non-nil, converts that buffer instead.
The returned alist contains one element for each `unit' in the buffer.
A `unit' is either a line containing a tag (`info:' or `info#:',
`error:', `exit:', etc.) or a block of untagged text. The car of each
element is the tag for the line (i.e., the string to the left of the
colon, or `text' for text units), and the cdr is the value for the
line (i.e., everything after the first `: ' sequence) or the entire
block of untagged text (including newlines other than the last one).
"Convert the current buffer containing p4 output to an alist.
If optional argument BUFFER is non-nil, converts that buffer
instead. The returned alist contains one element for each `unit'
in the buffer. A `unit' is either a line containing a
tag (`info:' or `info#:', `error:', `exit:', etc.) or a block of
text with no tag. The car of each element is the tag for the
line (i.e., the string to the left of the colon, or `text' for
text units), and the cdr is the value for the line (i.e.,
everything after the first `: ' sequence) or the entire block of
text without a tag (including newlines other than the last one).
The alist is in the same order as the contents of the buffer."
(save-excursion
(if buffer (set-buffer buffer))
@ -187,12 +191,13 @@ The alist is in the same order as the contents of the buffer."
(nreverse alist))))
(defun p4-lowlevel-command-to-alist (args &optional input)
"Calls `p4-lowlevel-command-to-buffer' and then
`p4-lowlevel-buffer-to-alist'. Passes ARGS and optional INPUT to
`p4-lowlevel-command-to-buffer'. Hands the resulting buffer to
`p4-lowlevel-buffer-to-alist' for parsing. Kills the output buffer
when it has been parsed. Returns the resulting alist on success, or
the return value of `p4-lowlevel-command-to-buffer' on failure."
"Call `p4-lowlevel-command-to-buffer' and then `p4-lowlevel-buffer-to-alist'.
Passes ARGS and optional INPUT to
`p4-lowlevel-command-to-buffer'. Hands the resulting buffer to
`p4-lowlevel-buffer-to-alist' for parsing. Kills the output
buffer when it has been parsed. Returns the resulting alist on
success, or the return value of `p4-lowlevel-command-to-buffer'
on failure."
(let ((output-buffer (p4-lowlevel-command-to-buffer args input))
return-value)
(setq return-value (p4-lowlevel-buffer-to-alist output-buffer))
@ -241,16 +246,17 @@ value of 0."
t)))))
(defun p4-lowlevel-items-matching-tag (tag output)
"Returns a list of the items maching TAG in p4 OUTPUT, or nil if none.
"Return a list of the items matching TAG in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist."
(if (bufferp output)
(setq output (p4-lowlevel-buffer-to-alist output)))
(mapcar (lambda (pair) (cdr pair)) (p4-lowlevel-re-assoc tag output)))
(defun p4-lowlevel-lines-matching-tag (tag output)
"Returns a string containing the lines matching TAG in p4 OUTPUT, or
nil if none. OUTPUT may be a buffer or alist. The lines are
terminated by newlines. The tags are not included in the string."
"Return a string containing the lines matching TAG in p4 OUTPUT.
Return nil if none of the lines match TAG. OUTPUT may be a buffer
or alist. The lines are terminated by newlines. The tags are not
included in the string."
(if (bufferp output)
(setq output (p4-lowlevel-buffer-to-alist output)))
(let* ((alist (p4-lowlevel-re-assoc tag output))
@ -263,8 +269,8 @@ terminated by newlines. The tags are not included in the string."
lines)))
(defun p4-lowlevel-errors (output)
"Returns a string containing the errors in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The error lines are separated by
"Return a string containing the errors in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The error lines are separated by
newlines, but there is no ending newline on the string."
(let ((errors (p4-lowlevel-lines-matching-tag "^error" output)))
(if errors
@ -272,26 +278,26 @@ newlines, but there is no ending newline on the string."
nil)))
(defun p4-lowlevel-info-lines (output)
"Returns a string containing the info in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The info lines are terminated by
"Return a string containing the info in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The info lines are terminated by
newlines."
(p4-lowlevel-lines-matching-tag "^info" output))
(defun p4-lowlevel-text (output)
"Returns a string containing the text in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The text lines are terminated by
"Return a string containing the text in p4 OUTPUT, or nil if none.
OUTPUT may be a buffer or alist. The text lines are terminated by
newlines."
(p4-lowlevel-lines-matching-tag "^text" output))
(defun p4-lowlevel-command-or-error (args &optional input output-format noerror)
"Executes p4 command specified by ARGS and returns output or signals error.
Pass optional argument INPUT to `p4-lowlevel-command-to-buffer'. If optional
argument OUTPUT-FORMAT is \'string, return a string containing the
output (including tags). If it is \'buffer, return the temporary
buffer containing the output. If it is a buffer, put output in that
buffer and return it. If it is anything else, return an alist of the
output. If optional fourth argument NOERROR is true, then returns nil
rather than raising an error."
"Execute p4 command specified by ARGS and return output or signal error.
Pass optional argument INPUT to `p4-lowlevel-command-to-buffer'.
If optional argument OUTPUT-FORMAT is \'string, return a string
containing the output (including tags). If it is \'buffer, return
the temporary buffer containing the output. If it is a buffer,
put output in that buffer and return it. If it is anything else,
return an alist of the output. If optional fourth argument
NOERROR is true, then returns nil rather than raising an error."
(let* (errors error-buffer return-value
(output-buffer (p4-lowlevel-command-to-buffer args input
(if (bufferp output-format)
@ -327,11 +333,12 @@ rather than raising an error."
return-value))
(defun p4-lowlevel-command-into-buffer (args buffer)
"Executes p4 command specified by ARGS, raising errors when necessary.
If BUFFER is a string, then puts output in buffer whose name is formed
by concatenating ` *p4-lowevel-', BUFFER-NAME, and `*' (e.g., if BUFFER is
`diff', then output goes in buffer ` *p4-lowevel-diff*'). If BUFFER is a
buffer, then puts output in that buffer. Returns the buffer."
"Execute p4 command specified by ARGS, raising errors when necessary.
If BUFFER is a string, then puts output in buffer whose name is
formed by concatenating ` *p4-lowevel-', BUFFER-NAME, and
`*' (e.g., if BUFFER is `diff', then output goes in buffer `
*p4-lowevel-diff*'). If BUFFER is a buffer, then puts output in
that buffer. Returns the buffer."
(let* ((output-alist (p4-lowlevel-command-or-error args))
(output-buffer (if (bufferp buffer) buffer
(p4-lowlevel-get-buffer-create
@ -344,15 +351,14 @@ buffer, then puts output in that buffer. Returns the buffer."
output-buffer)))
(defun p4-lowlevel-command-messages ()
"Return t if p4-lowlevel-command-messages or vc-command-messages is
bound and true."
"Return t if vc-command-messages bound and true.
Fall back to the value of p4-lowlevel-command-messages."
(if (and (boundp 'vc-command-messages) vc-command-messages)
t
p4-lowlevel-command-messages))
(defun p4-lowlevel-canonicalize-revision (rev)
"Turn REV into a form which can be concatenated to file names in P4
commands."
"Turn REV into a form which can be concatenated to file names in P4 commands."
;; There is some ambiguity here, since a number can be either a revision
;; number (#rev) or a change number (@change). We assume that a bare number is
;; a revision number.
@ -376,7 +382,8 @@ commands."
(cl-defun p4-lowlevel-add (file &key client)
"Tell Perforce to add FILE to the repository.
Returns nil or raises an error on failure."
Returns nil or raises an error on failure. If CLIENT is non-nil
it is passed along as the Perforce client to use."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "add" file))))
;; Note that because "p4 -s add" has bugs, at least as of p4 99.2, this won't
@ -385,8 +392,9 @@ Returns nil or raises an error on failure."
(p4-lowlevel-command-or-error args)))
(cl-defun p4-lowlevel-delete (file &key client)
"Tell Perforce to delet FILE from the repository.
Returns nil or raises an error on failure."
"Tell Perforce to delete FILE from the repository.
Returns nil or raises an error on failure. If CLIENT is non-nil
it is passed on as the Perforce client to use."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "delete" file))))
(p4-lowlevel-command-or-error args)))
@ -401,14 +409,15 @@ Returns nil or raises an error on failure."
;; DO need to support specified changelist #'s.
(cl-defun p4-lowlevel-change (&key buffer op client)
"Creates or edits a P4 changelist from/to BUFFER.
"Create or edit a P4 changelist from/to BUFFER.
If optional OP is a number, then the corresponding changelist is
retrieved into BUFFER, or into a new buffer if BUFFER is nil. If OP
is non-nil and not a number, then then BUFFER should contain an
existing changelist which is saved to the database; the number of the
new or updated changelist is returned. If OP is nil then a new
changelist is retrieved into BUFFER (or a new buffer). The output
buffer is returned."
retrieved into BUFFER, or into a new buffer if BUFFER is nil. If
OP is non-nil and not a number, then then BUFFER should contain
an existing changelist which is saved to the database; the number
of the new or updated changelist is returned. If OP is nil then a
new changelist is retrieved into BUFFER (or a new buffer). The
output buffer is returned. If CLIENT is non-nil it is passed on
as the Perforce client to use."
(let* ((input-buffer (if (and op (not (numberp op))) buffer nil))
(flag-arg (if (or (not op) (numberp op)) "-o" "-i"))
(number-arg (if (numberp op) (list (number-to-string op))))
@ -432,11 +441,14 @@ buffer is returned."
(cl-defun p4-lowlevel-changes
(file-pattern &key output-format rev1 rev2 i-flag l-flag m-val s-val client)
"Call `p4 changes' on FILE-PATTERN. Optional OUTPUT-FORMAT is as
described in `p4-lowlevel-command-or-error'. Optionally, limit output
to the revisions between REV1 and REV2. If I-FLAG is non-nil, pass
`-i'; if L-FLAG is non-nil, pass `-l'; if M-VAL is non-nil, pass that
value with `-m'; if S-VAL is non-nil, pass that value with `-s'."
"Call “p4 changes” command on FILE-PATTERN.
Optional OUTPUT-FORMAT is as described in
`p4-lowlevel-command-or-error'. Optionally, limit output to the
revisions between REV1 and REV2. If I-FLAG is non-nil, pass `-i';
if L-FLAG is non-nil, pass `-l'; if M-VAL is non-nil, pass that
value with `-m'; if S-VAL is non-nil, pass that value with `-s'.
If CLIENT is non-nil it is passed on as the Perforce client to
use."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2))
(let ((full-file
@ -468,12 +480,14 @@ value with `-m'; if S-VAL is non-nil, pass that value with `-s'."
;; Do NOT need to support diffing multiple files.
(cl-defun p4-lowlevel-diff (files &key rev buffer client)
"Run `p4 diff' on FILE at revision REV and return a buffer
containing the results. REV is in the syntax described by `p4 help
revisions'. If REV is nil, compare the client's sync'd revision to
the file on disk. Uses `p4-lowlevel-diff-switches' to determine flags
to pass to `p4 diff'. If optional BUFFER is non-nil, put output in
that buffer."
"Run `p4 diff' on FILES at revision REV.
Return a buffer containing the results. REV is in the syntax
described by `p4 help revisions'. If REV is nil, compare the
client's synced revision to the file on disk. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4
diff'. If optional BUFFER is non-nil, put output in that buffer.
If CLIENT is non-nil it is passed on as the Perforce client to
use."
(unless (listp files)
(setq files (list files)))
(setq rev (p4-lowlevel-canonicalize-revision rev))
@ -494,8 +508,9 @@ that buffer."
buffer))
(cl-defun p4-lowlevel-diff-s (file flag &key client)
"Run `p4 diff -s' on FILE, using FLAG as the argument to `-s', and
return a list of the matching files."
"Run `p4 diff -s' on FILE, using FLAG as the argument to `-s'.
Return a list of the matching files. If CLIENT is non-nil its
passed on as the Perforce client to use."
(p4-lowlevel-items-matching-tag
"^info"
(let* ((client-args (if client (list "-c" client)))
@ -511,11 +526,13 @@ return a list of the matching files."
;; Do NOT need to support "-b".
(cl-defun p4-lowlevel-diff2 (file1 file2 &key rev1 rev2 buffer client)
"Run `p4 diff2' on FILE and FILE2 and return a buffer containing the
results. If optional REV1 and/or REV2 are non-nil, they specify the
revisions to diff in the syntax described by `p4 help revisions'. If
optional BUFFER is non-nil, output goes in that buffer. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4 diff2'."
"Run `p4 diff2' on FILE1 and FILE2 and return a buffer containing the results.
If optional REV1 and/or REV2 are non-nil, they specify the
revisions to diff in the syntax described by `p4 help revisions'.
If optional BUFFER is non-nil, output goes in that buffer. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4
diff2'. If CLIENT is non-nil its passed on as the Perforce
client to use."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((file1-spec (if rev1 (concat file1 rev1) file1))
@ -536,7 +553,9 @@ optional BUFFER is non-nil, output goes in that buffer. Uses
(cl-defun p4-lowlevel-edit (file &key client)
"Tell Perforce we want to edit FILE.
Returns non-nil on success or nil on failure (or raises an error)."
Returns non-nil on success or nil on failure (or raises an
error). If CLIENT is non-nil its passed on as the Perforce
client to use."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "edit" file))))
(p4-lowlevel-command-or-error args)))
@ -551,11 +570,12 @@ Returns non-nil on success or nil on failure (or raises an error)."
(cl-defun p4-lowlevel-filelog (file &key buffer long follow-branches limit client)
"Fetch the p4 log of FILE and return a buffer containing it.
If optional BUFFER is non-nil, put output in that buffer. If optional
LONG is non-nil, return long output (i.e., pass the `-l' flag). If
optional FOLLOW-BRANCHES is non-nil, include pre-branch log entries in
output (i.e., pass the `-i' flag). If LIMIT is non-nil, get only the
last LIMIT log entries."
If optional BUFFER is non-nil, put output in that buffer. If
optional LONG is non-nil, return long output (i.e., pass the `-l'
flag). If optional FOLLOW-BRANCHES is non-nil, include pre-branch
log entries in output (i.e., pass the `-i' flag). If LIMIT is
non-nil, get only the last LIMIT log entries. If CLIENT is
non-nil it will be passed on as the Perforce client to use."
(let* ((long-flag (if long (list "-l")))
(branch-flag (if follow-branches (list "-i")))
(limit-flag (if limit (list "-m" (number-to-string limit))))
@ -564,7 +584,9 @@ last LIMIT log entries."
(p4-lowlevel-command-into-buffer args (or buffer "log"))))
(cl-defun p4-lowlevel-opened (file &key client)
"Fetch the string returned by running `p4 opened' on FILE."
"Fetch the string returned by running `p4 opened' on FILE.
Optional argument CLIENT is passed on to the p4 command as the
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "opened" file))))
(p4-lowlevel-command-or-error args nil 'string)))
@ -577,11 +599,14 @@ last LIMIT log entries."
(cl-defun p4-lowlevel-fstat (file &key rev noerror client)
"Fetch p4 information about FILE (optionally, at REV).
REV should be in the syntax described by `p4 help revisions'. Returns
a list of field-name/value elements on success, or raises an error on
failure. If optional third argument NOERROR is true, then returns nil
rather than raising an error on failure. If FILE matches multiple
files, then returns a list of lists of field-name/value elements."
REV should be in the syntax described by `p4 help revisions'.
Returns a list of field-name/value elements on success, or raises
an error on failure. If optional third argument NOERROR is true,
then returns nil rather than raising an error on failure. If FILE
matches multiple files, then returns a list of lists of
field-name/value elements. Optional argument CLIENT is passed on
to the p4 command as the Perforce client to use for the
operation."
(setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((file-spec (if rev (concat file rev) file))
(client-args (when client (list "-c" client)))
@ -611,7 +636,9 @@ files, then returns a list of lists of field-name/value elements."
lists)))
(cl-defun p4-lowlevel-info (&key client)
"Return an alist representing the output of `p4 info'."
"Return an alist representing the output of `p4 info'.
Optional argument CLIENT is passed on to the p4 command as the
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "info")))
(base-alist (p4-lowlevel-command-or-error args nil nil t))
@ -629,10 +656,13 @@ files, then returns a list of lists of field-name/value elements."
(cl-defun p4-lowlevel-print (file &key rev output-format quiet client)
"Retrieve the contents of FILE using `p4 print'.
If optional REV is non-nil, retrieve that revision, which should be in
the syntax described by `p4 help revisions'. Optional OUTPUT-FORMAT
is interpreted as described for `p4-lowlevel-command-or-error'. If optional
QUIET is non-nil, then the `-q' flag is passed to `p4 print'."
If optional REV is non-nil, retrieve that revision, which should
be in the syntax described by `p4 help revisions'. Optional
OUTPUT-FORMAT is interpreted as described for
`p4-lowlevel-command-or-error'. If optional QUIET is non-nil,
then the `-q' flag is passed to `p4 print'. Optional argument
CLIENT is passed on to the p4 command as the Perforce client to
use for the operation."
(setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file))
(quiet-args (if quiet (list "-q")))
@ -650,7 +680,9 @@ QUIET is non-nil, then the `-q' flag is passed to `p4 print'."
(cl-defun p4-lowlevel-reopen (file &key changelist client)
"Call `p4 reopen' on FILE.
Optional CHANGELIST specifies the changelist to which to move it."
Optional CHANGELIST specifies the changelist to which to move it.
Optional argument CLIENT is passed on to the p4 command as the
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(changelist-args (if changelist (list "-c" changelist)))
(args (append client-args (list "reopen") changelist-args (list file))))
@ -670,7 +702,9 @@ Optional CHANGELIST specifies the changelist to which to move it."
(cl-defun p4-lowlevel-resolve (file &key client)
"Call `p4 resolve' on FILE.
Specifies the `-af' and `-t' options to ensure a non-interactive
resolve. Raises an error if the command fails."
resolve. Raises an error if the command fails. Optional argument
CLIENT is passed on to the p4 command as the Perforce client to
use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "resolve" "-af" "-t" file))))
(p4-lowlevel-command-or-error args)))
@ -683,7 +717,9 @@ resolve. Raises an error if the command fails."
;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-revert (file &key client)
"Tell Perforce to unedit FILE."
"Tell Perforce to revert FILE.
Optional argument CLIENT is passed on to the p4 command as the
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "revert" file))))
(p4-lowlevel-command-or-error args)))
@ -695,7 +731,9 @@ resolve. Raises an error if the command fails."
;; support "p4 submit -i".
(cl-defun p4-lowlevel-submit (change-spec &key client)
"Calls `p4 submit' on CHANGE-SPEC, which should be a string or buffer."
"Call `p4 submit' on CHANGE-SPEC, which should be a string or buffer.
Optional argument CLIENT is passed on to the p4 command as the
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "submit" "-i")))
buffer)
@ -719,8 +757,10 @@ resolve. Raises an error if the command fails."
(cl-defun p4-lowlevel-sync (file &key rev force client)
"Call `p4 sync' for FILE.
If optional REV is specified, use that revision specifier. If
optional FORCE is non-nil, pass the `-f' flag."
If optional REV is specified, use that revision specifier. If
optional FORCE is non-nil, pass the `-f' flag. Optional argument
CLIENT is passed on to the p4 command as the Perforce client to
use for the operation."
(setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file))
(force-args (if force (list "-f")))
@ -729,9 +769,11 @@ optional FORCE is non-nil, pass the `-f' flag."
(p4-lowlevel-command-or-error args)))
(cl-defun p4-lowlevel-integrate (from-file to-file &key rev1 rev2 force client)
"Call `p4 integrate' from FROM-FILE to TO-FILE, with optional revision
range specified by REV1 and REV2, forcing the integration (i.e.,
specifying `-f' to `p4 integrate' if FORCE is non-nil."
"Call `p4 integrate' from FROM-FILE to TO-FILE.
Optionally a revision range can be specified by REV1 and REV2,
forcing the integration (i.e., specifying `-f' to `p4 integrate'
if FORCE is non-nil. Optional argument CLIENT is passed on to the
p4 command as the Perforce client to use for the operation."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((force-arg (if force (list "-f")))
@ -743,10 +785,10 @@ specifying `-f' to `p4 integrate' if FORCE is non-nil."
(p4-lowlevel-command-or-error args)))
(defun p4-lowlevel-client-version (&optional noerror)
"Returns the Perforce client version string from `p4 -V'.
"Return the Perforce client version string from `p4 -V'.
Returns the third field of the last line of output from `p4 -V', or
signals an error if the invocation failed. if optional NOERROR is
non-nil, returns nil instead of signalling an error."
non-nil, returns nil instead of signaling an error."
(let ((version-string (p4-lowlevel-command-or-error "-V" nil 'string
noerror)))
(if (string-match "\n[^/\n]+/[^/\n]+/\\([^\n/]+\\)/.*\n?\\'"
@ -755,9 +797,11 @@ non-nil, returns nil instead of signalling an error."
version-string))
(defun p4-lowlevel-get-buffer-create (name)
"Like get-buffer-create, but always changes default-directory of the
returned buffer to the current default-directory, even if the buffer
already exists."
"A specialization of get-buffer-create.
Always change default-directory of the returned buffer to the
current default-directory, even if the buffer already exists.
Argument NAME is passed directly to `get-buffer-create', so check
the documentation for that function to see its purpose."
(let ((buf (get-buffer-create name))
(caller-default-directory default-directory))
(save-excursion
@ -766,6 +810,7 @@ already exists."
buf))
(defun p4-lowlevel-local-clients ()
"Find the clients that are available on the current host."
(mapcar (lambda (client) (alist-get "client" client nil nil #'string=))
(seq-filter (lambda (client) (string= (alist-get "Host" client nil nil #'string=)
(system-name)))
@ -779,6 +824,9 @@ already exists."
lines nil)))
(defun p4-lowlevel-clients-parse-line (acc line)
"Parse a given line for information and store it in ACC.
ACC is an alist of found keys and their values from the output in
LINE."
(pcase (car line)
((pred (string= "info1"))
(let ((values (split-string (cdr line) " ")))
@ -803,7 +851,7 @@ already exists."
(_ acc))))
(`("exit" . ,result)
(if (not (= result 0))
(error "Clients returned non-zero exit code.")))
(error "Clients returned non-zero exit code")))
(_ acc)))
(cl-defun p4-lowlevel-login (&key password status)
@ -838,3 +886,7 @@ only on the server and not touch the local files."
(p4-lowlevel-command-or-error args)))
(provide 'p4-lowlevel)
(provide 'p4-lowlevel)
;;; p4-lowlevel.el ends here

148
vc-p4.el
View file

@ -77,25 +77,27 @@
(put 'P4 'vc-functions nil)
(defcustom vc-p4-require-p4config nil
"If non-nil and the `P4CONFIG' environment variable is set, then
only perform p4 operations on a file when a P4CONFIG file can be found
in one of its parent directories. This is useful if P4 operations are
expensive to start, e.g., if you are connect to the network over a
slow dialup connection and/or using a SSH tunnel for P4 access. To
avoid delays when opening non-P4 files, simply set P4CONFIG as
described in the Perforce documentation, create an empty P4CONFIG file
at the root of your client workspace, and set `vc-p4-require-p4config'
to t."
"Flag indicating the P4CONFIG environment variable is required.
If non-nil and the P4CONFIG environment variable is set, then
only perform p4 operations on a file when a P4CONFIG file can be
found in one of its parent directories. This is useful if P4
operations are expensive to start, e.g., if you are connect to
the network over a slow dial-up connection and/or using a SSH
tunnel for P4 access. To avoid delays when opening non-P4 files,
simply set P4CONFIG as described in the Perforce documentation,
create an empty P4CONFIG file at the root of your client
workspace, and set vc-p4-require-p4config to t."
:type 'boolean
:group 'vc)
(defcustom vc-p4-annotate-command nil
"*Specifies the name of a command to call to annotate Perforce files.
If nil, then `vc-p4-annotate-command-internal' will be used.
I recommend //guest/jonathan_kamens/p4pr.perl in the Perforce
repository public.perforce.com:1666. Note that you need a version of
this script which accept `--after=date', if you want to be able to
specify a starting date when you run C-u C-x v g."
If nil, then `vc-p4-annotate-command-internal' will be used. I
recommend //guest/jonathan_kamens/p4pr.perl in the Perforce
repository public.perforce.com:1666. Note that you need a version
of this script which accept `--after=date', if you want to be
able to specify a starting date when you run \\[vc-annotate] with
a prefix argument."
:type 'string
:group 'vc)
@ -110,7 +112,8 @@ Perforce has per-file revisions."
'file)
(defun vc-p4-create-repo ()
(error "create-repo not supported yet for P4"))
"This function is not supported."
(error "The create-repo function isnt supported yet for P4"))
(defun vc-p4-registered (file)
"Return non-nil is FILE is handled by Perforce."
@ -130,10 +133,10 @@ Perforce has per-file revisions."
t)))))
(defun vc-p4-state (file &optional fstat-list force dont-compare-nonopened)
"Returns the current version control state of FILE in Perforce.
"Return the current version control state of FILE in Perforce.
If optional FSTAT-LIST is non-nil, use that list of attributes
from p4-lowlevel-fstat instead of calling it. If optional FORCE
is non-nil, refetch all properties even if properties were
is non-nil, re-fetch all properties even if properties were
previously fetched. If DONT-COMPARE-NONOPENED is non-nil, don't
compare non-open files to the depot version."
(if (and (not force) (vc-file-getprop file 'vc-p4-did-fstat))
@ -180,7 +183,9 @@ compare non-open files to the depot version."
state)))))
(defun vc-p4-dir-status-files (dir files update-function)
"Find information for `vc-dir'."
"Find status information for files in the directory DIR.
FILES is ignored. The UPDATE-FUNCTION is used to process the
results of this function."
;; XXX: this should be asynchronous.
(let ((lists (p4-lowlevel-fstat
(format "%s/..." (directory-file-name (expand-file-name dir)))
@ -199,28 +204,30 @@ compare non-open files to the depot version."
(funcall update-function nil nil)))
(defun vc-p4-working-revision (file)
"Returns the Perforce version of FILE."
"Return the Perforce version of FILE."
(vc-p4-state file)
(vc-file-getprop file 'vc-workfile-version))
(defun vc-p4-previous-revision (file rev)
"Return the revision of FILE before REV.
If FILEs revision is 1, return that revision."
(let ((newrev (1- (string-to-number rev))))
(when (< 0 newrev)
(number-to-string newrev))))
(defun vc-p4-latest-on-branch-p (file)
"Returns non-nil if the Perforce version of FILE is the head
revision."
"Return non-nil if the Perforce version of FILE is the head revision."
(vc-p4-state file)
(string= (vc-file-getprop file 'vc-latest-version)
(vc-file-getprop file 'vc-workfile-version)))
(defun vc-p4-checkout-model (file)
"Returns the checkout model for Perforce (`announce')."
"Return the checkout model for Perforce (`announce').
Perforce only has one checkout model for all files, so FILE is ignored."
'announce)
(defun vc-p4-workfile-unchanged-p (file)
"Returns non-nil if FILE is unchanged from the version in Perforce."
"Return non-nil if FILE is unchanged from the version in Perforce."
(let ((state (vc-p4-state file)))
(and (not (equal (vc-file-getprop file 'vc-p4-action) "add"))
(not (equal (vc-file-getprop file 'vc-p4-action) "delete"))
@ -229,7 +236,7 @@ revision."
(p4-lowlevel-diff-s file "r" :client vc-p4-client)))))
(defun vc-p4-mode-line-string (file)
"Return string for placement into the modeline for FILE.
"Return string for placement into the mode-line for FILE.
Compared to the default implementation, this function handles the
special case of a Perforce file that is added but not yet committed."
(let ((state (vc-state file))
@ -248,10 +255,14 @@ special case of a Perforce file that is added but not yet committed."
(concat "P4:" rev)))))
(defun vc-p4-register (files &optional rev comment)
"Register FILES with Perforce.
REV can only be 1, Perforce doesnt support registering at any
other revision. COMMENT can only be nil or the empty string since
Perforce doesnt support adding a comment to registering a file."
(if (and rev (not (string= rev "1")))
(error "Can't specify revision when registering Perforce file."))
(error "Can't specify revision when registering Perforce file"))
(if (and comment (not (string= comment "")))
(error "Can't specify comment when registering Perforce file."))
(error "Can't specify comment when registering Perforce file"))
;; In emacs-23 vc-register has a list of files as a parameter,
;; before it used to be just a single file. We don't support that
;; interface yet, so just use the first file in the list.
@ -270,16 +281,16 @@ special case of a Perforce file that is added but not yet committed."
(rename-file tempfile file))
(p4-lowlevel-revert file :client vc-p4-client))
(p4-lowlevel-edit file :client vc-p4-client))
(error "File %s already opened for delete." file))
(error "File %s already opened for delete" file))
(p4-lowlevel-add file :client vc-p4-client))))
(defun vc-p4-init-revision ()
"Returns `1', the default initial version for Perforce files."
"Return `1', the default initial version for Perforce files."
"1")
(defun vc-p4-responsible-p (file)
"Returns true if FILE refers to a file or directory that is
administered by Perforce."
"Return non-nil if FILE is administered by Perforce.
FILE can point to either a file or a directory."
(if (and vc-p4-require-p4config
(getenv "P4CONFIG")
(not (vc-p4-find-p4config file)))
@ -290,6 +301,7 @@ administered by Perforce."
file)))))
(defun vc-p4-find-version (file rev buffer)
"Get the contents of FILE at revision REV and put it into BUFFER."
(p4-lowlevel-print file
:rev rev
:output-format buffer
@ -297,10 +309,10 @@ administered by Perforce."
:client vc-p4-client))
(defun vc-p4-checkin (files comment &optional rev)
"Check FILES into Perforce. Error if REV is non-nil. Check in with
comment COMMENT."
"Check FILES into Perforce.
Check in with comment COMMENT. Error if REV is non-nil."
(if rev
(error "Can't specify revision for Perforce checkin."))
(error "Can't specify revision for Perforce checkin"))
(let* (;; XXX: default-directory? this should work for most (all?) cases
(default-directory (file-name-directory (car files)))
(current-client
@ -337,8 +349,8 @@ comment COMMENT."
(dolist (file files)
(vc-p4-state file nil t)))))
;;; FIXME: this should not have a DESTFILE argument
(defun vc-p4-checkout (file &optional rev)
"Checkout FILE from Perforce, optionally at revision REV."
(let ((default-directory (file-name-directory file))
buffer)
;; Make sure we've got all the current state of the file
@ -355,7 +367,8 @@ comment COMMENT."
(vc-p4-state file nil t))
(defun vc-p4-revert (file contents-done)
"Revert FILE in Perforce. Ignores CONTENTS-DONE."
"Revert FILE in Perforce.
CONTENTS-DONE is ignored."
(let ((action (vc-file-getprop file 'vc-p4-action)))
(cond
((null action)
@ -372,7 +385,8 @@ comment COMMENT."
(vc-p4-state file nil t))))
(defun vc-p4-merge (file rev1 rev2)
"Merge changes into Perforce FILE from REV1 to REV2."
"Merge together two revisions of FILE.
REV1 and REV2 are the revisions to merge together."
(p4-lowlevel-integrate file file
:rev1 rev1
:rev2 rev2
@ -386,7 +400,7 @@ comment COMMENT."
0))
(defun vc-p4-merge-news (file)
"Merge new changes from Perforce into FILE."
"Get the latest version of FILE from Perforce."
(p4-lowlevel-sync file :client vc-p4-client)
(p4-lowlevel-resolve file :client vc-p4-client)
(vc-resynch-buffer file t t)
@ -396,18 +410,23 @@ comment COMMENT."
0))
(defun vc-p4-resolve-select-yours ()
"Resolve a file by selecting your version."
(vc-p4-select-conflict-text (current-buffer) 3))
(defun vc-p4-resolve-select-theirs ()
"Resolve a file by selecting their version."
(vc-p4-select-conflict-text (current-buffer) 2))
(defun vc-p4-resolve-select-original ()
"Resolve a file by selecting the original version."
(vc-p4-select-conflict-text (current-buffer) 1))
(defun vc-p4-steal-lock (file &optional version)
"Steal Perforce lock on FILE."
"Steal Perforce lock on FILE.
VERSION can only be the current version used in the workspace,
otherwise this function will raise an error."
(if (and version (not (equal version (vc-workfile-version file))))
(error "Can't specify version when stealing Perforce lock."))
(error "Can't specify version when stealing Perforce lock"))
;; Must set default-directory because this is called in a mail send hook and
;; thus not with the current buffer set to the file being reopened.
(let ((default-directory (file-name-directory file))
@ -416,7 +435,9 @@ comment COMMENT."
(p4-lowlevel-reopen file :client vc-p4-client)))
(defun vc-p4-print-log (files &optional buffer shortlog revision limit)
"Print Perforce log for FILE into *vc* buffer."
"Print Perforce log for FILES into BUFFER.
If SHORTLOG is non-nil print a short version of the log. REVISION
is ignored. If LIMIT is non-nil only print that many log messages."
;; `log-view-mode' needs to have the file name in order to function
;; correctly. "p4 logview" does not print it, so we insert it here by
;; hand.
@ -443,8 +464,7 @@ comment COMMENT."
(insert "File: " (file-name-nondirectory file) "\n"))))
(defun vc-p4-show-log-entry (version)
"Make sure Perforce log entry for VERSION is displayed in the
current buffer."
"Make sure Perforce log entry for VERSION is displayed in the current buffer."
(goto-char (point-min))
(let (start end lines)
(if (not (search-forward (format "\n#%s " version) nil t)) t
@ -474,14 +494,14 @@ current buffer."
(recenter 0))))))
(defun vc-p4-wash-log (file)
"Remove all non-comment information from the Perforce log in the
current buffer."
"Remove all non-comment information from the log in the current buffer.
FILE is ignored."
(goto-char (point-min))
(delete-non-matching-lines "^\t"))
(defun vc-p4-update-changelog (&optional files)
"Create ChangeLog entriers for FILES if it's non-nil, or for all
files under the default directory otherwise."
"Create ChangeLog entries for the files under default-directory.
Limit it to FILES if its non-nil"
(let ((odefault default-directory)
(changelog (find-change-log))
default-directory start-rev end-rev)
@ -541,7 +561,10 @@ files under the default directory otherwise."
("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))
(defun vc-p4-diff (file-or-files &optional rev1 rev2 buff _async)
"Do a Perforce diff."
"Do a Perforce diff of FILE-OR-FILES.
If REV1 and REV2 are non-nil display the diff of the two
revisions. If BUFF is non-nil output the diff into it, or use the
*vc-diff* buffer otherwise. _async is ignored."
(let* ((buffer (cond
((bufferp buff) buff)
((stringp buff) (get-buffer-create buff))
@ -671,7 +694,7 @@ Annotate version VERSION if it's specified."
;;; Adapted from p4.el
(defun vc-p4-read-output (buffer)
"Reads first line of BUFFER and returns it.
"Read the first line of BUFFER and return it.
Read lines are deleted from buffer."
(save-excursion
(set-buffer buffer)
@ -688,8 +711,8 @@ Read lines are deleted from buffer."
;;; Adapted from p4.el
(defun vc-p4-annotate-command-internal (file buffer &optional version)
"Execute \"hg annotate\" on FILE, inserting the contents in BUFFER.
Optional arg VERSION is a version to annotate from."
"Execute \"p4 annotate\" on FILE, inserting the contents in BUFFER.
Optional argument VERSION is a version to annotate from."
;; XXX maybe not needed, but just in case.
(vc-setup-buffer buffer)
;; (with-current-buffer buffer
@ -728,7 +751,7 @@ Optional arg VERSION is a version to annotate from."
(save-excursion
;; (set-buffer buffer)
(if (> (count-lines (point-min) (point-max)) 1)
(error "File pattern maps to more than one file."))))
(error "File pattern maps to more than one file"))))
;; get the file change history:
;;(p4-exec-p4 buffer (list "filelog" "-i" file-spec) t)
(with-temp-buffer
@ -895,9 +918,9 @@ Optional arg VERSION is a version to annotate from."
"[[:space:]]+\\([[:digit:]]+\\) "))
(defun vc-p4-annotate-time ()
"Returns the time of the next Perforce annotation at or after point,
as a floating point fractional number of days.
Moves the point to the end of the annotation."
"Return the time of the next Perforce annotation at or after point.
The value is returned as a floating point fractional number of
days. Moves the point to the end of the annotation."
(when (and (looking-at vc-p4-annotate-re) (fboundp 'vc-annotate-convert-time))
(goto-char (match-end 0))
(let ((timestr (match-string-no-properties 1)))
@ -909,6 +932,7 @@ Moves the point to the end of the annotation."
(string-to-number (match-string 1 timestr)))))))
(defun vc-p4-annotate-extract-revision-at-line ()
"Get the annotated revision on the current line."
(save-excursion
(beginning-of-line)
(if (looking-at vc-p4-annotate-re) (match-string-no-properties 3))))
@ -934,7 +958,7 @@ If DIRNAME is not specified, uses `default-directory'."
(directory-file-name this-directory))))))))
(defun vc-p4-is-in-client (file)
"Return true if FILE is inside the p4 client hierarchy."
"Return non-nil if FILE is inside the p4 client hierarchy."
(let* ((default-directory (file-name-directory file))
(info (p4-lowlevel-info :client vc-p4-client))
(root (alist-get "Client root" info nil nil #'string=))
@ -950,7 +974,7 @@ buffer, then that buffer is searched.
Returns nil if there are no conflicts. If there are conflicts,
returns a list of buffer positions containing the start and end of the
first conflict block in the file and then the start and end of each
subblock within it."
sub-block within it."
(let ((buffer (if (bufferp file) file (get-file-buffer file)))
block-start block-end block1-start block1-end block2-start block2-end
block3-start block3-end)
@ -990,7 +1014,7 @@ subblock within it."
(defun vc-p4-select-conflict-text (buffer which)
"Search for P4 conflict markers in BUFFER and select the WHICH text of each.
WHICH should be either 1, 2, or 3 to indicate the first, second or
third subblock in each conflict block."
third sub-block in each conflict block."
(let (block-list block-start block-end sub-start sub-end sublist subcount
replacement)
(save-excursion
@ -1012,8 +1036,11 @@ third subblock in each conflict block."
(if block-start t nil)))
(defun vc-p4-command (buffer okstatus file &rest flags)
"A wrapper around `vc-do-command' for use in vc-p4.el.
The difference to vc-do-command is that this function always invokes `p4'."
"A wrapper around vc-do-command for use in vc-p4.el.
The difference to vc-do-command is that this function always
invokes p4. The arguments BUFFER, OKSTATUS, FILE, and FLAGS are
all passed directly to vc-do-command, so check the
documentation for that command for their meanings."
(apply 'vc-do-command buffer okstatus "p4" file flags))
(defun vc-p4-delete-file (file)
@ -1021,6 +1048,7 @@ The difference to vc-do-command is that this function always invokes `p4'."
(p4-lowlevel-delete file :client vc-p4-client))
(defun vc-p4-switch-client (client)
"Switch to CLIENT as the current client used for all operations."
(interactive
(list (completing-read "Client: " (p4-lowlevel-local-clients))))
(p4-lowlevel-command-or-error `("set" ,(format "P4CLIENT=%s" client))))
@ -1035,6 +1063,7 @@ The difference to vc-do-command is that this function always invokes `p4'."
(p4-lowlevel-login :status t))
(defun vc-p4-dir-extra-headers (dir)
"Get extra Perforce-specific vc-dir headers related to DIR."
(let ((extra-info (p4-lowlevel-info :client vc-p4-client)))
(concat
(propertize "Client :" 'face 'font-lock-type-face)
@ -1062,3 +1091,4 @@ The difference to vc-do-command is that this function always invokes `p4'."
(p4-lowlevel-rename old new))
(provide 'vc-p4)
;;; vc-p4.el ends here