From: Robin Green <[email protected]>
To: [email protected]
Subject: Strange bug in xdarcs.el
User-Agent: Wanderlust/2.15.7 (Almost Unreal) SEMI/1.14.6 (Maruoka)
FLIM/1.14.8 (=?UTF-8?B?U2hpasWN?=) APEL/10.7 Emacs/23.1
(i386-redhat-linux-gnu) MULE/6.0 (HANACHIRUSATO)
Organization: Swansea University
MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka")
Content-Type: multipart/mixed; boundary="Multipart_Sun_Nov__1_08:47:41_2009-1"
--Multipart_Sun_Nov__1_08:47:41_2009-1
Content-Type: text/plain; charset=US-ASCII
I don't think this is a bug in darcs, but maybe someone can give some
insight.
I'm trying to use xdarcs.el (patched by me, as attached, to append
'-q' to all commands to avoid confusing xdarcs with progress output
from darcs) as an emacs front-end to darcs record. Everything goes
fine, until xdarcs talks to darcs. Here is the complete contents of
the *darcs output* buffer:
hunk ./Distribution/Client/Configure.hs 21
+import qualified Distribution.Client.GeneralPackageIndex as GeneralPackageIndex
Shall I record this change? (1/202) [ynWsfvplxdaqjk], or ? for help: n
So after xdarcs supplies a response to the first question (doesn't
matter whether it's "y" or "n"), nothing happens. darcs appears to
just sit there. Indeed, I have confirmed with gdb that emacs never
calls read_process_output to process any more output from darcs. Not
sure yet whether emacs actually does receive any more output - I guess
that's the next thing to check, but I'm not familiar with how
asynchronous I/O code works in C.
Most likely this is a bug in emacs 23.1, then. I don't think it could
be a failure-to-flush-output bug in darcs, because emacs uses a
psuedoterminal to communicate with subprocesses (at least in this
case, anyway), and so such a bug would have been evident when using
darcs directly.
So, time for me to read up on select() and friends, then...
--
Robin
--Multipart_Sun_Nov__1_08:47:41_2009-1
Content-Type: application/octet-stream; type=emacs-lisp
Content-Disposition: attachment; filename="xdarcs.el"
Content-Transfer-Encoding: 7bit
;;; xdarcs.el --- Implements Emacs integration for darcs
;; Copyright (C) 2007 James Wright
;; Author: James Wright <[email protected]>
;; Created: 12 May 2007
;; This file is not yet part of GNU Emacs.
;; xdarcs.el 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, or (at your option)
;; any later version.
;; xdarcs.el 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This is a set of commands for integrating darcs with Emacs
;; (either of GNU Emacs or XEmacs ought to work). It was inspired by
;; John Wiegley and Christian Neukirchen's darcsum.el.
;;
;; To get started, visit a file that is in a darcs repository. Make
;; some changes, and then type `M-x darcs-whatsnew'. Select the
;; patches that you want to include (space toggles inclusion), and hit
;; `C-c C-c' to record them.
;;; Code:
(require 'xml)
(require 'timezone)
(require 'cl)
;;;; ======================================= rendezvous variables =======================================
(unless (boundp 'running-xemacs)
(defconst running-xemacs (if (string-match "XEmacs\\|Lucid" emacs-version) t)))
(defvar darcs-patch-responses nil
"Patch responses for the currently-running interactive darcs process")
(make-variable-buffer-local 'darcs-patch-responses)
(defvar *darcs-narrow-target* nil
"If `darcs-whatsnew' was called with TARGET-LOCATION-ONLY, contains the target that was displayed.")
;;;; ======================================== Convenience macros ========================================
(defmacro darcs-do-command-async (root-dir-options-list &rest body)
"Run darcs asynchronously in ROOT-DIR, passing it OPTIONS.
Output will be sent to the current buffer. When the process
terminates, the body of the macro will be executed in the
current buffer."
(let ((root-dir (car root-dir-options-list))
(options (cdr root-dir-options-list)))
`(darcs-do-command-async-fn ,root-dir (lambda () ,@body) ,@options)))
;;;; =============================================== faces ==============================================
(defface darcs-blame-author-face
'((((class color) (background dark))
(:foreground "royalblue4"))
(((class color) (background light))
(:foreground "royalblue4"))
(t (:bold t)))
"Face used to highlight the author column of blame output"
:group 'darcs)
(defface darcs-blame-date-face
'((((class color) (background dark))
(:foreground "gray38"))
(((class color) (background light))
(:foreground "gray38"))
(t (:bold t)))
"Face used to highlight the date column of blame output"
:group 'darcs)
(defface darcs-patch-name-face
'((((class color) (background dark))
(:foreground "black" :bold t))
(((class color) (background light))
(:foreground "black" :bold t))
(t (:bold t)))
"Face used to highlight patch names"
:group 'darcs)
(defface darcs-tag-name-face
'((((class color) (background dark))
(:foreground "red" :bold t))
(((class color) (background light))
(:foreground "red" :bold t))
(t (:bold t)))
"Face used to highlight tag names"
:group 'darcs)
(defface darcs-file-link-face
'((((class color) (background dark))
(:foreground "yellow" :bold t))
(((class color) (background light))
(:foreground "black" :bold t))
(t (:bold t)))
"Face used to highlight filename links"
:group 'darcs)
(defface darcs-line-added-face
'((((class color) (background dark))
(:foreground "blue"))
(((class color) (background light))
(:foreground "blue"))
(t (:bold t)))
"Face used for lines added"
:group 'darcs)
(defface darcs-line-removed-face
'((((class color) (background dark))
(:foreground "red"))
(((class color) (background light))
(:foreground "red"))
(t (:bold t)))
"Face used for lines removed"
:group 'darcs)
(defface darcs-header-line-face
'((((class color) (background dark))
(:background "gray90" :foreground "black"))
(((class color) (background light))
(:background "gray90" :foreground "black"))
(t (:bold t)))
"Face used for header lines (eg atomic patch description)"
:group 'darcs)
(defface darcs-excluded-patch-face
'((((class color) (background dark))
(:foreground "gray50"))
(((class color) (background light))
(:foreground "gray50"))
(t (:bold t)))
"Face used for patches that have been excluded"
:group 'darcs)
(defface darcs-excluded-header-line-face
'((((class color) (background dark))
(:background "gray90" :strikethru t))
(((class color) (background light))
(:background "gray90" :strikethru t))
(t (:bold t)))
"Face used for header lines of excluded patches"
:group 'darcs)
(defface darcs-excluded-patch-name-face
'((((class color) (background dark))
(:strikethru t))
(((class color) (background light))
(:strikethru t))
(t (:bold t)))
"Face used for header lines of excluded patches"
:group 'darcs)
;;;; ---------------------------- Other customizable settings ----------------------------
(defcustom darcs-command-prefix [(control x) ?t]
"Prefix key sequence for darcs commands."
:group 'darcs)
(defcustom darcs-ediff-requires-workaround t
"Set to true to use the manual workaround for darcs 2.0's Windows/diff woes"
:type 'boolean
:group 'darcs)
(defcustom darcs-debug nil
"When true, the *darcs output* buffer is never deleted"
:type 'boolean
:group 'darcs)
;;;; ============================================== keymaps =============================================
;;;; ----------------------------------- global keymap -----------------------------------
(defvar darcs-prefix-map
(let ((map (make-sparse-keymap)))
(define-key map [?a] 'darcs-add)
(define-key map [?b] 'darcs-blame)
(define-key map [?c] 'darcs-changes)
(define-key map [?=] 'darcs-diff)
(define-key map [??] 'darcs-describe-bindings)
(define-key map [?d] 'darcs-describe-patch)
(define-key map [?-] 'darcs-ediff)
(define-key map [?f] 'darcs-filelog)
(define-key map [?h] 'darcs-filelog)
(define-key map [?G] 'darcs-pull)
(define-key map [?l] 'darcs-pull)
(define-key map [?S] 'darcs-push)
(define-key map [?u] 'darcs-push)
(define-key map [?i] 'darcs-init)
(define-key map [?r] 'darcs-record)
(define-key map [(control ?r)] 'darcs-revert)
(define-key map [?m] 'darcs-query-manifest)
(define-key map [?q] 'darcs-query-manifest)
(define-key map [?w] 'darcs-whatsnew)
(define-key map [?x] 'darcs-remove)
map)
"The prefix for darcs commands")
(if (not (keymapp (lookup-key global-map darcs-command-prefix)))
(define-key global-map darcs-command-prefix darcs-prefix-map))
(defun darcs-describe-bindings ()
"Show a buffer describing the keys for darcs functions"
(interactive)
(if (fboundp 'describe-bindings-internal)
(let ((map (make-sparse-keymap)))
(save-selected-window
(switch-to-buffer-other-window "*darcs bindings*")
(define-key map [?q] 'darcs-quit-current)
(use-local-map map)
(erase-buffer)
(describe-bindings-internal darcs-prefix-map)))
(describe-bindings [(control x) ?t])))
;;;; --------------------------------- mode-specific maps --------------------------------
(defvar darcs-base-map
(let ((map (make-sparse-keymap 'darcs-base-map)))
(if running-xemacs
(define-key map 'button2 'darcs-mouse-follow-link)
(define-key map [mouse-2] 'darcs-mouse-follow-link))
map)
"Base keymap for darcs buffers. For many this will be sufficient.")
(defvar darcs-link-map
(let ((map (make-sparse-keymap 'darcs-link-map)))
(suppress-keymap map)
(define-key map [?q] 'darcs-quit-current)
(define-key map [?\r] 'darcs-follow-link)
(if running-xemacs
(define-key map 'button2 'darcs-mouse-follow-link)
(define-key map [mouse-2] 'darcs-mouse-follow-link))
map)
"Keymap for darcs links")
(defvar darcs-patch-display-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map darcs-link-map)
(define-key map [?\ ] 'darcs-toggle-patch-included)
(define-key map [?\r] 'darcs-toggle-patch-expanded)
(define-key map [(control return)] 'darcs-find-patch-in-other-window)
(define-key map [?n] 'darcs-next-patch)
(define-key map [?p] 'darcs-prev-patch)
(define-key map [?y] 'darcs-include-patch)
(define-key map [?x] 'darcs-exclude-patch)
(define-key map [?s] 'darcs-exclude-all-in-current-file)
(define-key map [?f] 'darcs-include-all-in-current-file)
(define-key map [?a] 'darcs-expand-all-patches)
(define-key map [?z] 'darcs-collapse-all-patches)
(define-key map [?Y] 'darcs-include-all-patches)
(define-key map [?X] 'darcs-exclude-all-patches)
(define-key map [?j] 'darcs-next-named-patch)
(define-key map [?k] 'darcs-prev-named-patch)
(define-key map [?N] 'darcs-next-named-patch) ;??? Should we keep N and P?
(define-key map [?P] 'darcs-prev-named-patch)
(define-key map [?A] 'darcs-expand-only-named-patches)
map)
"Keymap for displaying lists of atomic patches")
(defvar darcs-record-map
(let ((map (make-sparse-keymap 'darcs-record-map)))
(set-keymap-parent map darcs-base-map)
(define-key map [(control ?c) (control ?c)] 'darcs-commit-record)
(define-key map [(control ?x) ?#] 'darcs-commit-record)
map)
"Keymap for darcs-record-mode")
(defvar darcs-whatsnew-map
(let ((map (make-sparse-keymap 'darcs-whatsnew-map)))
(set-keymap-parent map darcs-base-map)
(define-key map [(control ?c) (control ?c)] 'darcs-record-from-whatsnew)
(define-key map [(control ?c) (control ?r)] 'darcs-commit-revert)
(define-key map [(control ?x) ?#] 'darcs-record-record-from-whatsnew)
map)
"Keymap for darcs-whatsnew-mode")
(defvar darcs-revert-map
(let ((map (make-sparse-keymap 'darcs-revert-map)))
(set-keymap-parent map darcs-base-map)
(define-key map [(control ?c) (control ?r)] 'darcs-commit-revert)
map)
"Keymap for darcs-revert-mode")
(defvar darcs-pull-map
(let ((map (make-sparse-keymap 'darcs-pull-map)))
(set-keymap-parent map darcs-base-map)
(define-key map [(control ?c) (control ?c)] 'darcs-commit-pull)
(define-key map [(control ?x) ?#] 'darcs-commit-pull)
map)
"Keymap for darcs-pull-mode")
(defvar darcs-push-map
(let ((map (make-sparse-keymap 'darcs-push-map)))
(set-keymap-parent map darcs-base-map)
(define-key map [(control ?c) (control ?c)] 'darcs-commit-push)
(define-key map [(control ?x) ?#] 'darcs-commit-push)
map)
"Keymap for darcs-push-mode")
;;;; ============================================ darcs links ===========================================
(defun darcs-make-link-overlay (start end action)
"Make an overlay that highlights when hovered over, and which when double-clicked or RET'ed on will
perform ACTION."
(let ((ov (make-overlay start end)))
(overlay-put ov 'mouse-face 'highlight)
(overlay-put ov 'read-only t)
(overlay-put ov 'darcs-select-action action)
(set-overlay-keymap ov darcs-link-map)
ov))
(defun darcs-quit-current ()
"Hide the current buffer"
(interactive)
(if (one-window-p)
(bury-buffer)
(bury-buffer)
(delete-window)))
(defun darcs-follow-link ()
"In the other window, perform the action in the 'darcs-select-action property of the nearest
enclosing overlay of point"
(interactive)
(let ((ov (overlay-at (point) 'darcs-select-action)))
(unless ov
(error "No link on current line"))
(apply (car (overlay-get ov 'darcs-select-action)) (cdr (overlay-get ov 'darcs-select-action)))))
(defun darcs-mouse-follow-link (evt)
"Function to translate mouse clicks to character events"
(interactive "e")
(let ((win (event-window evt))
(pnt (event-point evt)))
(select-window win)
(goto-char pnt)
(darcs-follow-link)))
(defvar darcs-editable-patch-name-overlay nil
"The overlay that we use to highlight the patch name in a darcs record buffer")
(make-variable-buffer-local 'darcs-editable-patch-name-overlay)
(defun darcs-pre-idle-hook ()
"Displays tool-tips on active overlays when point is over them, and maintains font-locking"
(when darcs-editable-patch-name-overlay
(save-excursion
(goto-char (point-min))
(move-overlay darcs-editable-patch-name-overlay
(point-at-bol) (point-at-eol))))
(let ((ov (overlay-at (point) 'darcs-tool-tip)))
(when ov
(message "%s" (overlay-get ov 'darcs-tool-tip)))))
(add-hook 'post-command-hook 'darcs-pre-idle-hook)
;;;; ============================== specialized handling for patch display ==============================
(defvar darcs-exclude-enabled-function (lambda (ov) t)
"This function is called to determine whether `darcs-include-patch' and `darcs-exclude-patch'
functions should be permitted on a given overlay.")
(make-variable-buffer-local 'darcs-exclude-enabled-function)
(defun darcs-nearest-patch ()
"Returns the nearest patch to point"
(or (overlay-at (point) 'darcs-patch-ov)
(progn (beginning-of-line-text) (overlay-at (point) 'darcs-patch-ov))
(darcs-move-to-patch -1)
(error "no patch around point")))
(defun darcs-toggle-patch-included ()
"If a patch is included, then exclude it; else re-include it"
(interactive)
(let ((ov (darcs-nearest-patch)))
(unless (funcall darcs-exclude-enabled-function ov)
(error "`darcs-exclude-patch' is not enabled for this patch"))
(if (overlay-get ov 'patch-excluded)
(darcs-include-patch t)
(darcs-exclude-patch t))))
(defun darcs-exclude-patch (&optional recursive-p)
"Exclude the current patch and skip to the next patch"
(interactive)
(let ((ov (darcs-nearest-patch)))
(unless (or recursive-p
(funcall darcs-exclude-enabled-function ov))
(error "`darcs-exclude-patch' is not enabled for this patch"))
(let ((desc-ov (overlay-get ov 'darcs-patch-ov)))
(overlay-put ov 'patch-excluded t)
(set-overlay-face ov (if (darcs-named-patch-p ov)
'darcs-excluded-patch-name-face
'darcs-excluded-header-line-face))
(set-overlay-face desc-ov 'darcs-excluded-patch-face)
(set-overlay-priority desc-ov 10)
(when (darcs-named-patch-p ov)
(save-restriction
(save-excursion
(narrow-to-region (overlay-start desc-ov) (overlay-end desc-ov))
(goto-char (overlay-start ov))
(when (darcs-move-to-patch 1)
(darcs-on-all-patches
(lambda (ov)
(unless (darcs-named-patch-p ov)
(darcs-exclude-patch t))))))))
(darcs-collapse-patch)
(unless recursive-p
(if (darcs-named-patch-p ov)
(darcs-next-named-patch)
(darcs-next-patch))))))
(defun darcs-include-patch (&optional recursive-p)
"Include the current patch and skip to the next patch"
(interactive)
(let* ((ov (darcs-nearest-patch))
(desc-ov (overlay-get ov 'darcs-patch-ov)))
(unless (or recursive-p
(funcall darcs-exclude-enabled-function ov))
(error "`darcs-include-patch' is not enabled for this patch"))
(overlay-put ov 'patch-excluded nil)
(set-overlay-face ov (if (darcs-named-patch-p ov)
'darcs-patch-name-face
'darcs-header-line-face))
(set-overlay-face (overlay-get ov 'darcs-patch-ov) nil)
(darcs-expand-patch)
(when (darcs-named-patch-p ov)
(save-restriction
(save-excursion
(narrow-to-region (overlay-start desc-ov) (overlay-end desc-ov))
(goto-char (overlay-start ov))
(when (darcs-move-to-patch 1)
(darcs-on-all-patches
(lambda (ov)
(unless (darcs-named-patch-p ov)
(darcs-include-patch t)))))
(goto-char (point-min))
(darcs-collapse-all-atomic-patches))))
(unless recursive-p
(if (darcs-named-patch-p ov)
(darcs-next-named-patch)
(darcs-next-patch)))))
(defun darcs-patch-collapsed-p ()
"Returns non-NIL if patch at point is collapsed"
(let* ((ov (darcs-nearest-patch)))
(= ?\^M (char-after (or (overlay-get ov 'darcs-collapse-point)
(overlay-end ov))))))
(defun darcs-toggle-patch-expanded ()
"Expands or collapses the current patch"
(interactive)
(save-excursion
(if (darcs-patch-collapsed-p)
(darcs-expand-patch)
(darcs-collapse-patch))))
(defun darcs-flag-patch (flag-char)
"Set all newlines to ^M or vice versa. (if FLAG-CHAR is ?\n, set all to ?\n).
Applies to the description region of the current patch."
(let* ((inhibit-read-only t)
(ov (darcs-nearest-patch))
(desc-ov (overlay-get ov 'darcs-patch-ov))
(collapse-point (overlay-get ov 'darcs-collapse-point)))
;; A little bit of hackery here. We assume that the collapse-point precedes a space; we convert
;; that space to a ^M to hide the rest of the line. When expanding, we convert it back to a
;; space. If collapse-point ever precedes a non-space we're screwed, so include an explicit
;; check.
(when collapse-point
(save-excursion
(goto-char collapse-point)
(delete-char 1)
(if (= flag-char ?\n)
(insert-char ?\ 1)
(unless (looking-at " ")
(error "assertion failed: (looking-at \" \")"))
(insert-char ?\^M 1))))
(subst-char-in-region (or collapse-point
(overlay-end ov))
(overlay-end desc-ov)
(if (= flag-char ?\n) ?\^M ?\n) flag-char)))
(defun darcs-expand-patch ()
"Expand the current patch"
(interactive)
(let* ((ov (darcs-nearest-patch))
(desc-ov (overlay-get ov 'darcs-patch-ov)))
(darcs-flag-patch ?\n)
;; More special-case hackery. If we expand a named patch, collapse all its children afterward.
(when (darcs-named-patch-p ov)
(save-excursion
(save-restriction
(narrow-to-region (overlay-start desc-ov) (overlay-end desc-ov))
(darcs-collapse-all-atomic-patches))))))
(defun darcs-collapse-patch ()
"Hide the current patch"
(interactive)
(darcs-flag-patch ?\^M))
(defun darcs-find-patch-in-other-window ()
"Opens the file associated with the nearest patch in the other window and moves point to the
associated line, if any"
(interactive)
(let ((root-dir (darcs-root-directory default-directory))
(ov (darcs-nearest-patch)))
(when ov
(let ((file (darcs-associated-file root-dir (overlay-string ov)))
(line (darcs-associated-line root-dir (overlay-string ov))))
(unless file
(error (format "no file associated with change '%s'" (overlay-string ov))))
(find-file-other-window file)
(when line
(goto-line line))))))
(defun darcs-move-to-patch (delta)
"Move to the next patch (when DELTA is 1) or the previous patch (when DELTA is -1).
Skips over intermediate patches when (> (abs DELTA) 1)"
(interactive)
(when (zerop delta)
(error "DELTA must not be 0"))
(let ((orig-point (point))
(ov nil))
(goto-char (point-at-bol))
(while (and (null ov)
(zerop (forward-line delta))
(/= (point) (point-max)))
(beginning-of-line-text)
(setq ov (overlay-at (point) 'darcs-patch-ov)))
(if (and ov (/= (point) orig-point))
ov
(goto-char orig-point)
nil)))
;(defun darcs-maybe-recenter ()
; "Recenter if necessary to bring the current patch into full view"
; (let* ((ov (darcs-nearest-patch))
; (desc-ov (overlay-get ov 'darcs-patch-ov))
; (ws (line-number (window-start)))
; (we (line-number (window-end)))
; (l (line-number))
; (oe (overlay-end desc-ov)))
; (when (> oe we)
; (let ((top (- (- l ws) (- oe we))))
; (message (format "Recentering at %d or %d" top 5))
; (recenter (max top 5))))))
(defun darcs-maybe-recenter (&optional median-height)
"Recenter if we are more than MEDIAN-HEIGHT lines from the top of the buffer"
(setq median-height (or median-height (/ (window-body-height) 4)))
(let ((median-line (+ (line-number (window-start))
median-height)))
(when (> (line-number) median-line)
(recenter median-height))))
(defun darcs-next-patch ()
"Move point to the beginning of the next patch heading"
(interactive)
(if (darcs-move-to-patch 1)
(darcs-maybe-recenter)
(message "No more patches")))
(defun darcs-prev-patch ()
"Move point to the beginning of the previous patch heading"
(interactive)
(if (darcs-move-to-patch -1)
(darcs-maybe-recenter)
(message "No more patches")))
(defun darcs-named-patch-p (ov)
"Return non-NIL if OV is an overlay representing a named patch"
;; only named patches have a collapse-point
(overlay-get ov 'darcs-collapse-point))
(defun darcs-next-named-patch ()
"Move point to the beginning of the next named patch"
(interactive)
(let ((orig-point (point))
(ov (darcs-move-to-patch 1)))
(while (and ov
(not (darcs-named-patch-p ov)))
(setq ov (darcs-move-to-patch 1)))
(if ov
(darcs-maybe-recenter)
(goto-char orig-point)
(message "No more named patches"))))
(defun darcs-prev-named-patch ()
"Move point to the beginning of the next named patch"
(interactive)
(let ((orig-point (point))
(ov (darcs-move-to-patch -1)))
(while (and ov
(not (darcs-named-patch-p ov)))
(setq ov (darcs-move-to-patch -1)))
(if ov
(darcs-maybe-recenter)
(goto-char orig-point)
(message "No more named patches"))))
(defun darcs-on-all-patches (thunk)
"Evaluates THUNK with point set to the beginning of each patch in the current buffer"
(save-excursion
(goto-char (point-min))
(let ((ov (or (overlay-at (point) 'darcs-patch-ov)
(darcs-move-to-patch 1))))
(while ov
(funcall thunk ov)
(setq ov (darcs-move-to-patch 1))))))
(defun darcs-collapse-all-patches ()
"Collapse all patches in the current buffer"
(interactive)
(darcs-on-all-patches (lambda (ov) (darcs-flag-patch ?\^M))))
(defun darcs-expand-all-patches ()
"Expand all patches in the current buffer"
(interactive)
(darcs-on-all-patches (lambda (ov) (darcs-flag-patch ?\n))))
(defun darcs-include-all-patches ()
"Include all patches in the current buffer"
(interactive)
(darcs-on-all-patches (lambda(ov) (darcs-include-patch t))))
(defun darcs-exclude-all-patches ()
"Exclude all patches in the current buffer"
(interactive)
(darcs-on-all-patches (lambda(ov) (darcs-exclude-patch t))))
(defun darcs-collect-patch-responses ()
"Returns a list of cells of the form (PATCH-DESC . PLIST), where PATCH-DESC is a string
describing the patch (eg, \"hunk ./notes/darcs-mode 35\") and PLIST contains two properties:
:INCLUDED = non-NIL for included patches
:EXPANDED = non-NIL for expanded patches"
(let ((responses nil))
(darcs-on-all-patches (lambda (ov)
(push (list (overlay-string ov)
:named (darcs-named-patch-p ov)
:included (not (overlay-get ov 'patch-excluded))
:expanded (not (darcs-patch-collapsed-p)))
responses)))
responses))
(defun darcs-apply-patch-responses (patch-responses)
"Ensures that every patch in the current buffer is excluded if it is excluded in PATCH-RESPONSES."
;; ??? make number of patches etc. match??
(darcs-on-all-patches (lambda (ov)
(let ((cell (assoc (overlay-string ov) patch-responses)))
(when cell
(if (plist-get-with-default (cdr cell) :included t)
(darcs-include-patch t)
(darcs-exclude-patch t))
(if (plist-get-with-default (cdr cell) :expanded t)
(darcs-expand-patch)
(darcs-collapse-patch)))))))
(defun darcs-on-all-henceforth-patches-in-current-file (thunk)
"Apply THUNK with point on the current patch, and on each _subsequent_ patch with the same
associate file. On completion, point will be either on the last patch, or on the first subsequent
patch associated with a different file."
(let* ((ov (darcs-nearest-patch))
(file (when (and ov (not (darcs-named-patch-p ov)))
(darcs-associated-file default-directory
(overlay-string ov)))))
(while (and ov file
(not (darcs-named-patch-p ov))
(string= file (darcs-associated-file default-directory
(overlay-string ov))))
(funcall thunk)
(setq ov (darcs-move-to-patch 1)))))
(defun darcs-include-all-in-current-file ()
"Includes current patch, and all following patches in the same file"
(interactive)
(unless (funcall darcs-exclude-enabled-function (darcs-nearest-patch))
(error "`darcs-include-patch' is not enabled for this patch"))
(darcs-on-all-henceforth-patches-in-current-file
(lambda ()
(darcs-include-patch t))))
(defun darcs-exclude-all-in-current-file ()
"Excludes current patch, and all following patches in the same file"
(interactive)
(unless (funcall darcs-exclude-enabled-function (darcs-nearest-patch))
(error "`darcs-exclude-patch' is not enabled for this patch"))
(darcs-on-all-henceforth-patches-in-current-file
(lambda ()
(darcs-exclude-patch t))))
(defun darcs-collapse-all-atomic-patches ()
"Excludes all atomic (ie, unnamed) patches while leaving named patches unchanged"
(darcs-on-all-patches
(lambda (ov)
(unless (darcs-named-patch-p ov)
(darcs-flag-patch ?\^M)))))
(defun darcs-expand-only-named-patches ()
"Expands all named patches but collapses all others"
(interactive)
(darcs-on-all-patches
(lambda (ov)
(if (darcs-named-patch-p ov)
(darcs-expand-patch)
(darcs-collapse-patch)))))
;;;; ======================================= interactive functions ======================================
;;;;; XML format
;;;
;;; The XML produced by 'darcs annotate' appears to have the following features:
;;; a single tag of the form
;;;
;;; <modified><modified_how></<modified_how><patch></patch></modified>
;;;
;;; describing the most-recent patch to be applied to the file, followed by several of
;;;
;;; <normal_line><added_by><patch></patch></added_by> ...text... </normal_line>
;;;
;;; for lines that are part of the file due to previous (ie, not the most-recent) patches, plus
;;; several of
;;;
;;; <added_line> ...text... </added_line>
;;; <removed_line> ...text... </removed_line>
;;;
;;; for lines that were added or removed by the most-recent patch.
;;;;; code
(defun darcs-blame (file)
"Evaluates the darcs annotate command on FILE and outputs it with author and date annotations"
(interactive (list (or (buffer-truename (current-buffer))
default-directory)))
(unless (darcs-file-registered-p file)
(if (darcs-root-directory file)
(error (format "%s is not part of darcs repo at %s" file (darcs-root-directory file)))
(error (format "No darcs repo at or around %s" (file-name-directory file)))))
(let* ((root-dir (darcs-root-directory file))
(data (with-temp-buffer
(darcs-do-command root-dir
"annotate"
(darcs-canonical-name file)
"--xml")
(xml-parse-region (point-min) (point-max))))
(inhibit-read-only t))
(switch-to-buffer (darcs-format-buffername 'blame (file-name-nondirectory file)))
(erase-buffer)
(darcs-set-mode-from-name file)
(let ((modified-tag (car (xml-get-children* (car data) 'modified))))
(dolist (child (xml-node-children (car data)))
(when (and (listp child)
(or (eq 'normal_line (xml-node-name child))
(eq 'added_line (xml-node-name child))))
(let* ((chg-spec (or (car (xml-get-children* child 'added_by))
modified-tag))
(patch-tag (car (xml-get-children* chg-spec 'patch)))
(local-date (xml-get-attribute patch-tag 'local_date))
(author (xml-substitute-special
(xml-get-attribute patch-tag 'author)))
(patch-name (darcs-xml-node-text
(car (xml-get-children* patch-tag 'name))))
(hash (xml-get-attribute patch-tag 'hash))
(line (darcs-xml-node-text child)))
(when (> (length line) 0)
(let (pa1 pa2 pd1 pd2 pn1 pn2
author-ov date-ov name-ov all-ov)
(setq pd1 (point))
(insert (substring (darcs-cook-date local-date)
0 11))
(setq pd2 (point))
(insert " ")
(setq pa1 (point))
(insert (format "%-7s" (if (> (length author) 7)
(substring author 0 7)
author)))
(setq pa2 (point))
(insert " ")
(setq pn1 (point))
(insert (format "%-15s" (if (> (length patch-name) 15)
(substring patch-name 0 15)
patch-name)))
(setq pn2 (point))
(insert ": ")
(setq e (point))
(insert (format "%s\n" (darcs-trim-newlines line)))
(setq author-ov (make-overlay pa1 pa2))
(setq date-ov (make-overlay pd1 pd2))
(setq name-ov (make-overlay pn1 pn2))
(setq all-ov (darcs-make-link-overlay
pd1 e (list 'darcs-describe-patch root-dir patch-name hash)))
(overlay-put all-ov 'darcs-tool-tip
(format "%s [%s %s]"
patch-name
(darcs-cook-date local-date)
author))
(set-overlay-priority date-ov 5)
(set-overlay-priority author-ov 5)
(set-overlay-priority name-ov 5)
(set-overlay-face author-ov 'darcs-blame-author-face)
(set-overlay-face date-ov 'darcs-blame-date-face)
(set-overlay-face name-ov 'darcs-patch-name-face))))))
(goto-char (point-min)))))
(defun darcs-add (filename)
"Add FILENAME to the nearest darcs repository"
(interactive (list (or (buffer-truename (current-buffer))
default-directory)))
(let ((root-dir (darcs-root-directory filename))
(canonical-name (darcs-canonical-name filename)))
(unless root-dir
(error (format "No darcs repo at or around %s" (file-name-directory filename))))
(with-temp-buffer
(unless (zerop (darcs-do-command root-dir "add" canonical-name))
(error (one-line-buffer)))
(message "Added %s to darcs repo %s" canonical-name root-dir)
(darcs-refresh-query-manifest))))
(defun darcs-remove (filename)
"Removes FILENAME from the nearest darcs repository"
(interactive (list (or (buffer-truename (current-buffer))
default-directory)))
(let ((root-dir (darcs-root-directory filename))
(canonical-name (darcs-canonical-name filename)))
(unless root-dir
(error (format "No darcs repo at or around %s" (file-name-directory filename))))
(with-temp-buffer
(unless (zerop (darcs-do-command root-dir "remove" canonical-name))
(error (one-line-buffer)))
(message "Removed %s from darcs repo %s" canonical-name root-dir)
(darcs-refresh-query-manifest))))
(defun darcs-query-manifest (file-or-dir &optional recursive-p)
"Shows the files managed in the repo at or around FILE-OR-DIR. If RECURSIVE-P in non-nil, does
not for the window to display."
(interactive (list (or (buffer-truename (current-buffer))
default-directory)))
(let ((root-dir (darcs-root-directory file-or-dir))
(inhibit-read-only t))
(unless root-dir
(error (format "No darcs repo at or around %s" (file-name-directory file-or-dir))))
(save-excursion
(darcs-set-buffer 'query-manifest root-dir recursive-p)
(erase-buffer)
(dolist (file (darcs-manifest file-or-dir))
(let (p1 p2 ov)
(setq p1 (point))
(insert (format "%s" file))
(setq p2 (point))
(insert "\n")
(setq ov (darcs-make-link-overlay p1 p2 (list 'find-file-other-window (expand-file-name (concat root-dir file)))))
(set-overlay-face ov 'bold)))
(when (= (point-min) (point-max))
(insert "No files managed in this repo"))
(unless recursive-p
(goto-char (point-min))))))
(defun darcs-refresh-query-manifest ()
"Refresh the appropriate query-manifest window if it exists (based on the current buffer's default
directory)"
(let ((root-dir (darcs-root-directory default-directory)))
(save-excursion
(when (get-buffer (darcs-format-buffername 'query-manifest root-dir))
(darcs-query-manifest root-dir t)))))
(defvar darcs-patch-headers-re
(regexp-opt
'("hunk" "replace" "binary" "addfile" "adddir" "rmfile" "rmdir" "move"
"changepref" "merger" "regrem" "conflict" "tcilfnoc"))
"All the different kinds of atomic patch that can be part of a patch")
(defun darcs-describe-patch (file-or-dir patch-name &optional patch-hash)
"Describe a particular patch"
(interactive (list (or (buffer-truename (current-buffer))
default-directory)
(read-string "Patch name/regexp: ")))
(let ((root-dir (darcs-root-directory file-or-dir))
(inhibit-read-only t))
(unless root-dir
(error (format "No darcs repo at or around %s" (file-name-directory file-or-dir))))
(darcs-set-buffer 'describe (or patch-name patch-hash))
(erase-buffer)
(setq darcs-exclude-enabled-function (lambda (ov) nil))
(darcs-do-command root-dir
"annotate"
(if patch-hash
(format "--match=hash %s" patch-hash)
(format "--patch=%s" patch-name))
"-u")
(goto-char (point-min))
(darcs-markup-patch-descriptions)
(goto-char (point-min))
(toggle-read-only 1)))
(defun darcs-whatsnew (location &optional recursive-p target-location-only)
"Show all unrecorded changes in the specified repo. If RECURSIVE-P is non-NIL, updates an
existing buffer without necessarily displaying it. If TARGET-LOCATION-ONLY is non-NIL, only
shows differences for LOCATION."
(interactive (list (or (buffer-truename (current-buffer))
default-directory)))
(let ((root-dir (darcs-root-directory location))
(inhibit-read-only t))
(unless root-dir
(error (format "No darcs repo at or around %s" (file-name-directory location))))
(darcs-set-buffer 'whatsnew root-dir recursive-p)
(erase-buffer)
(if target-location-only
(set (make-local-variable '*darcs-narrow-target*) (darcs-canonical-name location))
(set (make-local-variable '*darcs-narrow-target*) nil))
(save-excursion
(unless (zerop (darcs-do-command root-dir "whatsnew" "-u" *darcs-narrow-target*))
(set-overlay-keymap (make-overlay (point-min) (point-max)) darcs-patch-display-map)
(toggle-read-only 1)
(unless recursive-p
(message (one-line-buffer))))
(goto-char (point-min))
(darcs-markup-patch-descriptions))
(or (progn (beginning-of-line-text) (overlay-at (point) 'darcs-patch-ov))
(darcs-move-to-patch -1)
--Multipart_Sun_Nov__1_08:47:41_2009-1--_______________________________________________
darcs-users mailing list
[email protected]
http://lists.osuosl.org/mailman/listinfo/darcs-users