;; Simple Emacs <-> Perforce Integration ;; ;; $Id: //depot/user/rv/xemacs-lisp/lisp/p4/p4.el#4$ ;; Applied the GNU G.P.L. to this file - rv 3/27/1997 ;; Programs for Emacs <-> Perforce Integration. ;; Copyright (C) 1996, 1997 Eric Promislow ;; Copyright (C) 1997, 1998 Rajesh Vaidheeswarran ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2 of the License, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;; ;; If you have any problems to report, or suggestions, please send them ;; to rv@fore.com ;; ;; WARNING: ;; -------- ;; ;; % p4 edit foo.c ;; ... make changes to foo.c in emacs ;; % p4 submit ;; ... keep the writable copy of foo.c in emacs. Start making changes ;; to it. Discover that you can't save it. If you do M-x:p4-edit, ;; you'll lose your changes. You need to do a 'p4 edit' at the ;; command-line. ;; ;; Original Functions: (Contributed by Eric Promislow) ;; p4-exec-p4 (not exported) ;; p4-buffer-action (not exported) ;; p4-edit ;; p4-revert ;; p4-diff ;; Modified Original Functions ;; p4-buffer-action - modified to support optional args to be passed ;; p4-edit - added help ;; p4-revert - added help ;; p4-diff - added help ;; Added Functions: ;; p4-add ;; p4-delete ;; p4-diff2 ;; p4-filelog ;; p4-files ;; p4-refresh ;; p4-noinput-exec-p4 (not exported) ;; p4-noinput-buffer-action (not exported) ;; p4-get ;; p4-have ;; p4-help ;; p4-info ;; p4-integ ;; p4-opened ;; p4-users ;; p4-where ;; p4-async-commands (not exported) ;; p4-change ;; p4-client ;; p4-submit ;; p4-user ;; p4-get-client-name ;; p4-set-client-name ;; p4-find-file-hook (not exported) ;; p4-is-vc (not exported) ;; p4-check-mode (not exported) ;; p4-menu-add ;; NOTES: ;; ------ ;; ;; It is best if you take this file and byte compile it. To do that, you ;; need to do the following: ;; ;; % emacs -batch -f batch-byte-compile /full/path/to/file/p4.el ;; ;; This creates a binary file p4.elc in the path. Add the path to your ;; load-path variable in .emacs like this: ;; ;; (setq load-path (cons "/full/path/to/file" load-path)) ;; ;; Then add the library like this: ;; ;; (load-library "p4") ;; ;; NEW Additions in the 1.2 Release: ;; ;; Functions: ;; ----------- ;; p4-set-notify-list ;; p4-get-notify-list ;; p4-notify ;; p4-emacs-version ;; ;; Modifications: ;; -------------- ;; The following functions now accept prefix argument (C-u) to specify ;; optional argument like -n or ..., etc. ;; ;; p4-files, p4-refresh, p4-get, p4-have, p4-integ, p4-opened, p4-users, ;; p4-where. ;; ;; Now supports RCS/CVS type vc revision level display on mode line, and ;; menu bar support. ;; ;; NEW Additions in the 1.3 Release: ;; --------------------------------- ;; 1. "/dev/null" replaced with grep-null-device for NT compatibility ;; ;; 2. p4-is-vc now returns the version number if p4 owns the file ;; ;; 3. added p4-ediff which uses ediff to allow more interactive diffing ;; ;; 4. removed p4-noinput-exec-p4 because it was the same as p4-exec-p4. ;; p4-noinput-buffer-action and p4-buffer-action should probably be one ;; routine but I left them alone for now. ;; ;; 5. changed kill-region to delete-region to avoid kill ring clutter ;; ;; 6. replaced 'nil with nil and 't with t ;; ;; 7. removed unused p4-output-buffer ;; ;; New in 1.4 Release ;; ------------------ ;; Split p4.el for GNU Emacs and XEmacs ;; ;; New in 1.5 Release ;; ------------------ ;; 1. Make p4.el work with both GNU Emacs and XEmacs. The problem is that ;; this might cause some compilation warnings since some functions are ;; mutually exclusive and don't exist on both. But, testing has proved no ;; conflicts or problems that arise due to this. ;; 2. Cleanup comments and a document some more functions. ;; Find out what type of emacs we are running in. We will be using this ;; quite a few times in this program. (defvar p4-running-gnuemacs (string-match "GNU Emacs" (emacs-version))) (defvar p4-running-xemacs (string-match "XEmacs\\|Lucid" (emacs-version))) ;; This can be set to wherever 'p4' lies using p4-set-p4-executable (defvar p4-executable "/usr/swlocal/bin/p4") (cond (p4-running-xemacs ;; This can be set to wherever the appropriate client lies using ;; p4-set-gnu-client (defvar p4-gnuclient "/usr/swlocal/bin/gnuclient") (defvar p4-null-device "/dev/null")) (p4-running-gnuemacs ;; This can be set to wherever the appropriate client lies using ;; p4-set-gnu-client (defvar p4-gnuclient "/usr/swlocal/bin/emacsclient") (defvar p4-null-device grep-null-device))) (defvar p4-output-buffer-name "*P4 Output*") (defvar p4-oldclt (getenv "P4CLIENT")) (defvar p4-value 'no) (defvar p4-old-notify-list (getenv "P4NOTIFY")) (defvar p4-notify-list (getenv "P4NOTIFY")) ;; This can be set with p4-set-notifier (defvar p4-notifier "/usr/swlocal/bin/mailch") (defvar p4-notify nil) (defvar p4-emacs-version "1.5") ;; Now add a hook to find-file-hooks (add-hook 'find-file-hooks 'p4-find-file-hook) ;; Tell Emacs about this new kind of minor mode (if (not (assoc 'p4-mode minor-mode-alist)) (setq minor-mode-alist (cons '(p4-mode p4-mode) minor-mode-alist))) (make-variable-buffer-local 'p4-mode) (put 'p4-mode 'permanent-local t) ;; A generic function that we use to execute p4 commands (defun p4-exec-p4 (output-buffer args &optional clear-output-buffer) (if clear-output-buffer (progn (set-buffer output-buffer) (delete-region (point-min) (point-max)))) (apply 'call-process (append (list p4-executable p4-null-device output-buffer nil) ; update display? args))) ;; A generic function to take action of a buffer for interactive p4 commands (defun p4-buffer-action (cmd do-revert show-output &optional argument &optional argument1) (save-excursion (if (not (stringp cmd)) (error "p4-buffer-action: Command not a string.")) (save-excursion (if (and argument argument1) (p4-exec-p4 (get-buffer-create p4-output-buffer-name) (list cmd argument argument1) t) (p4-exec-p4 (get-buffer-create p4-output-buffer-name) (list cmd (buffer-file-name)) t))) (if do-revert (revert-buffer t t)) (if show-output (progn (delete-other-windows) (display-buffer p4-output-buffer-name t))))) ;; We use the noinput version for commands like p4 opened, p4 get etc. ;; which don't take any input file name. (defun p4-noinput-buffer-action (cmd do-revert show-output &optional argument) (save-excursion (if (not (stringp cmd)) (error "p4-noinput-buffer-action: Command not a string.")) (save-excursion (if argument (p4-exec-p4 (get-buffer-create p4-output-buffer-name) (append (list cmd) argument) t) (p4-exec-p4 (get-buffer-create p4-output-buffer-name) (list cmd) t))) (if do-revert (revert-buffer t t)) (if show-output (progn (delete-other-windows) (display-buffer p4-output-buffer-name t))))) ;; The p4 edit command (defun p4-edit (show-output) "To open the current depot file for edit, type M-x `p4-edit' Open or re-open an existing file for edit. If file is already open for edit or delete then it is reopened for edit and moved into the specified change number (or 'default' change if no change number is given.) If -t type is given the file is explicitly opened as the specified file type, which may be text, ltext, xtext, binary, xbinary, ktext kxtext, symlink, or resource. Not all types are supported on all operating systems. See the Users' Guide for a description of file types. If no type is specified, the type is determined automatically by examination of the file's contents and execution permission bits." (interactive "P") (p4-buffer-action "edit" t show-output) (p4-check-mode)) ;; The p4 revert command (defun p4-revert (show-output) (interactive "P") (if (yes-or-no-p "Really revert changes? ") (p4-buffer-action "revert" t show-output)) (p4-check-mode)) ;; The p4 diff command (defun p4-diff () "To diff the current file and topmost depot version, type M-x `p4-diff' Run diff (on the client) of a client file against the corresponding revision in the depot. The file is only compared if the file is opened for edit or the revision provided with the file argument is not the same as the revision had by the client. If no file argument is given, diff all open files. This can be used to view pending changes. The -f flag forces a diff for every file, regardless of whether they are opened or if the client has the named revision. This can be used to verify the client contents. The -s flag outputs reduces the output of diff to the names of files satisfying the following criteria: -sa Opened files that are different than the revision in the depot, or missing. -sd Unopened files that are missing on the client. -se Unopened files that are different than the revision in the depot. -sr Opened files that are the same as the revision in the depot." (interactive) (p4-buffer-action "diff" nil t)) ;; The p4 diff2 command (defun p4-diff2 (version1 version2 ) " diff2 -- Display diff of two depot files. When visiting a depot file, type M-x `p4-diff2' and enter the versions. Example: (find-file \"/us/rv/tag/main/Construct\") M-x p4-diff2 First Version to diff: 113 Second Version to diff: 100 Will produce the diff between the two versions in the output buffer *P4 Output* Run diff (on the server) of two files in the depot. Both files may optionally include a revision specification; the default is to compare the head revision. Wildcards may be used, but they must match between file1 and file2 and they must be escaped from the user's shell by quoting or with backslashes (\). The -d flag allows you to pass flags to the underlying diff program. -dc passes the -c (context diff) flag. -dn passes the the -n (rcs diff) flag. Other diff flags are not supported." (interactive "sFirst Version to diff: \nsSecond Version to diff: ") (make-variable-buffer-local 'p4-diff-version1) (make-variable-buffer-local 'p4-diff-version2) (defvar p4-diff-version1 nil) (defvar p4-diff-version2 nil) (setq p4-diff-version1 (concat (buffer-file-name) "#" version1)) (setq p4-diff-version2 (concat (buffer-file-name) "#" version2)) (p4-buffer-action "diff2" nil t p4-diff-version1 p4-diff-version2)) ;; p4-ediff for all those who diff using ediff (defun p4-ediff () "Use ediff to compare file with its original client version" (interactive) (require 'ediff) (p4-noinput-buffer-action "print" nil nil (list "-q" (concat (buffer-file-name) "#have"))) (let ((local (current-buffer)) (depot (get-buffer-create p4-output-buffer-name))) (ediff-buffers local depot `((lambda () (make-local-variable 'ediff-cleanup-hook) (setq ediff-cleanup-hook (cons (lambda () (kill-buffer ,depot)) ediff-cleanup-hook))))))) ;; The p4 add command (defun p4-add () "To add the current file to the depot, type M-x `p4-add' Open a new file for adding to the depot. If the file exists on the client it is read to determine if it is text or binary. If it does not exist it is assumed to be text. The file must either not exist in the depot, or it must be deleted at the current head revision. Files may be deleted and re-added arbitrarily. If the -c flag is given the open files are associated with the specified pending change number; otherwise the open files are associated with the current 'default' change. If file is already open it is moved into the specified pending change. It is not permissible to reopen a file for add unless it was already open for add. If -t type is given the file is explicitly opened as the specified file type, which may be text, ltext, xtext, binary, xbinary, ktext kxtext, symlink, or resource. Not all types are supported on all operating systems. See the Users' Guide for a description of file types. If no type is specified, the type is determined automatically by examination of the file's contents and execution permission bits." (interactive) (if (not (p4-is-vc)) (progn (p4-buffer-action "add" nil t) (setq p4-mode " P4:Add" ) (force-mode-line-update)))) ;; The p4 delete command (defun p4-delete () "To delete the current file from the depot, type M-x `p4-delete' Opens a file that currently exists in the depot for deletion. If the file is present on the client it is removed. If a pending change number is given with the -c flag the opened file is associated with that change, otherwise it is associated with the 'default' pending change. If file is already open it is reopened for delete and moved into the specified pending change (or 'default' change if no change number is given.) Files that are deleted generally do not appear on the have list." (interactive) (if (yes-or-no-p "Really delete from depot? ") (if (p4-is-vc) (p4-buffer-action "delete" nil t)) (p4-check-mode))) ;; The p4 filelog command (defun p4-filelog () "To view a history of the changes made to the current file, type M-x `p4-filelog' List the revision history of the files named, working backwards from the latest revision to the most recent revision 'added'. If file is given as a client file, the depot file last gotten is listed. The -l flag produces long output with the full text of the change descriptions." (interactive) (p4-buffer-action "filelog" nil t)) ;; The p4 files command (defun p4-files () "files -- List files in the depot To pass optional args [file ...] use the prefix argument (i.e.) C-u M-x `p4-files' List files named or matching wild card specification. Display shows depot file name, revision, file type, change action and change number of the current head revision. If client file names are given as arguments the view mapping is used to list the corresponding depot files." (interactive) (if current-prefix-arg (progn (setq args (list (read-string "Optional Args: "))) (p4-noinput-buffer-action "files" nil t args)) (p4-buffer-action "files" nil t))) ;; The p4 refresh command (defun p4-refresh () "refresh -- Refresh the contents of an unopened file To pass optional args [file ...] use the prefix argument (i.e.) C-u M-x `p4-refresh' Refresh replaces the files with their contents from the depot. Refresh only refreshes unopened files; opened files must be reverted. This command requires naming files explicitly." (interactive) (if current-prefix-arg (progn (setq args (list (read-string "Optional Args: "))) (p4-noinput-buffer-action "refresh" nil t args)) (p4-buffer-action "refresh" nil t))) ;; The p4 get/sync command (defun p4-get () "To synchronise the local view with the depot, type M-x `p4-get' To pass optional args [-n] [file ...] use the prefix argument (i.e.) C-u M-x `p4-get' Synchronize a client with its view for the files named, or for the entire client if no files named. Get handles the case where files have been updated in the depot and need to be brought up-to-date on the client as well as the case when the view itself has changed. Depot files in the clients view not currently gotten on the client will be added. Client files that were gotten from the depot but that are no longer in the clients view (or have been deleted in the depot) will be deleted from the client. Client files that are still in the client view but which have been updated in the depot are replaced by the needed revision from the depot. If file gives a revision specifier, then retrieve the revision so indicated. The client view is used to map client file names to depot file names and vice versa. If -n is given show what revisions would need to be gotten to synchronize the client with the view, but do not actually get the files. If no files are named show the result for the entire client view." (interactive) (if current-prefix-arg (setq args (list (read-string "Optional Args: "))) (setq args (list ""))) (p4-noinput-buffer-action "get" nil t args)) ;; The p4 have command (defun p4-have () "To list revisions last gotten, type M-x `p4-have' To pass optional args [file ...] use the prefix argument (i.e.) C-u M-x `p4-have' List revisions of named files that were last gotten from the depot. If no file name is given list all files gotten on this client." (interactive) (if current-prefix-arg (setq args (list (read-string "Optional Args: "))) (setq args (list ""))) (p4-noinput-buffer-action "have" nil t args)) ;; The p4 help command (defun p4-help (arg) "To print help message , type M-x `p4-help' Print a help message about command. If no command name is given print a general help message about Perforce and give a list of available client commands." (interactive "sHelp on which command: ") (if (string< "" arg) (p4-noinput-buffer-action "help" nil t (list arg)) (p4-noinput-buffer-action "help" nil t))) ;; The p4 info command (defun p4-info () "To print out client/server information, type M-x `p4-info' Info dumps out what the server knows about the client (the user name, the client name, and the client directory) and some server information (the server's address, version, and license data)." (interactive) (p4-noinput-buffer-action "info" nil t)) ;; The p4 integrate command (defun p4-integ (branch) "To schedule integrations between branches, type M-x `p4-integ' To pass optional args [-n -r] [-c change#] [file ...] use the prefix argument (i.e.) C-u M-x `p4-integ' Integ determines what integrations are necessary between related files, according to the branch named and its view. These integrations, represented by a list of revisions of branch source files which need to be merged into the related branch target file, are scheduled for later action. The actual merge and any necessary conflict resolution is performed using the resolve command. The -n flag displays what integrations would be necessary but does not schedule them. A branch name is required. If the -r flag is present, the mappings in the branch view are reversed, with the target files and source files exchanging place. If no file names are given then the entire branch view is examined for needed integrations. Files that are not mapped in the client's view are ignored. Files scheduled for integration are opened for the appropriate action in the default change. If -c change# is given the files are opened in the numbered pending change." (interactive "sP4 Branch Name: ") (if current-prefix-arg (setq args (list "-b" branch (read-string "Optional Args: "))) (setq args (list "-b" branch))) (p4-noinput-buffer-action "integ" nil t args)) ;; The p4 opened command (defun p4-opened () "To display list of files opened for pending change, type M-x `p4-opened' To pass optional args [-a] [file ...] use the prefix argument (i.e.) C-u M-x `p4-opened' Shows files currently opened for pending changes or indicates for the specified individual files whether they are currently opened. If no file names are given, all files open on the current client are listed. The -a flag lists opened files in all clients." (interactive) (p4-noinput-buffer-action "opened" nil t)) ;; The p4 users command (defun p4-users () "To display list of known users, type M-x `p4-users' To pass optional args [user ...] use the prefix argument (i.e.) C-u M-x `p4-users' Reports the list of all users, or those users matching the argument, currently known to the system. The report includes the last time each user accessed the system." (interactive) (if current-prefix-arg (setq args (list (read-string "Optional Args: "))) (setq args (list ""))) (p4-noinput-buffer-action "users" nil t args)) ;; The p4 where command (defun p4-where () "To show how local file names map into depot names, type M-x `p4-where' To pass optional args [file ...] use the prefix argument (i.e.) C-u M-x `p4-where' Where shows how the named files map through the client map into the depot. If no file is given, the mapping for '...' (all files in the current directory and below) is shown." (interactive) (if current-prefix-arg (setq args (list (read-string "Optional Args: "))) (setq args (list ""))) (p4-noinput-buffer-action "where" nil t args)) ;; A generic function to do asynchronous p4 commands. All commands that use ;; this would use the emacsclient or gnuclient as their editor using the ;; server running inside emacs. (defun p4-async-commands (value &optional clear-output-buffer) (setenv "EDITOR" p4-gnuclient) (if clear-output-buffer (progn (set-buffer (get-buffer-create p4-output-buffer-name)) (delete-region (point-min) (point-max)))) (save-excursion (p4-emacs-server-start) (start-process value p4-output-buffer-name p4-executable value) (display-buffer p4-output-buffer-name t))) ;; The p4 change command (defun p4-change () "To edit the change specification, type M-x `p4-change' To pass optional args [-d | -o] [ change# ] use the prefix argument (i.e.) C-u M-x `p4-change' Creates a new change description with no argument or edit the text description of an existing change if a change number is given. To associate or remove files from a pending change use the open commands (edit, add, delete) or revert. The -d flag discards a pending change, but only if it has no opened files and no pending fixes associated with it. Use 'opened -a' to report on opened files and 'reopen' to move them to another change. Use 'fixes -c change#' to report on pending fixes and 'fix -d -c change# jobs...' to delete pending fixes. The change can only be deleted by the user and client who created it. The -o flag causes the change specification to be written to the standard output. The user's editor is not invoked. The -i flag causes a change specification to be read from the standard input. The user's editor is not invoked." (interactive) (p4-async-commands "change")) ;; The p4 client command (defun p4-client () "To edit a client specification , type M-x `p4-client' With no argument client creates a new client view specification or edits an existing client specification. The client name is taken from the environment variable $P4CLIENT if set, or else from the current host name. The specification form is put into a temporary file and the editor (given by the environment variable $EDITOR) is invoked. If a name is given, the specification of the named client is displayed read-only. The specification form contains the following fields: Client: The client name (read only.) Date: The date specification was last modified (read only.) Description: A short description of the client (optional). Root: The root directory of the client file workspace (given in local file system syntax), under which all client files will be placed. If you change this, you must physically relocate any files as well. View: What files you want to see from the depot and how they map to locations on the client. The left hand side specifies a depot path, which must begin with //depot/. The right hand side gives the corresponding client path, given in canonical Perforce file syntax. On expansion to an actual local client file name the initial //client/ is replaced by the Root value, given above. You may use wildcards: ... matches any characters including * matches any character except / %1 to %9 like *, used to associate wild cards Wildcarding must be congruent in both the client and depot paths. You may have any number of view entries. A new view takes effect on the next 'get'. Normally, new clients are created with a default view that maps all depot files onto the client. The -t flag uses the view from the named template client as a default instead. The -d flag causes the named client to be deleted. The -o flag causes the named client specification to be written to the standard output. The user's editor is not invoked. The -i flag causes a client specification to be read from the standard input. The user's editor is not invoked." (interactive) (p4-async-commands "client")) ;; The p4 submit command (defun p4-submit () "To submit a pending change to the depot, type M-x `p4-submit' Submit commits a pending change with its associated files to the depot. With no argument submit sends the 'default' change. With the -c flag the designated pending change is sent. Before committing the change submit locks all associated files not already locked. If any file cannot be locked the change is aborted. If submit is sending the default change it first provides the user with a dialog similar to 'p4 change' so the user can compose a change description. In this dialog the user is presented with the list of open files in change 'default'. Files may be deleted from this list but they cannot be added. (Use an open command (open, edit, add, delete) to add additional files to a change or to move files between changes.) If the submit fails for any reason the files are left open in a newly created pending change. Submit is guaranteed to be atomic. Either all files will be updated in the depot as a unit or none will be. The -i flag causes a change specification to be read from the standard input. The user's editor is not invoked." (interactive) (p4-async-commands "submit" t)) ;; The p4 user command (defun p4-user () "To create or edit a user specification, type M-x `p4-user' Create a new user specification or edit an existing user specification. The specification form is put into a temporary file and the editor (given by the environment variable $EDITOR) is invoked. Normally, a user specification is created automatically the first time the user invokes any client command that can update the depot. The 'user' command is generally used to edit the user's reviewing subscription list for change review. The user specification form contains the following fields: User: The user name (read only). Email: The user's email address (user@client default). Update: The date the specification was last modified (read only). Access: The date the user last issued a client command. FullName: The user's real name. Reviews: The subscription list for change review. You may use wildcards: ... matches any characters including * matches any character except / There may be any number of review lines. The -d flag deletes the named user, but only if the user is not the owner of any branches, clients, jobs, labels, or opened files. The -o flag causes the named user specification to be written to the standard output. The user's editor is not invoked. The -i flag causes a user specification to be read from the standard input. The user's editor is not invoked." (interactive) (p4-async-commands "user")) ;; A function to get the current P4 client name (defun p4-get-client-name () "To get the current value of the environment variable P4CLIENT, type M-x `p4-get-client-name' This will be the current client that is in use for access through Emacs P4." (interactive) (message "P4CLIENT is %s" (getenv "P4CLIENT"))) ;; A function to set the current P4 client name (defun p4-set-client-name (p4-new-client-name) "To set the current value of P4CLIENT, type M-x `p4-set-client-name' This will change the current client from the previous client to the new given value. Setting this value to `nil' would disable P4 Version Checking." (interactive (list (let ((symbol (read-string "Change Client to: " p4-oldclt))) (if (equal symbol "") p4-oldclt symbol)))) (if (equal p4-new-client-name "nil") (progn (setenv "P4CLIENT" nil) (message "P4 Version check disabled. Set a valid client name to enable.")) (progn (setenv "P4CLIENT" p4-new-client-name) (setq p4-oldclt p4-new-client-name) (message "P4CLIENT changed to %s" p4-new-client-name)))) ;; The find-file hook for p4. (defun p4-find-file-hook () "To check while loading the file, if it is a P4 version controlled file." (if (getenv "P4CLIENT") (p4-check-mode))) ;; A function to check if the file being opened is version controlled by p4. (defun p4-is-vc () "If a file is controlled by P4 then return version else return nil" (save-excursion (get-buffer-create p4-output-buffer-name) (set-buffer p4-output-buffer-name) (delete-region (point-min) (point-max))) (if (zerop (call-process p4-executable nil p4-output-buffer-name nil "files" buffer-file-name)) (progn (save-excursion (set-buffer p4-output-buffer-name) (goto-char (point-min)) (if (re-search-forward "#[0-9]+" (point-max) t) (substring (match-string 0) 1) nil))) nil)) ;; set keymap. We use the C-x P Keymap for all perforce commands (setq p4-prefix-map (lookup-key global-map "\C-xP")) (if (not (keymapp p4-prefix-map)) (progn (setq p4-prefix-map (make-sparse-keymap)) (define-key global-map "\C-xP" p4-prefix-map) (define-key p4-prefix-map "a" 'p4-add) (define-key p4-prefix-map "c" 'p4-client) (define-key p4-prefix-map "d" 'p4-diff2) (define-key p4-prefix-map "e" 'p4-edit) (define-key p4-prefix-map "f" 'p4-filelog) (define-key p4-prefix-map "g" 'p4-get-client-name) (define-key p4-prefix-map "G" 'p4-get) (define-key p4-prefix-map "h" 'p4-help) (define-key p4-prefix-map "i" 'p4-info) (define-key p4-prefix-map "n" 'p4-notify) (define-key p4-prefix-map "o" 'p4-opened) (define-key p4-prefix-map "r" 'p4-revert) (define-key p4-prefix-map "R" 'p4-refresh) (define-key p4-prefix-map "s" 'p4-set-client-name) (define-key p4-prefix-map "S" 'p4-submit) (define-key p4-prefix-map "u" 'p4-user) (define-key p4-prefix-map "v" 'p4-emacs-version) (define-key p4-prefix-map "x" 'p4-delete) (define-key p4-prefix-map "=" 'p4-diff) (define-key p4-prefix-map "-" 'p4-ediff))) ;; For users interested in notifying a change, a notification list can be ;; set up using this function. (defun p4-set-notify-list (p4-new-notify-list) " To set the current value of P4NOTIFY, type M-x `p4-set-notify-list' This will change the current notify list from the existing list to the new given value. Enter `nil' to disable notification." (interactive (list (let ((symbol (read-string "Change Notification List to: " p4-notify-list))) (if (equal symbol "") p4-notify-list symbol)))) (setq p4-old-notify-list p4-notify-list) (if (equal p4-new-notify-list "nil") (progn (setenv "P4NOTIFY" nil) (setq p4-notify-list nil) (setq p4-notify nil)) (progn (setenv "P4NOTIFY" p4-new-notify-list) (setq p4-notify-list p4-new-notify-list) (setq p4-notify t))) (message "Notification list changed from '%s' to '%s'" p4-old-notify-list p4-notify-list)) ;; To get the current notification list. (defun p4-get-notify-list () "To get the current value of the environment variable P4NOTIFY, type M-x `p4-get-notify-list' This will be the current notification list that is in use for mailing change notifications through Emacs P4." (interactive) (message "P4NOTIFY is %s" p4-notify-list)) ;; Function to actually notify the users. Unfortunately, there are ;; limitations to this. It uses an external program to make the ;; notification, and since Emacs is not threaded, we can't really do ;; autonotification without going through pains, since p4-submit calls an ;; async process. The closest thing would be a diligent user who notifies ;; voluntarily after submitting a change. (defun p4-notify (users) "To notify a list of users of a change submission. Since Emacs is not threaded, there is no good way to make this happen intelligently. So, `p4-notify' is supposed to be used only after you submit `p4-submit' or `C-x P S' a change _from_ Emacs using the Emacs-P4 Interface. `p4-notify' will look for a submitted change returned from the submit command. Any external program can be used to actually mail the change description, and the default is a perl-based program `mailch' - mail change description." (interactive (list (let ((symbol (read-string "Notify whom? " p4-notify-list))) (if (equal symbol "") p4-notify-list symbol)))) (set-buffer (get-buffer-create p4-output-buffer-name)) (goto-char (point-min)) (if (re-search-forward "[0-9]+.*submitted" (point-max) t) (progn (p4-set-notify-list users) (setq p4-matched-change (substring (match-string 0) 0 -9)) (start-process p4-matched-change nil p4-notifier (concat p4-matched-change " " users))) (message "No Changes Submitted." users))) ;; Function to return the current version. (defun p4-emacs-version () "Return the current Emacs-P4 Integration version." (interactive) (cond (running-xemacs (message "XEmacs-P4 Integration v%s" p4-emacs-version)) (running-gnuemacs (message "GNU Emacs-P4 Integration v%s" p4-emacs-version)))) ;; To set the path to the p4 executable (defun p4-set-p4-executable (p4-exe-name) "Set the path to the correct P4 Executable." (interactive "fFull path to your P4 executable: " ) (setq p4-executable p4-exe-name)) ;; To set the path to the emacsclient/gnuclient. (defun p4-set-gnu-client (p4-gnu-client) "Set the path to the correct Emacs client. This would be 'emacsclient' for GNU Emacs and 'gnuclient' for XEmacs " (interactive "fFull path to your GNU client: " ) (setq p4-gnuclient p4-gnu-client)) ;; To set the path to the mailch notification program (if it exists). (defun p4-set-notifier (p4-notify-name) "Set the path to the correct mailch." (interactive "fFull path to your P4 Notifier (mailch): " ) (setq p4-notifier p4-exe-name)) ;; To start the correct emacs server depending on the type of emacs. (defun p4-emacs-server-start () "To Start the correct server depending on type of Emacs" (cond (running-xemacs (if (not (gnuserv-running-p)) (gnuserv-start))) (running-gnuemacs (if (not server-process) (server-start))))) ;; To check if the current buffer's modeline and menu need to be altered (defun p4-check-mode () "Check to see whether we should export the menu map to this buffer." (setq p4-vc-check (p4-is-vc)) (if p4-vc-check (progn ;; I have not been able to get the Menu working in GNU Emacs ;; successfully. :( So, disable menu add for GNU Emacs (cond (running-xemacs (p4-menu-add))) (setq p4-mode (concat " P4:" p4-vc-check))) (setq p4-mode nil)) (force-mode-line-update)) ;; I separated the menu support for GNU Emacs and XEmacs since they seemed ;; to differ considerably. (cond (running-xemacs ;; Menu Support for XEmacs (require 'easymenu) (defun p4-mode-menu (modestr) (let ((m '(["Add" p4-add (and buffer-file-name (not p4-mode))] ["Edit" p4-edit (and buffer-file-name p4-mode)] ["Revert" p4-revert (and buffer-file-name p4-mode)] ["Filelog" p4-filelog (and buffer-file-name p4-mode)] ["Diff" p4-diff (and buffer-file-name p4-mode)] ["Diff2" p4-diff2 (and buffer-file-name p4-mode)] ["Delete" p4-delete (and buffer-file-name p4-mode)] ["Ediff" p4-ediff (and buffer-file-name p4-mode)] ["Submit" p4-submit (and buffer-file-name p4-mode)] ))) (cons modestr m))) (defun p4-menu-add () "To add the P4 menu bar button for files that are already not in the P4 depot" (interactive) (easy-menu-add (p4-mode-menu "P4")))) (running-gnuemacs ;; Attempted Menu support for GNU Emacs ;; (make-variable-buffer-local 'p4-mode-map) ;; (put 'p4-mode-map 'permanent-local nil) (defvar p4-mode-map nil "Local keymap for p4 minor mode") (if p4-mode-map nil ;;(define-key p4-menu-map [show-files] ;; '("Show Files under VC" . (p4-directory t))) (let ((map (make-sparse-keymap))) (define-key map [menu-bar p4] (cons "P4" (make-sparse-keymap "P4"))) (define-key map [menu-bar p4 p4-submit] '("Submit Changes" . p4-submit)) (define-key map [menu-bar p4 p4-notify] '("Notify Change Submission" . p4-notify)) (define-key map [menu-bar p4 p4-set-client-name] '("Set P4 Client" . p4-set-client-name)) (define-key map [menu-bar p4 p4-get-client-name] '("Get P4 Client" . p4-get-client-name)) (define-key map [menu-bar p4 separator1] '("----")) (define-key map [menu-bar p4 p4-info] '("Info" . p4-info)) (define-key map [menu-bar p4 p4-help] '("Help" . p4-help)) (define-key map [menu-bar p4 separator2] '("----")) (define-key map [menu-bar p4 p4-delete] '("Delete File" . p4-delete)) (define-key map [menu-bar p4 separator3] '("----")) (define-key map [menu-bar p4 p4-diff] '("Compare with Last Version" . p4-diff)) (define-key map [menu-bar p4 p4-diff2] '("Compare 2 Versions" . p4-diff2)) (define-key map [menu-bar p4 p4-filelog] '("File Log" . p4-filelog)) (define-key map [menu-bar p4 separator4] '("----")) (define-key map [menu-bar p4 p4-revert-buffer] '("Revert to Last Version" . p4-revert)) (define-key map [menu-bar p4 p4-add] '("Add" . p4-add)) (define-key map [menu-bar p4 p4-edit] '("Edit" . p4-edit)) (setq p4-mode-map map))) (put 'p4-add 'menu-enable '(and buffer-file-name (not p4-mode))) (put 'p4-edit 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-revert 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-filelog 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-diff2 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-diff 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-delete 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-ediff 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-submit 'menu-enable '(and buffer-file-name p4-mode)) (put 'p4-notify 'menu-enable '(and buffer-file-name p4-mode)) (defun p4-menu-add () "To add the P4 menu bar button for files that are already not in the P4 depot" (interactive) (setq map (copy-keymap p4-mode-map)) (setq map (cons 'keymap (cons map (cons 'keymap (current-local-map))))) (use-local-map map))))