-;;; vc-dispatcher.el -- generic command-dispatcher facility.
+;;; vc-dispatcher.el -- generic command-dispatcher facility. -*- lexical-binding: t -*-
-;; Copyright (C) 2008-2011 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>
;;
;; When the client mode adds a local vc-mode-line-hook to a buffer, it
;; will be called with the buffer file name as argument whenever the
-;; dispatcher resynchs the buffer.
+;; dispatcher resyncs the buffer.
;; To do:
;;
(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)))
(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.
((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
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))
(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)
(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
(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,
(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.
(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
(kill-buffer (current-buffer)))))
(declare-function vc-dir-resynch-file "vc-dir" (&optional fname))
-(declare-function vc-string-prefix-p "vc" (prefix string))
(defun vc-resynch-buffers-in-directory (directory &optional keep noquery reset-vc-info)
"Resync all buffers that visit files in DIRECTORY."
(dolist (buffer (buffer-list))
(let ((fname (buffer-file-name buffer)))
- (when (and fname (vc-string-prefix-p directory fname))
+ (when (and fname (string-prefix-p directory fname))
(with-current-buffer buffer
(vc-resynch-buffer fname keep noquery reset-vc-info))))))
;; 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
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
(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))
(funcall log-operation
log-fileset
log-entry))
- ;; Remove checkin window (after the checkin so that if that fails
- ;; we don't zap the log buffer and the typing therein).
- ;; -- IMO this should be replaced with quit-window
- (cond ((and logbuf vc-delete-logbuf-window)
- (delete-windows-on logbuf (selected-frame))
- ;; Kill buffer and delete any other dedicated windows/frames.
- (kill-buffer logbuf))
- (logbuf
- (with-selected-window (or (get-buffer-window logbuf 0)
- (selected-window))
- (with-current-buffer logbuf
- (bury-buffer)))))
+
+ ;; Quit windows on logbuf.
+ (cond
+ ((not logbuf))
+ (vc-delete-logbuf-window
+ (quit-windows-on logbuf t (selected-frame)))
+ (t
+ (quit-windows-on logbuf nil 0)))
+
;; Now make sure we see the expanded headers
(when log-fileset
(mapc