contrib/mercurial.el
changeset 4433 ba22e867cb23
parent 4413 b008deae9910
child 4693 3f484688c702
equal deleted inserted replaced
4432:905397be7688 4433:ba22e867cb23
    41 ;; Please send problem reports and suggestions to bos@serpentine.com.
    41 ;; Please send problem reports and suggestions to bos@serpentine.com.
    42 
    42 
    43 
    43 
    44 ;;; Code:
    44 ;;; Code:
    45 
    45 
    46 (require 'advice)
    46 (eval-when-compile (require 'cl))
    47 (require 'cl)
       
    48 (require 'diff-mode)
    47 (require 'diff-mode)
    49 (require 'easymenu)
    48 (require 'easymenu)
    50 (require 'executable)
    49 (require 'executable)
    51 (require 'vc)
    50 (require 'vc)
    52 
    51 
       
    52 (defmacro hg-feature-cond (&rest clauses)
       
    53   "Test CLAUSES for feature at compile time.
       
    54 Each clause is (FEATURE BODY...)."
       
    55   (dolist (x clauses)
       
    56     (let ((feature (car x))
       
    57 	  (body (cdr x)))
       
    58       (when (or (eq feature t)
       
    59 		(featurep feature))
       
    60 	(return (cons 'progn body))))))
       
    61 
    53 
    62 
    54 ;;; XEmacs has view-less, while GNU Emacs has view.  Joy.
    63 ;;; XEmacs has view-less, while GNU Emacs has view.  Joy.
    55 
    64 
    56 (condition-case nil
    65 (hg-feature-cond
    57     (require 'view-less)
    66  (xemacs (require 'view-less))
    58   (error nil))
    67  (t (require 'view)))
    59 (condition-case nil
       
    60     (require 'view)
       
    61   (error nil))
       
    62 
    68 
    63 
    69 
    64 ;;; Variables accessible through the custom system.
    70 ;;; Variables accessible through the custom system.
    65 
    71 
    66 (defgroup mercurial nil
    72 (defgroup mercurial nil
   145   :group 'mercurial)
   151   :group 'mercurial)
   146 
   152 
   147 
   153 
   148 ;;; Other variables.
   154 ;;; Other variables.
   149 
   155 
   150 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
       
   151   "Is mercurial.el running under XEmacs?")
       
   152 
       
   153 (defvar hg-mode nil
   156 (defvar hg-mode nil
   154   "Is this file managed by Mercurial?")
   157   "Is this file managed by Mercurial?")
   155 (make-variable-buffer-local 'hg-mode)
   158 (make-variable-buffer-local 'hg-mode)
   156 (put 'hg-mode 'permanent-local t)
   159 (put 'hg-mode 'permanent-local t)
   157 
   160 
   165 
   168 
   166 (defvar hg-root nil)
   169 (defvar hg-root nil)
   167 (make-variable-buffer-local 'hg-root)
   170 (make-variable-buffer-local 'hg-root)
   168 (put 'hg-root 'permanent-local t)
   171 (put 'hg-root 'permanent-local t)
   169 
   172 
       
   173 (defvar hg-view-mode nil)
       
   174 (make-variable-buffer-local 'hg-view-mode)
       
   175 (put 'hg-view-mode 'permanent-local t)
       
   176 
       
   177 (defvar hg-view-file-name nil)
       
   178 (make-variable-buffer-local 'hg-view-file-name)
       
   179 (put 'hg-view-file-name 'permanent-local t)
       
   180 
   170 (defvar hg-output-buffer-name "*Hg*"
   181 (defvar hg-output-buffer-name "*Hg*"
   171   "The name to use for Mercurial output buffers.")
   182   "The name to use for Mercurial output buffers.")
   172 
   183 
   173 (defvar hg-file-history nil)
   184 (defvar hg-file-history nil)
   174 (defvar hg-repo-history nil)
   185 (defvar hg-repo-history nil)
   175 (defvar hg-rev-history nil)
   186 (defvar hg-rev-history nil)
       
   187 (defvar hg-repo-completion-table nil)	; shut up warnings
   176 
   188 
   177 
   189 
   178 ;;; Random constants.
   190 ;;; Random constants.
   179 
   191 
   180 (defconst hg-commit-message-start
   192 (defconst hg-commit-message-start
   181   "--- Enter your commit message.  Type `C-c C-c' to commit. ---\n")
   193   "--- Enter your commit message.  Type `C-c C-c' to commit. ---\n")
   182 
   194 
   183 (defconst hg-commit-message-end
   195 (defconst hg-commit-message-end
   184   "--- Files in bold will be committed.  Click to toggle selection. ---\n")
   196   "--- Files in bold will be committed.  Click to toggle selection. ---\n")
   185 
   197 
       
   198 (defconst hg-state-alist
       
   199   '((?M . modified)
       
   200     (?A . added)
       
   201     (?R . removed)
       
   202     (?! . deleted)
       
   203     (?C . normal)
       
   204     (?I . ignored)
       
   205     (?? . nil)))
   186 
   206 
   187 ;;; hg-mode keymap.
   207 ;;; hg-mode keymap.
   188 
   208 
   189 (defvar hg-mode-map (make-sparse-keymap))
       
   190 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
       
   191 
       
   192 (defvar hg-prefix-map
   209 (defvar hg-prefix-map
   193   (let ((map (copy-keymap vc-prefix-map)))
   210   (let ((map (make-sparse-keymap)))
   194     (if (functionp 'set-keymap-name)
   211     (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
   195       (set-keymap-name map 'hg-prefix-map)); XEmacs
   212     (set-keymap-parent map vc-prefix-map)
       
   213     (define-key map "=" 'hg-diff)
       
   214     (define-key map "c" 'hg-undo)
       
   215     (define-key map "g" 'hg-annotate)
       
   216     (define-key map "i" 'hg-add)
       
   217     (define-key map "l" 'hg-log)
       
   218     (define-key map "n" 'hg-commit-start)
       
   219     ;; (define-key map "r" 'hg-update)
       
   220     (define-key map "u" 'hg-revert-buffer)
       
   221     (define-key map "~" 'hg-version-other-window)
   196     map)
   222     map)
   197   "This keymap overrides some default vc-mode bindings.")
   223   "This keymap overrides some default vc-mode bindings.")
   198 (fset 'hg-prefix-map hg-prefix-map)
   224 
   199 (define-key hg-prefix-map "=" 'hg-diff)
   225 (defvar hg-mode-map
   200 (define-key hg-prefix-map "c" 'hg-undo)
   226   (let ((map (make-sparse-keymap)))
   201 (define-key hg-prefix-map "g" 'hg-annotate)
   227     (define-key map "\C-xv" hg-prefix-map)
   202 (define-key hg-prefix-map "l" 'hg-log)
   228     map))
   203 (define-key hg-prefix-map "n" 'hg-commit-start)
       
   204 ;; (define-key hg-prefix-map "r" 'hg-update)
       
   205 (define-key hg-prefix-map "u" 'hg-revert-buffer)
       
   206 (define-key hg-prefix-map "~" 'hg-version-other-window)
       
   207 
   229 
   208 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
   230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
   209 
   231 
   210 
   232 
   211 ;;; Global keymap.
   233 ;;; Global keymap.
   212 
   234 
   213 (global-set-key "\C-xvi" 'hg-add)
   235 (defvar hg-global-map
   214 
   236   (let ((map (make-sparse-keymap)))
   215 (defvar hg-global-map (make-sparse-keymap))
   237     (define-key map "," 'hg-incoming)
   216 (fset 'hg-global-map hg-global-map)
   238     (define-key map "." 'hg-outgoing)
   217 (global-set-key hg-global-prefix 'hg-global-map)
   239     (define-key map "<" 'hg-pull)
   218 (define-key hg-global-map "," 'hg-incoming)
   240     (define-key map "=" 'hg-diff-repo)
   219 (define-key hg-global-map "." 'hg-outgoing)
   241     (define-key map ">" 'hg-push)
   220 (define-key hg-global-map "<" 'hg-pull)
   242     (define-key map "?" 'hg-help-overview)
   221 (define-key hg-global-map "=" 'hg-diff-repo)
   243     (define-key map "A" 'hg-addremove)
   222 (define-key hg-global-map ">" 'hg-push)
   244     (define-key map "U" 'hg-revert)
   223 (define-key hg-global-map "?" 'hg-help-overview)
   245     (define-key map "a" 'hg-add)
   224 (define-key hg-global-map "A" 'hg-addremove)
   246     (define-key map "c" 'hg-commit-start)
   225 (define-key hg-global-map "U" 'hg-revert)
   247     (define-key map "f" 'hg-forget)
   226 (define-key hg-global-map "a" 'hg-add)
   248     (define-key map "h" 'hg-help-overview)
   227 (define-key hg-global-map "c" 'hg-commit-start)
   249     (define-key map "i" 'hg-init)
   228 (define-key hg-global-map "f" 'hg-forget)
   250     (define-key map "l" 'hg-log-repo)
   229 (define-key hg-global-map "h" 'hg-help-overview)
   251     (define-key map "r" 'hg-root)
   230 (define-key hg-global-map "i" 'hg-init)
   252     (define-key map "s" 'hg-status)
   231 (define-key hg-global-map "l" 'hg-log-repo)
   253     (define-key map "u" 'hg-update)
   232 (define-key hg-global-map "r" 'hg-root)
   254     map))
   233 (define-key hg-global-map "s" 'hg-status)
   255 
   234 (define-key hg-global-map "u" 'hg-update)
   256 (global-set-key hg-global-prefix hg-global-map)
   235 
       
   236 
   257 
   237 ;;; View mode keymap.
   258 ;;; View mode keymap.
   238 
   259 
   239 (defvar hg-view-mode-map
   260 (defvar hg-view-mode-map
   240   (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
   261   (let ((map (make-sparse-keymap)))
   241 			      view-minor-mode-map
   262     (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
   242 			    view-mode-map))))
   263     (define-key map (hg-feature-cond (xemacs [button2])
   243     (if (functionp 'set-keymap-name)
   264 				     (t [mouse-2]))
   244       (set-keymap-name map 'hg-view-mode-map)); XEmacs
   265       'hg-buffer-mouse-clicked)
   245     map))
   266     map))
   246 (fset 'hg-view-mode-map hg-view-mode-map)
   267 
   247 (define-key hg-view-mode-map
   268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
   248   (if hg-running-xemacs [button2] [mouse-2])
       
   249   'hg-buffer-mouse-clicked)
       
   250 
   269 
   251 
   270 
   252 ;;; Commit mode keymaps.
   271 ;;; Commit mode keymaps.
   253 
   272 
   254 (defvar hg-commit-mode-map (make-sparse-keymap))
   273 (defvar hg-commit-mode-map
   255 (define-key hg-commit-mode-map "\C-c\C-c" 'hg-commit-finish)
   274   (let ((map (make-sparse-keymap)))
   256 (define-key hg-commit-mode-map "\C-c\C-k" 'hg-commit-kill)
   275     (define-key map "\C-c\C-c" 'hg-commit-finish)
   257 (define-key hg-commit-mode-map "\C-xv=" 'hg-diff-repo)
   276     (define-key map "\C-c\C-k" 'hg-commit-kill)
   258 
   277     (define-key map "\C-xv=" 'hg-diff-repo)
   259 (defvar hg-commit-mode-file-map (make-sparse-keymap))
   278     map))
   260 (define-key hg-commit-mode-file-map
   279 
   261   (if hg-running-xemacs [button2] [mouse-2])
   280 (defvar hg-commit-mode-file-map
   262   'hg-commit-mouse-clicked)
   281   (let ((map (make-sparse-keymap)))
   263 (define-key hg-commit-mode-file-map " " 'hg-commit-toggle-file)
   282     (define-key map (hg-feature-cond (xemacs [button2])
   264 (define-key hg-commit-mode-file-map "\r" 'hg-commit-toggle-file)
   283 				     (t [mouse-2]))
       
   284       'hg-commit-mouse-clicked)
       
   285     (define-key map " " 'hg-commit-toggle-file)
       
   286     (define-key map "\r" 'hg-commit-toggle-file)
       
   287     map))
   265 
   288 
   266 
   289 
   267 ;;; Convenience functions.
   290 ;;; Convenience functions.
   268 
   291 
   269 (defsubst hg-binary ()
   292 (defsubst hg-binary ()
   276 Return the new string.  Optional LITERAL non-nil means do a literal
   299 Return the new string.  Optional LITERAL non-nil means do a literal
   277 replacement.
   300 replacement.
   278 
   301 
   279 This function bridges yet another pointless impedance gap between
   302 This function bridges yet another pointless impedance gap between
   280 XEmacs and GNU Emacs."
   303 XEmacs and GNU Emacs."
   281   (if (fboundp 'replace-in-string)
   304   (hg-feature-cond
   282       (replace-in-string str regexp newtext literal)
   305    (xemacs (replace-in-string str regexp newtext literal))
   283     (replace-regexp-in-string regexp newtext str nil literal)))
   306    (t (replace-regexp-in-string regexp newtext str nil literal))))
   284 
   307 
   285 (defsubst hg-strip (str)
   308 (defsubst hg-strip (str)
   286   "Strip leading and trailing blank lines from a string."
   309   "Strip leading and trailing blank lines from a string."
   287   (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
   310   (hg-replace-in-string (hg-replace-in-string str "[\r\n][ \t\r\n]*\\'" "")
   288 			"\\`[ \t\r\n]*[\r\n]" ""))
   311 			"\\`[ \t\r\n]*[\r\n]" ""))
   316 	       (cons command args)
   339 	       (cons command args)
   317 	       (car res))
   340 	       (car res))
   318       (cdr res))))
   341       (cdr res))))
   319 
   342 
   320 (defmacro hg-do-across-repo (path &rest body)
   343 (defmacro hg-do-across-repo (path &rest body)
   321   (let ((root-name (gensym "root-"))
   344   (let ((root-name (make-symbol "root-"))
   322 	(buf-name (gensym "buf-")))
   345 	(buf-name (make-symbol "buf-")))
   323     `(let ((,root-name (hg-root ,path)))
   346     `(let ((,root-name (hg-root ,path)))
   324        (save-excursion
   347        (save-excursion
   325 	 (dolist (,buf-name (buffer-list))
   348 	 (dolist (,buf-name (buffer-list))
   326 	   (set-buffer ,buf-name)
   349 	   (set-buffer ,buf-name)
   327 	   (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
   350 	   (when (and hg-status (equal (hg-root buffer-file-name) ,root-name))
   342 
   365 
   343 (defun hg-buffer-commands (pnt)
   366 (defun hg-buffer-commands (pnt)
   344   "Use the properties of a character to do something sensible."
   367   "Use the properties of a character to do something sensible."
   345   (interactive "d")
   368   (interactive "d")
   346   (let ((rev (get-char-property pnt 'rev))
   369   (let ((rev (get-char-property pnt 'rev))
   347 	(file (get-char-property pnt 'file))
   370 	(file (get-char-property pnt 'file)))
   348 	(date (get-char-property pnt 'date))
       
   349 	(user (get-char-property pnt 'user))
       
   350 	(host (get-char-property pnt 'host))
       
   351 	(prev-buf (current-buffer)))
       
   352     (cond
   371     (cond
   353      (file
   372      (file
   354       (find-file-other-window file))
   373       (find-file-other-window file))
   355      (rev
   374      (rev
   356       (hg-diff hg-view-file-name rev rev prev-buf))
   375       (hg-diff hg-view-file-name rev rev))
   357      ((message "I don't know how to do that yet")))))
   376      ((message "I don't know how to do that yet")))))
   358 
   377 
   359 (defsubst hg-event-point (event)
   378 (defsubst hg-event-point (event)
   360   "Return the character position of the mouse event EVENT."
   379   "Return the character position of the mouse event EVENT."
   361   (if hg-running-xemacs
   380   (hg-feature-cond (xemacs (event-point event))
   362       (event-point event)
   381 		   (t (posn-point (event-start event)))))
   363     (posn-point (event-start event))))
       
   364 
   382 
   365 (defsubst hg-event-window (event)
   383 (defsubst hg-event-window (event)
   366   "Return the window over which mouse event EVENT occurred."
   384   "Return the window over which mouse event EVENT occurred."
   367   (if hg-running-xemacs
   385   (hg-feature-cond (xemacs (event-window event))
   368       (event-window event)
   386 		   (t (posn-window (event-start event)))))
   369     (posn-window (event-start event))))
       
   370 
   387 
   371 (defun hg-buffer-mouse-clicked (event)
   388 (defun hg-buffer-mouse-clicked (event)
   372   "Translate the mouse clicks in a HG log buffer to character events.
   389   "Translate the mouse clicks in a HG log buffer to character events.
   373 These are then handed off to `hg-buffer-commands'.
   390 These are then handed off to `hg-buffer-commands'.
   374 
   391 
   375 Handle frickin' frackin' gratuitous event-related incompatibilities."
   392 Handle frickin' frackin' gratuitous event-related incompatibilities."
   376   (interactive "e")
   393   (interactive "e")
   377   (select-window (hg-event-window event))
   394   (select-window (hg-event-window event))
   378   (hg-buffer-commands (hg-event-point event)))
   395   (hg-buffer-commands (hg-event-point event)))
   379 
   396 
   380 (unless (fboundp 'view-minor-mode)
       
   381   (defun view-minor-mode (prev-buffer exit-func)
       
   382     (view-mode)))
       
   383 
       
   384 (defsubst hg-abbrev-file-name (file)
   397 (defsubst hg-abbrev-file-name (file)
   385   "Portable wrapper around abbreviate-file-name."
   398   "Portable wrapper around abbreviate-file-name."
   386   (if hg-running-xemacs
   399   (hg-feature-cond (xemacs (abbreviate-file-name file t))
   387       (abbreviate-file-name file t)
   400 		   (t (abbreviate-file-name file))))
   388     (abbreviate-file-name file)))
       
   389 
   401 
   390 (defun hg-read-file-name (&optional prompt default)
   402 (defun hg-read-file-name (&optional prompt default)
   391   "Read a file or directory name, or a pattern, to use with a command."
   403   "Read a file or directory name, or a pattern, to use with a command."
   392   (save-excursion
   404   (save-excursion
   393     (while hg-prev-buffer
   405     (while hg-prev-buffer
   401                         (format "File, directory or pattern%s: "
   413                         (format "File, directory or pattern%s: "
   402                                 (or prompt ""))
   414                                 (or prompt ""))
   403                         (and path (file-name-directory path))
   415                         (and path (file-name-directory path))
   404                         nil nil
   416                         nil nil
   405                         (and path (file-name-nondirectory path))
   417                         (and path (file-name-nondirectory path))
   406                         (if hg-running-xemacs
   418                         (hg-feature-cond
   407                             (cons (quote 'hg-file-history) nil)
   419 			 (xemacs (cons (quote 'hg-file-history) nil))
   408                           nil))))
   420 			 (t nil)))))
   409         path))))
   421         path))))
   410 
   422 
   411 (defun hg-read-number (&optional prompt default)
   423 (defun hg-read-number (&optional prompt default)
   412   "Read a integer value."
   424   "Read a integer value."
   413   (save-excursion
   425   (save-excursion
   475       (if current-prefix-arg
   487       (if current-prefix-arg
   476 	  (progn
   488 	  (progn
   477 	    (dolist (path (hg-config-section "paths" (hg-read-config)))
   489 	    (dolist (path (hg-config-section "paths" (hg-read-config)))
   478 	      (setq hg-repo-completion-table
   490 	      (setq hg-repo-completion-table
   479 		    (cons (cons (car path) t) hg-repo-completion-table))
   491 		    (cons (cons (car path) t) hg-repo-completion-table))
   480 	      (unless (hg-string-starts-with directory-sep-char (cdr path))
   492 	      (unless (hg-string-starts-with (hg-feature-cond
       
   493 					      (xemacs directory-sep-char)
       
   494 					      (t ?/))
       
   495 					     (cdr path))
   481 		(setq hg-repo-completion-table
   496 		(setq hg-repo-completion-table
   482 		      (cons (cons (cdr path) t) hg-repo-completion-table))))
   497 		      (cons (cons (cdr path) t) hg-repo-completion-table))))
   483 	    (completing-read (format "Repository%s: " (or prompt ""))
   498 	    (completing-read (format "Repository%s: " (or prompt ""))
   484 			     'hg-complete-repo
   499 			     'hg-complete-repo
   485 			     nil
   500 			     nil
   496       (set-buffer hg-prev-buffer))
   511       (set-buffer hg-prev-buffer))
   497     (let ((rev (or default "tip")))
   512     (let ((rev (or default "tip")))
   498       (if current-prefix-arg
   513       (if current-prefix-arg
   499 	  (let ((revs (split-string
   514 	  (let ((revs (split-string
   500 		       (hg-chomp
   515 		       (hg-chomp
   501 			(hg-run0 "-q" "log" "-r"
   516 			(hg-run0 "-q" "log" "-l"
   502 				 (format "-%d:tip" hg-rev-completion-limit)))
   517 				 (format "%d" hg-rev-completion-limit)))
   503 		       "[\n:]")))
   518 		       "[\n:]")))
   504 	    (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
   519 	    (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
   505 	      (setq revs (cons (car (split-string line "\\s-")) revs)))
   520 	      (setq revs (cons (car (split-string line "\\s-")) revs)))
   506 	    (completing-read (format "Revision%s (%s): "
   521 	    (completing-read (format "Revision%s (%s): "
   507 				     (or prompt "")
   522 				     (or prompt "")
   566 
   581 
   567 (defun hg-view-mode (prev-buffer &optional file-name)
   582 (defun hg-view-mode (prev-buffer &optional file-name)
   568   (goto-char (point-min))
   583   (goto-char (point-min))
   569   (set-buffer-modified-p nil)
   584   (set-buffer-modified-p nil)
   570   (toggle-read-only t)
   585   (toggle-read-only t)
   571   (view-minor-mode prev-buffer 'hg-exit-view-mode)
   586   (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
   572   (use-local-map hg-view-mode-map)
   587 		   (t (view-mode-enter nil 'hg-exit-view-mode)))
       
   588   (setq hg-view-mode t)
   573   (setq truncate-lines t)
   589   (setq truncate-lines t)
   574   (when file-name
   590   (when file-name
   575     (set (make-local-variable 'hg-view-file-name)
   591     (setq hg-view-file-name 
   576 	 (hg-abbrev-file-name file-name))))
   592 	  (hg-abbrev-file-name file-name))))
   577 
   593 
   578 (defun hg-file-status (file)
   594 (defun hg-file-status (file)
   579   "Return status of FILE, or nil if FILE does not exist or is unmanaged."
   595   "Return status of FILE, or nil if FILE does not exist or is unmanaged."
   580   (let* ((s (hg-run "status" file))
   596   (let* ((s (hg-run "status" file))
   581 	 (exit (car s))
   597 	 (exit (car s))
   582 	 (output (cdr s)))
   598 	 (output (cdr s)))
   583     (if (= exit 0)
   599     (if (= exit 0)
   584 	(let ((state (assoc (substring output 0 (min (length output) 2))
   600 	(let ((state (and (>= (length output) 2)
   585 			    '(("M " . modified)
   601 			  (= (aref output 1) ? )
   586 			      ("A " . added)
   602 			  (assq (aref output 0) hg-state-alist))))
   587 			      ("R " . removed)
       
   588 			      ("! " . deleted)
       
   589 			      ("? " . nil)))))
       
   590 	  (if state
   603 	  (if state
   591 	      (cdr state)
   604 	      (cdr state)
   592 	    'normal)))))
   605 	    'normal)))))
   593 
   606 
   594 (defun hg-path-status (root paths)
   607 (defun hg-path-status (root paths)
   596 Each entry is a pair (FILE-NAME . STATUS)."
   609 Each entry is a pair (FILE-NAME . STATUS)."
   597   (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
   610   (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
   598 	result)
   611 	result)
   599     (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
   612     (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
   600       (let (state name)
   613       (let (state name)
   601 	(if (equal (substring entry 1 2) " ")
   614 	(cond ((= (aref entry 1) ? )
   602 	    (setq state (cdr (assoc (substring entry 0 2)
   615 	       (setq state (assq (aref entry 0) hg-state-alist)
   603 				    '(("M " . modified)
   616 		     name (substring entry 2)))
   604 				      ("A " . added)
   617 	      ((string-match "\\(.*\\): " entry)
   605 				      ("R " . removed)
   618 	       (setq name (match-string 1 entry))))
   606 				      ("! " . deleted)
       
   607 				      ("C " . normal)
       
   608 				      ("I " . ignored)
       
   609 				      ("? " . nil))))
       
   610 		  name (substring entry 2))
       
   611 	  (setq name (substring entry 0 (search ": " entry :from-end t))))
       
   612 	(setq result (cons (cons name state) result))))))
   619 	(setq result (cons (cons name state) result))))))
   613 
   620 
   614 (defmacro hg-view-output (args &rest body)
   621 (defmacro hg-view-output (args &rest body)
   615   "Execute BODY in a clean buffer, then quickly display that buffer.
   622   "Execute BODY in a clean buffer, then quickly display that buffer.
   616 If the buffer contains one line, its contents are displayed in the
   623 If the buffer contains one line, its contents are displayed in the
   617 minibuffer.  Otherwise, the buffer is displayed in view-mode.
   624 minibuffer.  Otherwise, the buffer is displayed in view-mode.
   618 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
   625 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
   619 the name of the buffer to create, and FILE is the name of the file
   626 the name of the buffer to create, and FILE is the name of the file
   620 being viewed."
   627 being viewed."
   621   (let ((prev-buf (gensym "prev-buf-"))
   628   (let ((prev-buf (make-symbol "prev-buf-"))
   622 	(v-b-name (car args))
   629 	(v-b-name (car args))
   623 	(v-m-rest (cdr args)))
   630 	(v-m-rest (cdr args)))
   624     `(let ((view-buf-name ,v-b-name)
   631     `(let ((view-buf-name ,v-b-name)
   625 	   (,prev-buf (current-buffer)))
   632 	   (,prev-buf (current-buffer)))
   626        (get-buffer-create view-buf-name)
   633        (get-buffer-create view-buf-name)