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 (defcustom p4-lowlevel-command-messages nil
"*If non-nil, display run messages from P4 commands. "*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 :type 'boolean
:group 'p4-lowlevel) :group 'p4-lowlevel)
(defcustom p4-lowlevel-ignore-error-list '("file(s) up-to-date") (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 list of Perforce errors to ignore.
a portion of the error string you wish 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")) :type '(repeat (string :tag "Error to Ignore"))
:group 'p4-lowlevel) :group 'p4-lowlevel)
@ -72,8 +74,9 @@ a portion of the error string you wish to ignore."
'p4-error) 'p4-error)
(defun p4-lowlevel-locate-p4 () (defun p4-lowlevel-locate-p4 ()
"Attempts to locate the p4 command. Used by `vc-p4-registered' to "Attempts to locate the p4 command.
avoid an error on systems on which the Perforce client is not installed." 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)) (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 exec-suffixes 'file-executable-p) ; GNU Emacs
(locate-file p4-lowlevel-p4-program exec-path '("" ".btm" ".bat" ".cmd" ".exe" ".com") 'executable))) ; XEmacs (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))) output-buffer)))
(defun p4-lowlevel-buffer-to-alist (&optional buffer) (defun p4-lowlevel-buffer-to-alist (&optional buffer)
"Converts the current buffer containing p4 output to an alist. If "Convert the current buffer containing p4 output to an alist.
optional argument BUFFER is non-nil, converts that buffer instead. If optional argument BUFFER is non-nil, converts that buffer
The returned alist contains one element for each `unit' in the buffer. instead. The returned alist contains one element for each `unit'
A `unit' is either a line containing a tag (`info:' or `info#:', in the buffer. A `unit' is either a line containing a
`error:', `exit:', etc.) or a block of untagged text. The car of each tag (`info:' or `info#:', `error:', `exit:', etc.) or a block of
element is the tag for the line (i.e., the string to the left of the text with no tag. The car of each element is the tag for the
colon, or `text' for text units), and the cdr is the value for the line (i.e., the string to the left of the colon, or `text' for
line (i.e., everything after the first `: ' sequence) or the entire text units), and the cdr is the value for the line (i.e.,
block of untagged text (including newlines other than the last one). 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." The alist is in the same order as the contents of the buffer."
(save-excursion (save-excursion
(if buffer (set-buffer buffer)) (if buffer (set-buffer buffer))
@ -187,12 +191,13 @@ The alist is in the same order as the contents of the buffer."
(nreverse alist)))) (nreverse alist))))
(defun p4-lowlevel-command-to-alist (args &optional input) (defun p4-lowlevel-command-to-alist (args &optional input)
"Calls `p4-lowlevel-command-to-buffer' and then "Call `p4-lowlevel-command-to-buffer' and then `p4-lowlevel-buffer-to-alist'.
`p4-lowlevel-buffer-to-alist'. Passes ARGS and optional INPUT to Passes ARGS and optional INPUT to
`p4-lowlevel-command-to-buffer'. Hands the resulting buffer to `p4-lowlevel-command-to-buffer'. Hands the resulting buffer to
`p4-lowlevel-buffer-to-alist' for parsing. Kills the output buffer `p4-lowlevel-buffer-to-alist' for parsing. Kills the output
when it has been parsed. Returns the resulting alist on success, or buffer when it has been parsed. Returns the resulting alist on
the return value of `p4-lowlevel-command-to-buffer' on failure." success, or the return value of `p4-lowlevel-command-to-buffer'
on failure."
(let ((output-buffer (p4-lowlevel-command-to-buffer args input)) (let ((output-buffer (p4-lowlevel-command-to-buffer args input))
return-value) return-value)
(setq return-value (p4-lowlevel-buffer-to-alist output-buffer)) (setq return-value (p4-lowlevel-buffer-to-alist output-buffer))
@ -241,16 +246,17 @@ value of 0."
t))))) t)))))
(defun p4-lowlevel-items-matching-tag (tag output) (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." OUTPUT may be a buffer or alist."
(if (bufferp output) (if (bufferp output)
(setq output (p4-lowlevel-buffer-to-alist output))) (setq output (p4-lowlevel-buffer-to-alist output)))
(mapcar (lambda (pair) (cdr pair)) (p4-lowlevel-re-assoc tag output))) (mapcar (lambda (pair) (cdr pair)) (p4-lowlevel-re-assoc tag output)))
(defun p4-lowlevel-lines-matching-tag (tag output) (defun p4-lowlevel-lines-matching-tag (tag output)
"Returns a string containing the lines matching TAG in p4 OUTPUT, or "Return a string containing the lines matching TAG in p4 OUTPUT.
nil if none. OUTPUT may be a buffer or alist. The lines are Return nil if none of the lines match TAG. OUTPUT may be a buffer
terminated by newlines. The tags are not included in the string." or alist. The lines are terminated by newlines. The tags are not
included in the string."
(if (bufferp output) (if (bufferp output)
(setq output (p4-lowlevel-buffer-to-alist output))) (setq output (p4-lowlevel-buffer-to-alist output)))
(let* ((alist (p4-lowlevel-re-assoc tag output)) (let* ((alist (p4-lowlevel-re-assoc tag output))
@ -263,7 +269,7 @@ terminated by newlines. The tags are not included in the string."
lines))) lines)))
(defun p4-lowlevel-errors (output) (defun p4-lowlevel-errors (output)
"Returns a string containing the errors in p4 OUTPUT, or nil if none. "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 OUTPUT may be a buffer or alist. The error lines are separated by
newlines, but there is no ending newline on the string." newlines, but there is no ending newline on the string."
(let ((errors (p4-lowlevel-lines-matching-tag "^error" output))) (let ((errors (p4-lowlevel-lines-matching-tag "^error" output)))
@ -272,26 +278,26 @@ newlines, but there is no ending newline on the string."
nil))) nil)))
(defun p4-lowlevel-info-lines (output) (defun p4-lowlevel-info-lines (output)
"Returns a string containing the info in p4 OUTPUT, or nil if none. "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 OUTPUT may be a buffer or alist. The info lines are terminated by
newlines." newlines."
(p4-lowlevel-lines-matching-tag "^info" output)) (p4-lowlevel-lines-matching-tag "^info" output))
(defun p4-lowlevel-text (output) (defun p4-lowlevel-text (output)
"Returns a string containing the text in p4 OUTPUT, or nil if none. "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 OUTPUT may be a buffer or alist. The text lines are terminated by
newlines." newlines."
(p4-lowlevel-lines-matching-tag "^text" output)) (p4-lowlevel-lines-matching-tag "^text" output))
(defun p4-lowlevel-command-or-error (args &optional input output-format noerror) (defun p4-lowlevel-command-or-error (args &optional input output-format noerror)
"Executes p4 command specified by ARGS and returns output or signals 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 Pass optional argument INPUT to `p4-lowlevel-command-to-buffer'.
argument OUTPUT-FORMAT is \'string, return a string containing the If optional argument OUTPUT-FORMAT is \'string, return a string
output (including tags). If it is \'buffer, return the temporary containing the output (including tags). If it is \'buffer, return
buffer containing the output. If it is a buffer, put output in that the temporary buffer containing the output. If it is a buffer,
buffer and return it. If it is anything else, return an alist of the put output in that buffer and return it. If it is anything else,
output. If optional fourth argument NOERROR is true, then returns nil return an alist of the output. If optional fourth argument
rather than raising an error." NOERROR is true, then returns nil rather than raising an error."
(let* (errors error-buffer return-value (let* (errors error-buffer return-value
(output-buffer (p4-lowlevel-command-to-buffer args input (output-buffer (p4-lowlevel-command-to-buffer args input
(if (bufferp output-format) (if (bufferp output-format)
@ -327,11 +333,12 @@ rather than raising an error."
return-value)) return-value))
(defun p4-lowlevel-command-into-buffer (args buffer) (defun p4-lowlevel-command-into-buffer (args buffer)
"Executes p4 command specified by ARGS, raising errors when necessary. "Execute p4 command specified by ARGS, raising errors when necessary.
If BUFFER is a string, then puts output in buffer whose name is formed If BUFFER is a string, then puts output in buffer whose name is
by concatenating ` *p4-lowevel-', BUFFER-NAME, and `*' (e.g., if BUFFER is formed by concatenating ` *p4-lowevel-', BUFFER-NAME, and
`diff', then output goes in buffer ` *p4-lowevel-diff*'). If BUFFER is a `*' (e.g., if BUFFER is `diff', then output goes in buffer `
buffer, then puts output in that buffer. Returns the 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)) (let* ((output-alist (p4-lowlevel-command-or-error args))
(output-buffer (if (bufferp buffer) buffer (output-buffer (if (bufferp buffer) buffer
(p4-lowlevel-get-buffer-create (p4-lowlevel-get-buffer-create
@ -344,15 +351,14 @@ buffer, then puts output in that buffer. Returns the buffer."
output-buffer))) output-buffer)))
(defun p4-lowlevel-command-messages () (defun p4-lowlevel-command-messages ()
"Return t if p4-lowlevel-command-messages or vc-command-messages is "Return t if vc-command-messages bound and true.
bound and true." Fall back to the value of p4-lowlevel-command-messages."
(if (and (boundp 'vc-command-messages) vc-command-messages) (if (and (boundp 'vc-command-messages) vc-command-messages)
t t
p4-lowlevel-command-messages)) p4-lowlevel-command-messages))
(defun p4-lowlevel-canonicalize-revision (rev) (defun p4-lowlevel-canonicalize-revision (rev)
"Turn REV into a form which can be concatenated to file names in P4 "Turn REV into a form which can be concatenated to file names in P4 commands."
commands."
;; There is some ambiguity here, since a number can be either a revision ;; 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 ;; number (#rev) or a change number (@change). We assume that a bare number is
;; a revision number. ;; a revision number.
@ -376,7 +382,8 @@ commands."
(cl-defun p4-lowlevel-add (file &key client) (cl-defun p4-lowlevel-add (file &key client)
"Tell Perforce to add FILE to the repository. "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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "add" file)))) (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 ;; 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))) (p4-lowlevel-command-or-error args)))
(cl-defun p4-lowlevel-delete (file &key client) (cl-defun p4-lowlevel-delete (file &key client)
"Tell Perforce to delet FILE from the repository. "Tell Perforce to delete FILE from 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 on as the Perforce client to use."
(let* ((client-args (if client (list "-c" client))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "delete" file)))) (args (append client-args (list "delete" file))))
(p4-lowlevel-command-or-error args))) (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. ;; DO need to support specified changelist #'s.
(cl-defun p4-lowlevel-change (&key buffer op client) (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 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 retrieved into BUFFER, or into a new buffer if BUFFER is nil. If
is non-nil and not a number, then then BUFFER should contain an OP is non-nil and not a number, then then BUFFER should contain
existing changelist which is saved to the database; the number of the an existing changelist which is saved to the database; the number
new or updated changelist is returned. If OP is nil then a new of the new or updated changelist is returned. If OP is nil then a
changelist is retrieved into BUFFER (or a new buffer). The output new changelist is retrieved into BUFFER (or a new buffer). The
buffer is returned." 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)) (let* ((input-buffer (if (and op (not (numberp op))) buffer nil))
(flag-arg (if (or (not op) (numberp op)) "-o" "-i")) (flag-arg (if (or (not op) (numberp op)) "-o" "-i"))
(number-arg (if (numberp op) (list (number-to-string op)))) (number-arg (if (numberp op) (list (number-to-string op))))
@ -432,11 +441,14 @@ buffer is returned."
(cl-defun p4-lowlevel-changes (cl-defun p4-lowlevel-changes
(file-pattern &key output-format rev1 rev2 i-flag l-flag m-val s-val client) (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 "Call “p4 changes” command on FILE-PATTERN.
described in `p4-lowlevel-command-or-error'. Optionally, limit output Optional OUTPUT-FORMAT is as described in
to the revisions between REV1 and REV2. If I-FLAG is non-nil, pass `p4-lowlevel-command-or-error'. Optionally, limit output to the
`-i'; if L-FLAG is non-nil, pass `-l'; if M-VAL is non-nil, pass that revisions between REV1 and REV2. If I-FLAG is non-nil, pass `-i';
value with `-m'; if S-VAL is non-nil, pass that value with `-s'." 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) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let ((full-file (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. ;; Do NOT need to support diffing multiple files.
(cl-defun p4-lowlevel-diff (files &key rev buffer client) (cl-defun p4-lowlevel-diff (files &key rev buffer client)
"Run `p4 diff' on FILE at revision REV and return a buffer "Run `p4 diff' on FILES at revision REV.
containing the results. REV is in the syntax described by `p4 help Return a buffer containing the results. REV is in the syntax
revisions'. If REV is nil, compare the client's sync'd revision to described by `p4 help revisions'. If REV is nil, compare the
the file on disk. Uses `p4-lowlevel-diff-switches' to determine flags client's synced revision to the file on disk. Uses
to pass to `p4 diff'. If optional BUFFER is non-nil, put output in `p4-lowlevel-diff-switches' to determine flags to pass to `p4
that buffer." 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) (unless (listp files)
(setq files (list files))) (setq files (list files)))
(setq rev (p4-lowlevel-canonicalize-revision rev)) (setq rev (p4-lowlevel-canonicalize-revision rev))
@ -494,8 +508,9 @@ that buffer."
buffer)) buffer))
(cl-defun p4-lowlevel-diff-s (file flag &key client) (cl-defun p4-lowlevel-diff-s (file flag &key client)
"Run `p4 diff -s' on FILE, using FLAG as the argument to `-s', and "Run `p4 diff -s' on FILE, using FLAG as the argument to `-s'.
return a list of the matching files." 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 (p4-lowlevel-items-matching-tag
"^info" "^info"
(let* ((client-args (if client (list "-c" client))) (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". ;; Do NOT need to support "-b".
(cl-defun p4-lowlevel-diff2 (file1 file2 &key rev1 rev2 buffer client) (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 "Run `p4 diff2' on FILE1 and FILE2 and return a buffer containing the results.
results. If optional REV1 and/or REV2 are non-nil, they specify the If optional REV1 and/or REV2 are non-nil, they specify the
revisions to diff in the syntax described by `p4 help revisions'. If revisions to diff in the syntax described by `p4 help revisions'.
optional BUFFER is non-nil, output goes in that buffer. Uses If optional BUFFER is non-nil, output goes in that buffer. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4 diff2'." `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) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((file1-spec (if rev1 (concat file1 rev1) file1)) (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) (cl-defun p4-lowlevel-edit (file &key client)
"Tell Perforce we want to edit FILE. "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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "edit" file)))) (args (append client-args (list "edit" file))))
(p4-lowlevel-command-or-error args))) (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) (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. "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 If optional BUFFER is non-nil, put output in that buffer. If
LONG is non-nil, return long output (i.e., pass the `-l' flag). If optional LONG is non-nil, return long output (i.e., pass the `-l'
optional FOLLOW-BRANCHES is non-nil, include pre-branch log entries in flag). If optional FOLLOW-BRANCHES is non-nil, include pre-branch
output (i.e., pass the `-i' flag). If LIMIT is non-nil, get only the log entries in output (i.e., pass the `-i' flag). If LIMIT is
last LIMIT log entries." 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"))) (let* ((long-flag (if long (list "-l")))
(branch-flag (if follow-branches (list "-i"))) (branch-flag (if follow-branches (list "-i")))
(limit-flag (if limit (list "-m" (number-to-string limit)))) (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")))) (p4-lowlevel-command-into-buffer args (or buffer "log"))))
(cl-defun p4-lowlevel-opened (file &key client) (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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "opened" file)))) (args (append client-args (list "opened" file))))
(p4-lowlevel-command-or-error args nil 'string))) (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) (cl-defun p4-lowlevel-fstat (file &key rev noerror client)
"Fetch p4 information about FILE (optionally, at REV). "Fetch p4 information about FILE (optionally, at REV).
REV should be in the syntax described by `p4 help revisions'. Returns REV should be in the syntax described by `p4 help revisions'.
a list of field-name/value elements on success, or raises an error on Returns a list of field-name/value elements on success, or raises
failure. If optional third argument NOERROR is true, then returns nil an error on failure. If optional third argument NOERROR is true,
rather than raising an error on failure. If FILE matches multiple then returns nil rather than raising an error on failure. If FILE
files, then returns a list of lists of field-name/value elements." 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)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((file-spec (if rev (concat file rev) file)) (let* ((file-spec (if rev (concat file rev) file))
(client-args (when client (list "-c" client))) (client-args (when client (list "-c" client)))
@ -611,7 +636,9 @@ files, then returns a list of lists of field-name/value elements."
lists))) lists)))
(cl-defun p4-lowlevel-info (&key client) (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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "info"))) (args (append client-args (list "info")))
(base-alist (p4-lowlevel-command-or-error args nil nil t)) (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) (cl-defun p4-lowlevel-print (file &key rev output-format quiet client)
"Retrieve the contents of FILE using `p4 print'. "Retrieve the contents of FILE using `p4 print'.
If optional REV is non-nil, retrieve that revision, which should be in If optional REV is non-nil, retrieve that revision, which should
the syntax described by `p4 help revisions'. Optional OUTPUT-FORMAT be in the syntax described by `p4 help revisions'. Optional
is interpreted as described for `p4-lowlevel-command-or-error'. If optional OUTPUT-FORMAT is interpreted as described for
QUIET is non-nil, then the `-q' flag is passed to `p4 print'." `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)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file)) (let* ((fullfile (if rev (concat file rev) file))
(quiet-args (if quiet (list "-q"))) (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) (cl-defun p4-lowlevel-reopen (file &key changelist client)
"Call `p4 reopen' on FILE. "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))) (let* ((client-args (if client (list "-c" client)))
(changelist-args (if changelist (list "-c" changelist))) (changelist-args (if changelist (list "-c" changelist)))
(args (append client-args (list "reopen") changelist-args (list file)))) (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) (cl-defun p4-lowlevel-resolve (file &key client)
"Call `p4 resolve' on FILE. "Call `p4 resolve' on FILE.
Specifies the `-af' and `-t' options to ensure a non-interactive 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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "resolve" "-af" "-t" file)))) (args (append client-args (list "resolve" "-af" "-t" file))))
(p4-lowlevel-command-or-error args))) (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. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-revert (file &key client) (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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "revert" file)))) (args (append client-args (list "revert" file))))
(p4-lowlevel-command-or-error args))) (p4-lowlevel-command-or-error args)))
@ -695,7 +731,9 @@ resolve. Raises an error if the command fails."
;; support "p4 submit -i". ;; support "p4 submit -i".
(cl-defun p4-lowlevel-submit (change-spec &key client) (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))) (let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "submit" "-i"))) (args (append client-args (list "submit" "-i")))
buffer) buffer)
@ -720,7 +758,9 @@ resolve. Raises an error if the command fails."
(cl-defun p4-lowlevel-sync (file &key rev force client) (cl-defun p4-lowlevel-sync (file &key rev force client)
"Call `p4 sync' for FILE. "Call `p4 sync' for FILE.
If optional REV is specified, use that revision specifier. If If optional REV is specified, use that revision specifier. If
optional FORCE is non-nil, pass the `-f' flag." 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)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file)) (let* ((fullfile (if rev (concat file rev) file))
(force-args (if force (list "-f"))) (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))) (p4-lowlevel-command-or-error args)))
(cl-defun p4-lowlevel-integrate (from-file to-file &key rev1 rev2 force client) (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 "Call `p4 integrate' from FROM-FILE to TO-FILE.
range specified by REV1 and REV2, forcing the integration (i.e., Optionally a revision range can be specified by REV1 and REV2,
specifying `-f' to `p4 integrate' if FORCE is non-nil." 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) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((force-arg (if force (list "-f"))) (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))) (p4-lowlevel-command-or-error args)))
(defun p4-lowlevel-client-version (&optional noerror) (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 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 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 (let ((version-string (p4-lowlevel-command-or-error "-V" nil 'string
noerror))) noerror)))
(if (string-match "\n[^/\n]+/[^/\n]+/\\([^\n/]+\\)/.*\n?\\'" (if (string-match "\n[^/\n]+/[^/\n]+/\\([^\n/]+\\)/.*\n?\\'"
@ -755,9 +797,11 @@ non-nil, returns nil instead of signalling an error."
version-string)) version-string))
(defun p4-lowlevel-get-buffer-create (name) (defun p4-lowlevel-get-buffer-create (name)
"Like get-buffer-create, but always changes default-directory of the "A specialization of get-buffer-create.
returned buffer to the current default-directory, even if the buffer Always change default-directory of the returned buffer to the
already exists." 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)) (let ((buf (get-buffer-create name))
(caller-default-directory default-directory)) (caller-default-directory default-directory))
(save-excursion (save-excursion
@ -766,6 +810,7 @@ already exists."
buf)) buf))
(defun p4-lowlevel-local-clients () (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=)) (mapcar (lambda (client) (alist-get "client" client nil nil #'string=))
(seq-filter (lambda (client) (string= (alist-get "Host" client nil nil #'string=) (seq-filter (lambda (client) (string= (alist-get "Host" client nil nil #'string=)
(system-name))) (system-name)))
@ -779,6 +824,9 @@ already exists."
lines nil))) lines nil)))
(defun p4-lowlevel-clients-parse-line (acc line) (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) (pcase (car line)
((pred (string= "info1")) ((pred (string= "info1"))
(let ((values (split-string (cdr line) " "))) (let ((values (split-string (cdr line) " ")))
@ -803,7 +851,7 @@ already exists."
(_ acc)))) (_ acc))))
(`("exit" . ,result) (`("exit" . ,result)
(if (not (= result 0)) (if (not (= result 0))
(error "Clients returned non-zero exit code."))) (error "Clients returned non-zero exit code")))
(_ acc))) (_ acc)))
(cl-defun p4-lowlevel-login (&key password status) (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))) (p4-lowlevel-command-or-error args)))
(provide 'p4-lowlevel) (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) (put 'P4 'vc-functions nil)
(defcustom vc-p4-require-p4config nil (defcustom vc-p4-require-p4config nil
"If non-nil and the `P4CONFIG' environment variable is set, then "Flag indicating the P4CONFIG environment variable is required.
only perform p4 operations on a file when a P4CONFIG file can be found If non-nil and the P4CONFIG environment variable is set, then
in one of its parent directories. This is useful if P4 operations are only perform p4 operations on a file when a P4CONFIG file can be
expensive to start, e.g., if you are connect to the network over a found in one of its parent directories. This is useful if P4
slow dialup connection and/or using a SSH tunnel for P4 access. To operations are expensive to start, e.g., if you are connect to
avoid delays when opening non-P4 files, simply set P4CONFIG as the network over a slow dial-up connection and/or using a SSH
described in the Perforce documentation, create an empty P4CONFIG file tunnel for P4 access. To avoid delays when opening non-P4 files,
at the root of your client workspace, and set `vc-p4-require-p4config' simply set P4CONFIG as described in the Perforce documentation,
to t." create an empty P4CONFIG file at the root of your client
workspace, and set vc-p4-require-p4config to t."
:type 'boolean :type 'boolean
:group 'vc) :group 'vc)
(defcustom vc-p4-annotate-command nil (defcustom vc-p4-annotate-command nil
"*Specifies the name of a command to call to annotate Perforce files. "*Specifies the name of a command to call to annotate Perforce files.
If nil, then `vc-p4-annotate-command-internal' will be used. If nil, then `vc-p4-annotate-command-internal' will be used. I
I recommend //guest/jonathan_kamens/p4pr.perl in the Perforce recommend //guest/jonathan_kamens/p4pr.perl in the Perforce
repository public.perforce.com:1666. Note that you need a version of repository public.perforce.com:1666. Note that you need a version
this script which accept `--after=date', if you want to be able to of this script which accept `--after=date', if you want to be
specify a starting date when you run C-u C-x v g." able to specify a starting date when you run \\[vc-annotate] with
a prefix argument."
:type 'string :type 'string
:group 'vc) :group 'vc)
@ -110,7 +112,8 @@ Perforce has per-file revisions."
'file) 'file)
(defun vc-p4-create-repo () (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) (defun vc-p4-registered (file)
"Return non-nil is FILE is handled by Perforce." "Return non-nil is FILE is handled by Perforce."
@ -130,10 +133,10 @@ Perforce has per-file revisions."
t))))) t)))))
(defun vc-p4-state (file &optional fstat-list force dont-compare-nonopened) (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 If optional FSTAT-LIST is non-nil, use that list of attributes
from p4-lowlevel-fstat instead of calling it. If optional FORCE 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 previously fetched. If DONT-COMPARE-NONOPENED is non-nil, don't
compare non-open files to the depot version." compare non-open files to the depot version."
(if (and (not force) (vc-file-getprop file 'vc-p4-did-fstat)) (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))))) state)))))
(defun vc-p4-dir-status-files (dir files update-function) (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. ;; XXX: this should be asynchronous.
(let ((lists (p4-lowlevel-fstat (let ((lists (p4-lowlevel-fstat
(format "%s/..." (directory-file-name (expand-file-name dir))) (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))) (funcall update-function nil nil)))
(defun vc-p4-working-revision (file) (defun vc-p4-working-revision (file)
"Returns the Perforce version of FILE." "Return the Perforce version of FILE."
(vc-p4-state file) (vc-p4-state file)
(vc-file-getprop file 'vc-workfile-version)) (vc-file-getprop file 'vc-workfile-version))
(defun vc-p4-previous-revision (file rev) (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)))) (let ((newrev (1- (string-to-number rev))))
(when (< 0 newrev) (when (< 0 newrev)
(number-to-string newrev)))) (number-to-string newrev))))
(defun vc-p4-latest-on-branch-p (file) (defun vc-p4-latest-on-branch-p (file)
"Returns non-nil if the Perforce version of FILE is the head "Return non-nil if the Perforce version of FILE is the head revision."
revision."
(vc-p4-state file) (vc-p4-state file)
(string= (vc-file-getprop file 'vc-latest-version) (string= (vc-file-getprop file 'vc-latest-version)
(vc-file-getprop file 'vc-workfile-version))) (vc-file-getprop file 'vc-workfile-version)))
(defun vc-p4-checkout-model (file) (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) 'announce)
(defun vc-p4-workfile-unchanged-p (file) (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))) (let ((state (vc-p4-state file)))
(and (not (equal (vc-file-getprop file 'vc-p4-action) "add")) (and (not (equal (vc-file-getprop file 'vc-p4-action) "add"))
(not (equal (vc-file-getprop file 'vc-p4-action) "delete")) (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))))) (p4-lowlevel-diff-s file "r" :client vc-p4-client)))))
(defun vc-p4-mode-line-string (file) (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 Compared to the default implementation, this function handles the
special case of a Perforce file that is added but not yet committed." special case of a Perforce file that is added but not yet committed."
(let ((state (vc-state file)) (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))))) (concat "P4:" rev)))))
(defun vc-p4-register (files &optional rev comment) (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"))) (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 ""))) (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, ;; 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 ;; 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. ;; 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)) (rename-file tempfile file))
(p4-lowlevel-revert file :client vc-p4-client)) (p4-lowlevel-revert file :client vc-p4-client))
(p4-lowlevel-edit 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)))) (p4-lowlevel-add file :client vc-p4-client))))
(defun vc-p4-init-revision () (defun vc-p4-init-revision ()
"Returns `1', the default initial version for Perforce files." "Return `1', the default initial version for Perforce files."
"1") "1")
(defun vc-p4-responsible-p (file) (defun vc-p4-responsible-p (file)
"Returns true if FILE refers to a file or directory that is "Return non-nil if FILE is administered by Perforce.
administered by Perforce." FILE can point to either a file or a directory."
(if (and vc-p4-require-p4config (if (and vc-p4-require-p4config
(getenv "P4CONFIG") (getenv "P4CONFIG")
(not (vc-p4-find-p4config file))) (not (vc-p4-find-p4config file)))
@ -290,6 +301,7 @@ administered by Perforce."
file))))) file)))))
(defun vc-p4-find-version (file rev buffer) (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 (p4-lowlevel-print file
:rev rev :rev rev
:output-format buffer :output-format buffer
@ -297,10 +309,10 @@ administered by Perforce."
:client vc-p4-client)) :client vc-p4-client))
(defun vc-p4-checkin (files comment &optional rev) (defun vc-p4-checkin (files comment &optional rev)
"Check FILES into Perforce. Error if REV is non-nil. Check in with "Check FILES into Perforce.
comment COMMENT." Check in with comment COMMENT. Error if REV is non-nil."
(if rev (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 (let* (;; XXX: default-directory? this should work for most (all?) cases
(default-directory (file-name-directory (car files))) (default-directory (file-name-directory (car files)))
(current-client (current-client
@ -337,8 +349,8 @@ comment COMMENT."
(dolist (file files) (dolist (file files)
(vc-p4-state file nil t))))) (vc-p4-state file nil t)))))
;;; FIXME: this should not have a DESTFILE argument
(defun vc-p4-checkout (file &optional rev) (defun vc-p4-checkout (file &optional rev)
"Checkout FILE from Perforce, optionally at revision REV."
(let ((default-directory (file-name-directory file)) (let ((default-directory (file-name-directory file))
buffer) buffer)
;; Make sure we've got all the current state of the file ;; Make sure we've got all the current state of the file
@ -355,7 +367,8 @@ comment COMMENT."
(vc-p4-state file nil t)) (vc-p4-state file nil t))
(defun vc-p4-revert (file contents-done) (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))) (let ((action (vc-file-getprop file 'vc-p4-action)))
(cond (cond
((null action) ((null action)
@ -372,7 +385,8 @@ comment COMMENT."
(vc-p4-state file nil t)))) (vc-p4-state file nil t))))
(defun vc-p4-merge (file rev1 rev2) (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 (p4-lowlevel-integrate file file
:rev1 rev1 :rev1 rev1
:rev2 rev2 :rev2 rev2
@ -386,7 +400,7 @@ comment COMMENT."
0)) 0))
(defun vc-p4-merge-news (file) (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-sync file :client vc-p4-client)
(p4-lowlevel-resolve file :client vc-p4-client) (p4-lowlevel-resolve file :client vc-p4-client)
(vc-resynch-buffer file t t) (vc-resynch-buffer file t t)
@ -396,18 +410,23 @@ comment COMMENT."
0)) 0))
(defun vc-p4-resolve-select-yours () (defun vc-p4-resolve-select-yours ()
"Resolve a file by selecting your version."
(vc-p4-select-conflict-text (current-buffer) 3)) (vc-p4-select-conflict-text (current-buffer) 3))
(defun vc-p4-resolve-select-theirs () (defun vc-p4-resolve-select-theirs ()
"Resolve a file by selecting their version."
(vc-p4-select-conflict-text (current-buffer) 2)) (vc-p4-select-conflict-text (current-buffer) 2))
(defun vc-p4-resolve-select-original () (defun vc-p4-resolve-select-original ()
"Resolve a file by selecting the original version."
(vc-p4-select-conflict-text (current-buffer) 1)) (vc-p4-select-conflict-text (current-buffer) 1))
(defun vc-p4-steal-lock (file &optional version) (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)))) (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 ;; 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. ;; thus not with the current buffer set to the file being reopened.
(let ((default-directory (file-name-directory file)) (let ((default-directory (file-name-directory file))
@ -416,7 +435,9 @@ comment COMMENT."
(p4-lowlevel-reopen file :client vc-p4-client))) (p4-lowlevel-reopen file :client vc-p4-client)))
(defun vc-p4-print-log (files &optional buffer shortlog revision limit) (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 ;; `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 ;; correctly. "p4 logview" does not print it, so we insert it here by
;; hand. ;; hand.
@ -443,8 +464,7 @@ comment COMMENT."
(insert "File: " (file-name-nondirectory file) "\n")))) (insert "File: " (file-name-nondirectory file) "\n"))))
(defun vc-p4-show-log-entry (version) (defun vc-p4-show-log-entry (version)
"Make sure Perforce log entry for VERSION is displayed in the "Make sure Perforce log entry for VERSION is displayed in the current buffer."
current buffer."
(goto-char (point-min)) (goto-char (point-min))
(let (start end lines) (let (start end lines)
(if (not (search-forward (format "\n#%s " version) nil t)) t (if (not (search-forward (format "\n#%s " version) nil t)) t
@ -474,14 +494,14 @@ current buffer."
(recenter 0)))))) (recenter 0))))))
(defun vc-p4-wash-log (file) (defun vc-p4-wash-log (file)
"Remove all non-comment information from the Perforce log in the "Remove all non-comment information from the log in the current buffer.
current buffer." FILE is ignored."
(goto-char (point-min)) (goto-char (point-min))
(delete-non-matching-lines "^\t")) (delete-non-matching-lines "^\t"))
(defun vc-p4-update-changelog (&optional files) (defun vc-p4-update-changelog (&optional files)
"Create ChangeLog entriers for FILES if it's non-nil, or for all "Create ChangeLog entries for the files under default-directory.
files under the default directory otherwise." Limit it to FILES if its non-nil"
(let ((odefault default-directory) (let ((odefault default-directory)
(changelog (find-change-log)) (changelog (find-change-log))
default-directory start-rev end-rev) default-directory start-rev end-rev)
@ -541,7 +561,10 @@ files under the default directory otherwise."
("^summary:[ \t]+\\(.+\\)" (1 'log-view-message)))))) ("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))
(defun vc-p4-diff (file-or-files &optional rev1 rev2 buff _async) (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 (let* ((buffer (cond
((bufferp buff) buff) ((bufferp buff) buff)
((stringp buff) (get-buffer-create buff)) ((stringp buff) (get-buffer-create buff))
@ -671,7 +694,7 @@ Annotate version VERSION if it's specified."
;;; Adapted from p4.el ;;; Adapted from p4.el
(defun vc-p4-read-output (buffer) (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." Read lines are deleted from buffer."
(save-excursion (save-excursion
(set-buffer buffer) (set-buffer buffer)
@ -688,8 +711,8 @@ Read lines are deleted from buffer."
;;; Adapted from p4.el ;;; Adapted from p4.el
(defun vc-p4-annotate-command-internal (file buffer &optional version) (defun vc-p4-annotate-command-internal (file buffer &optional version)
"Execute \"hg annotate\" on FILE, inserting the contents in BUFFER. "Execute \"p4 annotate\" on FILE, inserting the contents in BUFFER.
Optional arg VERSION is a version to annotate from." Optional argument VERSION is a version to annotate from."
;; XXX maybe not needed, but just in case. ;; XXX maybe not needed, but just in case.
(vc-setup-buffer buffer) (vc-setup-buffer buffer)
;; (with-current-buffer buffer ;; (with-current-buffer buffer
@ -728,7 +751,7 @@ Optional arg VERSION is a version to annotate from."
(save-excursion (save-excursion
;; (set-buffer buffer) ;; (set-buffer buffer)
(if (> (count-lines (point-min) (point-max)) 1) (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: ;; get the file change history:
;;(p4-exec-p4 buffer (list "filelog" "-i" file-spec) t) ;;(p4-exec-p4 buffer (list "filelog" "-i" file-spec) t)
(with-temp-buffer (with-temp-buffer
@ -895,9 +918,9 @@ Optional arg VERSION is a version to annotate from."
"[[:space:]]+\\([[:digit:]]+\\) ")) "[[:space:]]+\\([[:digit:]]+\\) "))
(defun vc-p4-annotate-time () (defun vc-p4-annotate-time ()
"Returns the time of the next Perforce annotation at or after point, "Return the time of the next Perforce annotation at or after point.
as a floating point fractional number of days. The value is returned as a floating point fractional number of
Moves the point to the end of the annotation." days. Moves the point to the end of the annotation."
(when (and (looking-at vc-p4-annotate-re) (fboundp 'vc-annotate-convert-time)) (when (and (looking-at vc-p4-annotate-re) (fboundp 'vc-annotate-convert-time))
(goto-char (match-end 0)) (goto-char (match-end 0))
(let ((timestr (match-string-no-properties 1))) (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))))))) (string-to-number (match-string 1 timestr)))))))
(defun vc-p4-annotate-extract-revision-at-line () (defun vc-p4-annotate-extract-revision-at-line ()
"Get the annotated revision on the current line."
(save-excursion (save-excursion
(beginning-of-line) (beginning-of-line)
(if (looking-at vc-p4-annotate-re) (match-string-no-properties 3)))) (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)))))))) (directory-file-name this-directory))))))))
(defun vc-p4-is-in-client (file) (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)) (let* ((default-directory (file-name-directory file))
(info (p4-lowlevel-info :client vc-p4-client)) (info (p4-lowlevel-info :client vc-p4-client))
(root (alist-get "Client root" info nil nil #'string=)) (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 nil if there are no conflicts. If there are conflicts,
returns a list of buffer positions containing the start and end of the 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 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))) (let ((buffer (if (bufferp file) file (get-file-buffer file)))
block-start block-end block1-start block1-end block2-start block2-end block-start block-end block1-start block1-end block2-start block2-end
block3-start block3-end) block3-start block3-end)
@ -990,7 +1014,7 @@ subblock within it."
(defun vc-p4-select-conflict-text (buffer which) (defun vc-p4-select-conflict-text (buffer which)
"Search for P4 conflict markers in BUFFER and select the WHICH text of each. "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 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 (let (block-list block-start block-end sub-start sub-end sublist subcount
replacement) replacement)
(save-excursion (save-excursion
@ -1012,8 +1036,11 @@ third subblock in each conflict block."
(if block-start t nil))) (if block-start t nil)))
(defun vc-p4-command (buffer okstatus file &rest flags) (defun vc-p4-command (buffer okstatus file &rest flags)
"A wrapper around `vc-do-command' for use in vc-p4.el. "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 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)) (apply 'vc-do-command buffer okstatus "p4" file flags))
(defun vc-p4-delete-file (file) (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)) (p4-lowlevel-delete file :client vc-p4-client))
(defun vc-p4-switch-client (client) (defun vc-p4-switch-client (client)
"Switch to CLIENT as the current client used for all operations."
(interactive (interactive
(list (completing-read "Client: " (p4-lowlevel-local-clients)))) (list (completing-read "Client: " (p4-lowlevel-local-clients))))
(p4-lowlevel-command-or-error `("set" ,(format "P4CLIENT=%s" client)))) (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)) (p4-lowlevel-login :status t))
(defun vc-p4-dir-extra-headers (dir) (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))) (let ((extra-info (p4-lowlevel-info :client vc-p4-client)))
(concat (concat
(propertize "Client :" 'face 'font-lock-type-face) (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)) (p4-lowlevel-rename old new))
(provide 'vc-p4) (provide 'vc-p4)
;;; vc-p4.el ends here