     (suppress-keymap map)
     (define-key map [return] 'darcsum-toggle) ; ??
     (define-key map "\C-m" 'darcsum-toggle)
+    (define-key map "\C-\M-m" 'darcsum-show)
     (define-key map "\C-c\C-c" 'darcsum-goto)
     (define-key map [tab] 'darcsum-next-entity)
+    (define-key map [space] 'darcsum-mark-and-next-entity)
+    (define-key map " " 'darcsum-mark-and-next-entity)
+    (define-key map [backspace] 'darcsum-unmark-and-next-entity)
+    (define-key map [delete] 'darcsum-unmark-and-next-entity)
     (define-key map "?" 'describe-mode)
     (define-key map "f" 'darcsum-find-file)
+    (define-key map "v" 'darcsum-display-change)
     (define-key map "=" 'darcsum-diff)
     (define-key map "e" 'darcsum-ediff)
     (define-key map "E" 'darcsum-ediff-merge)

Propose the insertion of a regexp suitable to permanently ignore
the file or the directory at point into the boring file."
  (interactive
   (let ((type (get-text-property (point) 'darcsum-line-type)))
     (cond
      ((eq type 'dir)
       (setq path (get-text-property (point) 'darcsum-dir))
       (if (string-match "^\\./" path)
           (setq path (substring path 2)))
       (setq path (concat "(^|/)" (regexp-quote path) "($|/)")))
      ((memq type '(file change))
       (setq path (get-text-property (point) 'darcsum-file))
       (setq path (concat "(^|/)" path "$"))))
     (list (read-string "Add to boring list: " path))))
  (save-excursion


;; A changeset is an alist of the following form:
;;
;;   ((PATH (TYPE SELECTED CONTENT...))))
;;
;; where PATH is plain string, but TYPE is of the following
;; possible formats:
;;
;;   LINE     An integer giving the first line of the hunk
;;   SYMBOL   Non-hunk change: 'addfile 'newfile 'rmfile 'binary or 'replace
;;
;; SELECTED is a list of flags, 'mark or 'hide symbols.
;;
;; Each CONTENT is a string which represents a modification to make to the
;; file after the starting line. For hunks, each change begins with either a
;; "+" or "-" to indicate if the line should be removed or added to the
;; file.
;;
;; So, for example, in a buffer with changes visible in report.cc visible
;; and changes in report.h marked:
;;
;; (("./TODO" (addfile (hide)))
;;  ("./report.cc"
;;    (replace nil "[A-Za-z_0-9] indented intended")
;;    (606 nil "-    blah" "+    blah" "+    blah")
;;    (620 nil "-    blah" "+    blah" "+    blah")
;;    (629 nil "-    blah" "+    blah" "+    blah")
;;    (634 nil "-    blah" "+    blah" "+    blah")
;;    (641 nil "-    blah" "+    blah" "+    blah")
;;    (652 nil "-    blah" "+    blah" "+    blah")
;;    (664 nil "-    blah" "+    blah" "+    blah"))
;;  ("./report.h"
;;    (115 (mark) "-    blah" "+    blah" "+    blah")
;;    (126 (mark) "+"))))
;;

(defun darcsum-change-add-flag (change flag)
  "Add FLAG on CHANGE."
  (if (not (memq flag (cadr change)))
      (setcar (cdr change) (cons flag (cadr change)))))

(defun darcsum-change-remove-flag (change flag)
  "Remove FLAG on CHANGE."
  (if (memq flag (cadr change))
      (setcar (cdr change) (delq flag (cadr change)))))

(defun darcsum-change-remove-all-flags (change)
  "Remove all flags on CHANGE."
  (setcar (cdr change) nil))

(defun darcsum-change-toggle-flag (change flag)
  "Toggle FLAG on CHANGE."
  (if (memq flag (cadr change))
      (setcar (cdr change) (delq flag (cadr change)))
    (setcar (cdr change) (cons flag (cadr change)))))

(defun darcsum-change-mark-p (change)
  "Return mark if CHANGE is marked."
  (not (null (memq 'mark (cadr change)))))

(defun darcsum-change-unmark-p (change)
  "Return mark if CHANGE is not marked."
  (null (memq 'mark (cadr change))))

(defun darcsum-change-toggle-mark (change)
  "Toggle mark flag on CHANGE."
  (darcsum-change-toggle-flag change 'mark))

(defun darcsum-change-add-mark (change)
  "Add mark flag on CHANGE."
  (darcsum-change-add-flag change 'mark))

(defun darcsum-change-remove-mark (change)
  "Remove mark flag on CHANGE."
  (darcsum-change-remove-flag change 'mark))

(defun darcsum-change-visible-p (change)
  "Return t if CHANGE is visible."
  (not (memq 'hide (cadr change))))

(defun darcsum-change-toggle-hide (change)
  "Toggle hide flag on CHANGE."
  (darcsum-change-toggle-flag change 'hide))

(defun darcsum-change-add-hide (change)
  "Add hide flag on CHANGE."
  (darcsum-change-add-flag change 'hide))

(defun darcsum-change-remove-hide (change)
  "Add hide flag on CHANGE."
  (darcsum-change-remove-flag change 'hide))

(defun darcsum-changeset-any-p (changeset predicate)
  "Return t if PREDICATE is true for any change in CHANGESET."
  (catch 'exit
    (ignore
     (let (file change)
       (dolist (file changeset)
	 (dolist (change (cdr file))
	   (if (funcall predicate change)
	       (throw 'exit t))))))))

(defsubst darcsum-changeset-any-marked-p (changeset)
  "Return t if CHANGESET has change(s) which have been marked."
  (darcsum-changeset-any-p changeset (function darcsum-change-mark-p)))

(defsubst darcsum-changeset-any-unmarked-p (changeset)
  "Return t if CHANGESET has change(s) which have not been marked."
  (darcsum-changeset-any-p changeset (function darcsum-change-unmark-p)))

(defsubst darcsum-changeset-any-visible-p (changeset)
  "Return t if CHANGESET has change(s) which are visible."
  (darcsum-changeset-any-p changeset (function darcsum-change-visible-p)))

(defun darcsum-changeset-all-p (changeset predicate)
  "Return t if PREDICATE is true for all change in CHANGESET."
  (not (catch 'exit
	 (ignore
	  (let (file change)
	    (dolist (file changeset)
	      (dolist (change (cdr file))
		(if (not (funcall predicate change))
		    (throw 'exit t)))))))))

(defsubst darcsum-changeset-all-marked-p (changeset)
  "Return t if all changes in CHANGESET have been marked."
  (darcsum-changeset-all-p changeset (function darcsum-change-mark-p)))

(defsubst darcsum-changeset-all-visible-p (changeset)
  "Return t if all changes in CHANGESET are visible."
  (darcsum-changeset-all-p changeset (function darcsum-change-visible-p)))

(defun darcsum-changeset-find (changeset predicate)
  "Return changes selected by PREDICATE from CHANGESET."
  (let (file change found)
    (dolist (file changeset)
      (let (changes)
	(dolist (change (cdr file))
	  (if (funcall predicate change)
	      (setq changes (cons change changes))))
	(if changes
	    (setq changes (cons (car file) (nreverse changes))
		  found (cons changes found)))))
    (nreverse found)))

(defun darcsum-changeset-find-visible (changeset)
  "Return visible changes from CHANGESET."
  (darcsum-changeset-find changeset (function darcsum-change-visible-p)))

(defun darcsum-changeset-find-marked (changeset)
  "Return marked changes from CHANGESET."
  (darcsum-changeset-find changeset (function darcsum-change-mark-p)))

(defsubst darcsum-find-change (changeset file line content)
  ;; Return change in CHANGESET with matching FILE, LINE and CONTENT.
  ;; If CONTENT is 'any, it is ignored.
  (let ((change (assoc line (assoc file changeset))))
    (if (or (eq content 'any)
	    (equal (caddr change) content))
	change)))

(defconst darcsum-file-change-status-alist
  '((addfile . "Added")
    (adddir . "Added directory")
    (newfile . "New")
    (newdir . "New directory")
    (rmfile . "Removed")
    (rmdir . "Removed directory")
    (binary . "Modified binary")))

(defun darcsum-file-change-status (change)
  "Return file-change-status displayed with CHANGE."
  (cdr (assq (car change) darcsum-file-change-status-alist)))

(defun darcsum-make-temp-file (&optional template)
  "Create temporary file.  Optional argument TEMPLATE sets the base name.

The template, if present, is passed to `expand-file-name' to construct a
fully qualified base name.  If absent, the string \"_darcs\" is used.

The function `make-temp-file' is preferred, but if it is not available,
`make-temp-name' is used as a fallback."
  (unless template
    (setq template "darcsum"))
  (unless (file-name-absolute-p template)
    (setq template (expand-file-name template "_darcs")))
  (if (fboundp 'make-temp-file)
      (make-temp-file template)
    ;; make-temp-name generates a unique name when it is called, but
    ;; takes no provisions to ensure that it will remain unique. Thus,
    ;; there is a race condition before we use the name. This is
    ;; probably a bad thing.
    (make-temp-name template)))

(defun darcsum-changeset-has-directory-p (changeset dir)
  (and (assoc dir changeset) t))

(defun darcsum-apply-to-changes (data func)
  (let (file change)
    (dolist (file data)
      (dolist (change (cdr file))
	(funcall func change)))))

(defun darcsum-remove-changeset (changeset remove)
  "Remove REMOVE from the CHANGESET."
  (let (file change)
    (dolist (file remove)
      (let ((fentry (assoc (car file) changeset)))
	(dolist (change (cdr file))
	  (setcdr fentry (delete (assoc (car change) (cdr fentry))
				 (cdr fentry))))
	(unless (cdr fentry)
	  (setq changeset (delete fentry changeset))))))
  changeset)

(defconst darcsum-item-numeric-alist
  '((move . -2)
    (addfile . -1)
    (adddir . -1)
    (newfile . -1)
    (newdir . -1)
    (rmfile . -1)
    (rmdir . -1)
    (binary . 0)
    (replace . 0)))

(defun darcsum-change-< (l r)
  (setq l (car l)
	r (car r))
  (< (if (numberp l) l (or (cdr (assq l darcsum-item-numeric-alist)) 0))
     (if (numberp r) r (or (cdr (assq r darcsum-item-numeric-alist)) 0))))

(defun darcsum-add-changeset (changeset add)
  "Add ADD to CHANGESET."
  (let (file fentry change)
    (dolist (file add)
      (if (setq fentry (assoc (car file) changeset))
	  (progn
	    (dolist (change (cdr file))
	      (unless (member change (cdr fentry))
		(nconc fentry (list change))))
	    (setcdr fentry (sort (cdr fentry) (function darcsum-change-<))))
	(setq changeset (cons file changeset)))))
  (sort changeset))

(defun darcsum-merge-changeset (data changeset)
  "Merge CHANGESET into the DATA.

Currently this simply moves 'mark and 'hide from DATA to CHANGESET."
  ;;;;;;; TODO: commute new patches
  ;;;;;;; (iow, behave properly if lines are added or deleted)
  (let (file data-file change data-change)
    (dolist (file changeset)
      (if (setq data-file (assoc (car file) data))
	  (dolist (change (cdr file))
	    (let ((data-change (assoc (car change) data-file))
		  (item (car data-change)))
	      (if (cond
		   ((null item))
		   ((eq item 'replace) (equal (cddr change) (cddr data-change)))
		   ((numberp item) (darcsum-hunk-match (cddr change) (cddr data-change)))
		   (t t))
		  (setcar (cdr change) (car (cdr data-change)))))))))
  changeset)

(defun darcsum-hunk-match (a b)
  "Return t if hunks in A and B match (modify same lines)."
  (if (equal a b)
      t
    (while (string-match "^ " (car a)) (setq a (cdr a)))
    (while (string-match "^ " (car b)) (setq b (cdr b)))
    (while (and a b (string= (car a) (car b)))
      (setq a (cdr a) b (cdr b)))
    (if (or (null a) (null b)
	    (string-match "^[+]" (car a))
	    (string-match "^[+]" (car b)))
	t)))

(defun darcsum-parse-changeset (&optional pending visible)
  "Return the patch in the current buffer as a Lisp changeset."
  (when (looking-at "^{")
    (forward-line))
  (let ((limit (* 10 (count-lines (point-min) (point-max))))
        data change entry)
    (while (and (not (or (eobp) (looking-at "^}")))
                (> limit 0))
      (setq limit (1- limit))
      (cond
       ((looking-at "^\\(addfile\\|adddir\\|rmdir\\|move\\|binary\\|rmfile\\|hunk\\|replace\\)\\s-+\\(.+?\\)\\(\\s-+\\([0-9]+\\|.+\\)\\)?$")
        (forward-line)
        (let* ((item (intern (match-string 1)))
               (path (match-string 2))
               (extra (match-string 4))
               lines)
	  ;; (message (concat "Looking at " (match-string 1)))
          (case item
	    ('hunk
	     (while (looking-at "^\\([+ -].*\\)")
	       (setq lines (cons (match-string 1) lines))
	       (forward-line))
	     (setq item (string-to-number extra)
		   lines (nreverse lines)))
	    ('binary
	     (while (looking-at "^\\(old\\|new\\)hex$")
	       (forward-line)
	       (while (looking-at "^\\*")
		 (forward-line))))
	    ('addfile
	     (if (and (not (eq pending t))
		      (null (assoc path pending)))
		 (setq item 'newfile)))
	    ('adddir
	     (if (and (not (eq pending t))
		      (null (assoc path pending)))
		 (setq item 'newdir)))
	    ('move
	     (setq lines (list extra)))
	    ('replace
	     (setq lines (list extra))))
	  (setq change (cons item (cons (if visible nil (list 'hide)) lines))
		fentry (assoc path data))
	  (if (null fentry)
	      (setq data (cons (cons path (list change)) data))
	    ;; (message path)
	    (setcdr fentry (cons change (cdr fentry))))))
       ))
    (assert (>= limit 0))
    (dolist (entry data)
      (setcdr entry (sort (cdr entry) (function darcsum-change-<))))
    (nreverse data)))

(defun darcsum-read-changeset (&optional visible)
