Update copyright year to 2014 by running admin/update-copyright.
[bpt/emacs.git] / lisp / vc / vc-dispatcher.el
index 95c1503..a0efe02 100644 (file)
@@ -1,6 +1,6 @@
-;;; vc-dispatcher.el -- generic command-dispatcher facility.
+;;; vc-dispatcher.el -- generic command-dispatcher facility.  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2008-201 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2014 Free Software Foundation, Inc.
 
 ;; Author:     FSF (see below for full credits)
 ;; Maintainer: Eric S. Raymond <esr@thyrsus.com>
@@ -182,32 +182,29 @@ Another is that undo information is not kept."
 
 (defvar vc-sentinel-movepoint)          ;Dynamically scoped.
 
-(defun vc-process-sentinel (p s)
-  (let ((previous (process-get p 'vc-previous-sentinel))
-        (buf (process-buffer p)))
+(defun vc--process-sentinel (p code)
+  (let ((buf (process-buffer p)))
     ;; Impatient users sometime kill "slow" buffers; check liveness
     ;; to avoid "error in process sentinel: Selecting deleted buffer".
     (when (buffer-live-p buf)
-      (when previous (funcall previous p s))
       (with-current-buffer buf
         (setq mode-line-process
               (let ((status (process-status p)))
                 ;; Leave mode-line uncluttered, normally.
                 (unless (eq 'exit status)
                   (format " (%s)" status))))
-        (let (vc-sentinel-movepoint)
+        (let (vc-sentinel-movepoint
+              (m (process-mark p)))
           ;; Normally, we want async code such as sentinels to not move point.
           (save-excursion
-            (goto-char (process-mark p))
-            (let ((cmds (process-get p 'vc-sentinel-commands)))
-              (process-put p 'vc-sentinel-commands nil)
-              (dolist (cmd cmds)
-                ;; Each sentinel may move point and the next one should be run
-                ;; at that new point.  We could get the same result by having
-                ;; each sentinel read&set process-mark, but since `cmd' needs
-                ;; to work both for async and sync processes, this would be
-                ;; difficult to achieve.
-                (vc-exec-after cmd))))
+            (goto-char m)
+            ;; Each sentinel may move point and the next one should be run
+            ;; at that new point.  We could get the same result by having
+            ;; each sentinel read&set process-mark, but since `cmd' needs
+            ;; to work both for async and sync processes, this would be
+            ;; difficult to achieve.
+            (vc-exec-after code)
+            (move-marker m (point)))
           ;; But sometimes the sentinels really want to move point.
           (when vc-sentinel-movepoint
            (let ((win (get-buffer-window (current-buffer) 0)))
@@ -226,7 +223,8 @@ Another is that undo information is not kept."
 (defun vc-exec-after (code)
   "Eval CODE when the current buffer's process is done.
 If the current buffer has no process, just evaluate CODE.
-Else, add CODE to the process' sentinel."
+Else, add CODE to the process' sentinel.
+CODE should be a function of no arguments."
   (let ((proc (get-buffer-process (current-buffer))))
     (cond
      ;; If there's no background process, just execute the code.
@@ -237,23 +235,21 @@ Else, add CODE to the process' sentinel."
      ((or (null proc) (eq (process-status proc) 'exit))
       ;; Make sure we've read the process's output before going further.
       (when proc (accept-process-output proc))
-      (eval code))
+      (if (functionp code) (funcall code) (eval code)))
      ;; If a process is running, add CODE to the sentinel
      ((eq (process-status proc) 'run)
       (vc-set-mode-line-busy-indicator)
-      (let ((previous (process-sentinel proc)))
-        (unless (eq previous 'vc-process-sentinel)
-          (process-put proc 'vc-previous-sentinel previous))
-        (set-process-sentinel proc 'vc-process-sentinel))
-      (process-put proc 'vc-sentinel-commands
-                   ;; We keep the code fragments in the order given
-                   ;; so that vc-diff-finish's message shows up in
-                   ;; the presence of non-nil vc-command-messages.
-                   (append (process-get proc 'vc-sentinel-commands)
-                           (list code))))
+      (letrec ((fun (lambda (p _msg)
+                      (remove-function (process-sentinel p) fun)
+                      (vc--process-sentinel p code))))
+        (add-function :after (process-sentinel proc) fun)))
      (t (error "Unexpected process state"))))
   nil)
 
+(defmacro vc-run-delayed (&rest body)
+  (declare (indent 0) (debug t))
+  `(vc-exec-after (lambda () ,@body)))
+
 (defvar vc-post-command-functions nil
   "Hook run at the end of `vc-do-command'.
 Each function is called inside the buffer in which the command was run
@@ -329,12 +325,14 @@ case, and the process object in the asynchronous case."
                               command squeezed))))
                (when vc-command-messages
                  (message "Running %s in background..." full-command))
-               ;;(set-process-sentinel proc (lambda (p msg) (delete-process p)))
+                ;; Get rid of the default message insertion, in case we don't
+                ;; set a sentinel explicitly.
+               (set-process-sentinel proc #'ignore)
                (set-process-filter proc 'vc-process-filter)
                (setq status proc)
                (when vc-command-messages
-                 (vc-exec-after
-                  `(message "Running %s in background... done" ',full-command))))
+                 (vc-run-delayed
+                  (message "Running %s in background... done" full-command))))
            ;; Run synchronously
            (when vc-command-messages
              (message "Running %s in foreground..." full-command))
@@ -351,9 +349,9 @@ case, and the process object in the asynchronous case."
                     (if (integerp status) (format "status %d" status) status)))
            (when vc-command-messages
              (message "Running %s...OK = %d" full-command status))))
-       (vc-exec-after
-        `(run-hook-with-args 'vc-post-command-functions
-                             ',command ',file-or-list ',flags))
+       (vc-run-delayed
+        (run-hook-with-args 'vc-post-command-functions
+                             command file-or-list flags))
        status))))
 
 (defun vc-do-async-command (buffer root command &rest args)
@@ -386,6 +384,22 @@ Display the buffer in some window, but don't select it."
        (set-window-start window new-window-start))
     buffer))
 
+(defvar compilation-error-regexp-alist)
+
+(defun vc-compilation-mode (backend)
+  "Setup `compilation-mode' after with the appropriate `compilation-error-regexp-alist'."
+  (require 'compile)
+  (let* ((error-regexp-alist
+          (vc-make-backend-sym backend 'error-regexp-alist))
+        (error-regexp-alist (and (boundp error-regexp-alist)
+                                 (symbol-value error-regexp-alist))))
+    (let ((compilation-error-regexp-alist error-regexp-alist))
+      (compilation-mode))
+    (set (make-local-variable 'compilation-error-regexp-alist)
+        error-regexp-alist)))
+
+(declare-function vc-dir-refresh "vc-dir" ())
+
 (defun vc-set-async-update (process-buffer)
   "Set a `vc-exec-after' action appropriate to the current buffer.
 This action will update the current buffer after the current
@@ -399,17 +413,17 @@ If the current buffer is a Dired buffer, revert it."
     (cond
      ((derived-mode-p 'vc-dir-mode)
       (with-current-buffer process-buffer
-       (vc-exec-after
-        `(if (buffer-live-p ,buf)
-             (with-current-buffer ,buf
-               (vc-dir-refresh))))))
+       (vc-run-delayed
+        (if (buffer-live-p buf)
+             (with-current-buffer buf
+               (vc-dir-refresh))))))
      ((derived-mode-p 'dired-mode)
       (with-current-buffer process-buffer
-       (vc-exec-after
-        `(and (buffer-live-p ,buf)
-              (= (buffer-modified-tick ,buf) ,tick)
-              (with-current-buffer ,buf
-                (revert-buffer)))))))))
+       (vc-run-delayed
+        (and (buffer-live-p buf)
+              (= (buffer-modified-tick buf) tick)
+              (with-current-buffer buf
+                (revert-buffer)))))))))
 
 ;; These functions are used to ensure that the view the user sees is up to date
 ;; even if the dispatcher client mode has messed with file contents (as in,
@@ -466,7 +480,7 @@ Used by `vc-restore-buffer-context' to later restore the context."
                         (vc-position-context (mark-marker))))
        ;; Make the right thing happen in transient-mark-mode.
        (mark-active nil))
-    (list point-context mark-context nil)))
+    (list point-context mark-context)))
 
 (defun vc-restore-buffer-context (context)
   "Restore point/mark, and reparse any affected compilation buffers.
@@ -505,6 +519,8 @@ ARG and NO-CONFIRM are passed on to `revert-buffer'."
 (make-variable-buffer-local 'vc-mode-line-hook)
 (put 'vc-mode-line-hook 'permanent-local t)
 
+(defvar view-old-buffer-read-only)
+
 (defun vc-resynch-window (file &optional keep noquery reset-vc-info)
   "If FILE is in the current buffer, either revert or unvisit it.
 The choice between revert (to see expanded keywords) and unvisit
@@ -575,25 +591,26 @@ NOT-URGENT means it is ok to continue if the user says not to save."
 
 ;; Set up key bindings for use while editing log messages
 
-(defun vc-log-edit (fileset mode)
+(defun vc-log-edit (fileset mode backend)
   "Set up `log-edit' for use on FILE."
   (setq default-directory
-       (with-current-buffer vc-parent-buffer default-directory))
+       (buffer-local-value 'default-directory vc-parent-buffer))
   (log-edit 'vc-finish-logentry
-           nil
+           t
            `((log-edit-listfun . (lambda ()
                                     ;; FIXME: Should expand the list
                                     ;; for directories.
                                     (mapcar 'file-relative-name
                                             ',fileset)))
-             (log-edit-diff-function . (lambda () (vc-diff nil))))
+             (log-edit-diff-function . vc-diff)
+             (log-edit-vc-backend . ,backend)
+             (vc-log-fileset . ,fileset))
            nil
            mode)
-  (set (make-local-variable 'vc-log-fileset) fileset)
   (set-buffer-modified-p nil)
   (setq buffer-file-name nil))
 
-(defun vc-start-logentry (files comment initial-contents msg logbuf mode action &optional after-hook)
+(defun vc-start-logentry (files comment initial-contents msg logbuf mode action &optional after-hook backend)
   "Accept a comment for an operation on FILES.
 If COMMENT is nil, pop up a LOGBUF buffer, emit MSG, and set the
 action on close to ACTION.  If COMMENT is a string and
@@ -604,7 +621,8 @@ entered COMMENT.  If COMMENT is t, also do action immediately with an
 empty comment.  Remember the file's buffer in `vc-parent-buffer'
 \(current one if no file).  Puts the log-entry buffer in major-mode
 MODE, defaulting to `log-edit-mode' if MODE is nil.
-AFTER-HOOK specifies the local value for `vc-log-after-operation-hook'."
+AFTER-HOOK specifies the local value for `vc-log-after-operation-hook'.
+BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer."
   (let ((parent
          (if (vc-dispatcher-browsing)
              ;; If we are called from a directory browser, the parent buffer is
@@ -619,7 +637,7 @@ AFTER-HOOK specifies the local value for `vc-log-after-operation-hook'."
     (set (make-local-variable 'vc-parent-buffer) parent)
     (set (make-local-variable 'vc-parent-buffer-name)
         (concat " from " (buffer-name vc-parent-buffer)))
-    (vc-log-edit files mode)
+    (vc-log-edit files mode backend)
     (make-local-variable 'vc-log-after-operation-hook)
     (when after-hook
       (setq vc-log-after-operation-hook after-hook))