##// END OF EJS Templates
mercurial.el: change default log range to tip:0...
NIIMI Satoshi -
r4693:3f484688 default
parent child Browse files
Show More
@@ -1,1275 +1,1273 b''
1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2
2
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
4
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
6
7 ;; mercurial.el is free software; you can redistribute it and/or
7 ;; mercurial.el is free software; you can redistribute it and/or
8 ;; modify it under the terms of version 2 of the GNU General Public
8 ;; modify it under the terms of version 2 of the GNU General Public
9 ;; License as published by the Free Software Foundation.
9 ;; License as published by the Free Software Foundation.
10
10
11 ;; mercurial.el is distributed in the hope that it will be useful, but
11 ;; mercurial.el is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
14 ;; General Public License for more details.
15
15
16 ;; You should have received a copy of the GNU General Public License
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
17 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
18 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
20
21 ;;; Commentary:
21 ;;; Commentary:
22
22
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
23 ;; mercurial.el builds upon Emacs's VC mode to provide flexible
24 ;; integration with the Mercurial distributed SCM tool.
24 ;; integration with the Mercurial distributed SCM tool.
25
25
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
26 ;; To get going as quickly as possible, load mercurial.el into Emacs and
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
27 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
28 ;; usage overview.
28 ;; usage overview.
29
29
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
30 ;; Much of the inspiration for mercurial.el comes from Rajesh
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
31 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
32 ;; job for the commercial Perforce SCM product. In fact, substantial
32 ;; job for the commercial Perforce SCM product. In fact, substantial
33 ;; chunks of code are adapted from p4.el.
33 ;; chunks of code are adapted from p4.el.
34
34
35 ;; This code has been developed under XEmacs 21.5, and may not work as
35 ;; This code has been developed under XEmacs 21.5, and may not work as
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
36 ;; well under GNU Emacs (albeit tested under 21.4). Patches to
37 ;; enhance the portability of this code, fix bugs, and add features
37 ;; enhance the portability of this code, fix bugs, and add features
38 ;; are most welcome. You can clone a Mercurial repository for this
38 ;; are most welcome. You can clone a Mercurial repository for this
39 ;; package from http://www.serpentine.com/hg/hg-emacs
39 ;; package from http://www.serpentine.com/hg/hg-emacs
40
40
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 (eval-when-compile (require 'cl))
46 (eval-when-compile (require 'cl))
47 (require 'diff-mode)
47 (require 'diff-mode)
48 (require 'easymenu)
48 (require 'easymenu)
49 (require 'executable)
49 (require 'executable)
50 (require 'vc)
50 (require 'vc)
51
51
52 (defmacro hg-feature-cond (&rest clauses)
52 (defmacro hg-feature-cond (&rest clauses)
53 "Test CLAUSES for feature at compile time.
53 "Test CLAUSES for feature at compile time.
54 Each clause is (FEATURE BODY...)."
54 Each clause is (FEATURE BODY...)."
55 (dolist (x clauses)
55 (dolist (x clauses)
56 (let ((feature (car x))
56 (let ((feature (car x))
57 (body (cdr x)))
57 (body (cdr x)))
58 (when (or (eq feature t)
58 (when (or (eq feature t)
59 (featurep feature))
59 (featurep feature))
60 (return (cons 'progn body))))))
60 (return (cons 'progn body))))))
61
61
62
62
63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
63 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
64
64
65 (hg-feature-cond
65 (hg-feature-cond
66 (xemacs (require 'view-less))
66 (xemacs (require 'view-less))
67 (t (require 'view)))
67 (t (require 'view)))
68
68
69
69
70 ;;; Variables accessible through the custom system.
70 ;;; Variables accessible through the custom system.
71
71
72 (defgroup mercurial nil
72 (defgroup mercurial nil
73 "Mercurial distributed SCM."
73 "Mercurial distributed SCM."
74 :group 'tools)
74 :group 'tools)
75
75
76 (defcustom hg-binary
76 (defcustom hg-binary
77 (or (executable-find "hg")
77 (or (executable-find "hg")
78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
78 (dolist (path '("~/bin/hg" "/usr/bin/hg" "/usr/local/bin/hg"))
79 (when (file-executable-p path)
79 (when (file-executable-p path)
80 (return path))))
80 (return path))))
81 "The path to Mercurial's hg executable."
81 "The path to Mercurial's hg executable."
82 :type '(file :must-match t)
82 :type '(file :must-match t)
83 :group 'mercurial)
83 :group 'mercurial)
84
84
85 (defcustom hg-mode-hook nil
85 (defcustom hg-mode-hook nil
86 "Hook run when a buffer enters hg-mode."
86 "Hook run when a buffer enters hg-mode."
87 :type 'sexp
87 :type 'sexp
88 :group 'mercurial)
88 :group 'mercurial)
89
89
90 (defcustom hg-commit-mode-hook nil
90 (defcustom hg-commit-mode-hook nil
91 "Hook run when a buffer is created to prepare a commit."
91 "Hook run when a buffer is created to prepare a commit."
92 :type 'sexp
92 :type 'sexp
93 :group 'mercurial)
93 :group 'mercurial)
94
94
95 (defcustom hg-pre-commit-hook nil
95 (defcustom hg-pre-commit-hook nil
96 "Hook run before a commit is performed.
96 "Hook run before a commit is performed.
97 If you want to prevent the commit from proceeding, raise an error."
97 If you want to prevent the commit from proceeding, raise an error."
98 :type 'sexp
98 :type 'sexp
99 :group 'mercurial)
99 :group 'mercurial)
100
100
101 (defcustom hg-log-mode-hook nil
101 (defcustom hg-log-mode-hook nil
102 "Hook run after a buffer is filled with log information."
102 "Hook run after a buffer is filled with log information."
103 :type 'sexp
103 :type 'sexp
104 :group 'mercurial)
104 :group 'mercurial)
105
105
106 (defcustom hg-global-prefix "\C-ch"
106 (defcustom hg-global-prefix "\C-ch"
107 "The global prefix for Mercurial keymap bindings."
107 "The global prefix for Mercurial keymap bindings."
108 :type 'sexp
108 :type 'sexp
109 :group 'mercurial)
109 :group 'mercurial)
110
110
111 (defcustom hg-commit-allow-empty-message nil
111 (defcustom hg-commit-allow-empty-message nil
112 "Whether to allow changes to be committed with empty descriptions."
112 "Whether to allow changes to be committed with empty descriptions."
113 :type 'boolean
113 :type 'boolean
114 :group 'mercurial)
114 :group 'mercurial)
115
115
116 (defcustom hg-commit-allow-empty-file-list nil
116 (defcustom hg-commit-allow-empty-file-list nil
117 "Whether to allow changes to be committed without any modified files."
117 "Whether to allow changes to be committed without any modified files."
118 :type 'boolean
118 :type 'boolean
119 :group 'mercurial)
119 :group 'mercurial)
120
120
121 (defcustom hg-rev-completion-limit 100
121 (defcustom hg-rev-completion-limit 100
122 "The maximum number of revisions that hg-read-rev will offer to complete.
122 "The maximum number of revisions that hg-read-rev will offer to complete.
123 This affects memory usage and performance when prompting for revisions
123 This affects memory usage and performance when prompting for revisions
124 in a repository with a lot of history."
124 in a repository with a lot of history."
125 :type 'integer
125 :type 'integer
126 :group 'mercurial)
126 :group 'mercurial)
127
127
128 (defcustom hg-log-limit 50
128 (defcustom hg-log-limit 50
129 "The maximum number of revisions that hg-log will display."
129 "The maximum number of revisions that hg-log will display."
130 :type 'integer
130 :type 'integer
131 :group 'mercurial)
131 :group 'mercurial)
132
132
133 (defcustom hg-update-modeline t
133 (defcustom hg-update-modeline t
134 "Whether to update the modeline with the status of a file after every save.
134 "Whether to update the modeline with the status of a file after every save.
135 Set this to nil on platforms with poor process management, such as Windows."
135 Set this to nil on platforms with poor process management, such as Windows."
136 :type 'boolean
136 :type 'boolean
137 :group 'mercurial)
137 :group 'mercurial)
138
138
139 (defcustom hg-incoming-repository "default"
139 (defcustom hg-incoming-repository "default"
140 "The repository from which changes are pulled from by default.
140 "The repository from which changes are pulled from by default.
141 This should be a symbolic repository name, since it is used for all
141 This should be a symbolic repository name, since it is used for all
142 repository-related commands."
142 repository-related commands."
143 :type 'string
143 :type 'string
144 :group 'mercurial)
144 :group 'mercurial)
145
145
146 (defcustom hg-outgoing-repository "default-push"
146 (defcustom hg-outgoing-repository "default-push"
147 "The repository to which changes are pushed to by default.
147 "The repository to which changes are pushed to by default.
148 This should be a symbolic repository name, since it is used for all
148 This should be a symbolic repository name, since it is used for all
149 repository-related commands."
149 repository-related commands."
150 :type 'string
150 :type 'string
151 :group 'mercurial)
151 :group 'mercurial)
152
152
153
153
154 ;;; Other variables.
154 ;;; Other variables.
155
155
156 (defvar hg-mode nil
156 (defvar hg-mode nil
157 "Is this file managed by Mercurial?")
157 "Is this file managed by Mercurial?")
158 (make-variable-buffer-local 'hg-mode)
158 (make-variable-buffer-local 'hg-mode)
159 (put 'hg-mode 'permanent-local t)
159 (put 'hg-mode 'permanent-local t)
160
160
161 (defvar hg-status nil)
161 (defvar hg-status nil)
162 (make-variable-buffer-local 'hg-status)
162 (make-variable-buffer-local 'hg-status)
163 (put 'hg-status 'permanent-local t)
163 (put 'hg-status 'permanent-local t)
164
164
165 (defvar hg-prev-buffer nil)
165 (defvar hg-prev-buffer nil)
166 (make-variable-buffer-local 'hg-prev-buffer)
166 (make-variable-buffer-local 'hg-prev-buffer)
167 (put 'hg-prev-buffer 'permanent-local t)
167 (put 'hg-prev-buffer 'permanent-local t)
168
168
169 (defvar hg-root nil)
169 (defvar hg-root nil)
170 (make-variable-buffer-local 'hg-root)
170 (make-variable-buffer-local 'hg-root)
171 (put 'hg-root 'permanent-local t)
171 (put 'hg-root 'permanent-local t)
172
172
173 (defvar hg-view-mode nil)
173 (defvar hg-view-mode nil)
174 (make-variable-buffer-local 'hg-view-mode)
174 (make-variable-buffer-local 'hg-view-mode)
175 (put 'hg-view-mode 'permanent-local t)
175 (put 'hg-view-mode 'permanent-local t)
176
176
177 (defvar hg-view-file-name nil)
177 (defvar hg-view-file-name nil)
178 (make-variable-buffer-local 'hg-view-file-name)
178 (make-variable-buffer-local 'hg-view-file-name)
179 (put 'hg-view-file-name 'permanent-local t)
179 (put 'hg-view-file-name 'permanent-local t)
180
180
181 (defvar hg-output-buffer-name "*Hg*"
181 (defvar hg-output-buffer-name "*Hg*"
182 "The name to use for Mercurial output buffers.")
182 "The name to use for Mercurial output buffers.")
183
183
184 (defvar hg-file-history nil)
184 (defvar hg-file-history nil)
185 (defvar hg-repo-history nil)
185 (defvar hg-repo-history nil)
186 (defvar hg-rev-history nil)
186 (defvar hg-rev-history nil)
187 (defvar hg-repo-completion-table nil) ; shut up warnings
187 (defvar hg-repo-completion-table nil) ; shut up warnings
188
188
189
189
190 ;;; Random constants.
190 ;;; Random constants.
191
191
192 (defconst hg-commit-message-start
192 (defconst hg-commit-message-start
193 "--- 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")
194
194
195 (defconst hg-commit-message-end
195 (defconst hg-commit-message-end
196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
196 "--- Files in bold will be committed. Click to toggle selection. ---\n")
197
197
198 (defconst hg-state-alist
198 (defconst hg-state-alist
199 '((?M . modified)
199 '((?M . modified)
200 (?A . added)
200 (?A . added)
201 (?R . removed)
201 (?R . removed)
202 (?! . deleted)
202 (?! . deleted)
203 (?C . normal)
203 (?C . normal)
204 (?I . ignored)
204 (?I . ignored)
205 (?? . nil)))
205 (?? . nil)))
206
206
207 ;;; hg-mode keymap.
207 ;;; hg-mode keymap.
208
208
209 (defvar hg-prefix-map
209 (defvar hg-prefix-map
210 (let ((map (make-sparse-keymap)))
210 (let ((map (make-sparse-keymap)))
211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
211 (hg-feature-cond (xemacs (set-keymap-name map 'hg-prefix-map))) ; XEmacs
212 (set-keymap-parent map vc-prefix-map)
212 (set-keymap-parent map vc-prefix-map)
213 (define-key map "=" 'hg-diff)
213 (define-key map "=" 'hg-diff)
214 (define-key map "c" 'hg-undo)
214 (define-key map "c" 'hg-undo)
215 (define-key map "g" 'hg-annotate)
215 (define-key map "g" 'hg-annotate)
216 (define-key map "i" 'hg-add)
216 (define-key map "i" 'hg-add)
217 (define-key map "l" 'hg-log)
217 (define-key map "l" 'hg-log)
218 (define-key map "n" 'hg-commit-start)
218 (define-key map "n" 'hg-commit-start)
219 ;; (define-key map "r" 'hg-update)
219 ;; (define-key map "r" 'hg-update)
220 (define-key map "u" 'hg-revert-buffer)
220 (define-key map "u" 'hg-revert-buffer)
221 (define-key map "~" 'hg-version-other-window)
221 (define-key map "~" 'hg-version-other-window)
222 map)
222 map)
223 "This keymap overrides some default vc-mode bindings.")
223 "This keymap overrides some default vc-mode bindings.")
224
224
225 (defvar hg-mode-map
225 (defvar hg-mode-map
226 (let ((map (make-sparse-keymap)))
226 (let ((map (make-sparse-keymap)))
227 (define-key map "\C-xv" hg-prefix-map)
227 (define-key map "\C-xv" hg-prefix-map)
228 map))
228 map))
229
229
230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
230 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
231
231
232
232
233 ;;; Global keymap.
233 ;;; Global keymap.
234
234
235 (defvar hg-global-map
235 (defvar hg-global-map
236 (let ((map (make-sparse-keymap)))
236 (let ((map (make-sparse-keymap)))
237 (define-key map "," 'hg-incoming)
237 (define-key map "," 'hg-incoming)
238 (define-key map "." 'hg-outgoing)
238 (define-key map "." 'hg-outgoing)
239 (define-key map "<" 'hg-pull)
239 (define-key map "<" 'hg-pull)
240 (define-key map "=" 'hg-diff-repo)
240 (define-key map "=" 'hg-diff-repo)
241 (define-key map ">" 'hg-push)
241 (define-key map ">" 'hg-push)
242 (define-key map "?" 'hg-help-overview)
242 (define-key map "?" 'hg-help-overview)
243 (define-key map "A" 'hg-addremove)
243 (define-key map "A" 'hg-addremove)
244 (define-key map "U" 'hg-revert)
244 (define-key map "U" 'hg-revert)
245 (define-key map "a" 'hg-add)
245 (define-key map "a" 'hg-add)
246 (define-key map "c" 'hg-commit-start)
246 (define-key map "c" 'hg-commit-start)
247 (define-key map "f" 'hg-forget)
247 (define-key map "f" 'hg-forget)
248 (define-key map "h" 'hg-help-overview)
248 (define-key map "h" 'hg-help-overview)
249 (define-key map "i" 'hg-init)
249 (define-key map "i" 'hg-init)
250 (define-key map "l" 'hg-log-repo)
250 (define-key map "l" 'hg-log-repo)
251 (define-key map "r" 'hg-root)
251 (define-key map "r" 'hg-root)
252 (define-key map "s" 'hg-status)
252 (define-key map "s" 'hg-status)
253 (define-key map "u" 'hg-update)
253 (define-key map "u" 'hg-update)
254 map))
254 map))
255
255
256 (global-set-key hg-global-prefix hg-global-map)
256 (global-set-key hg-global-prefix hg-global-map)
257
257
258 ;;; View mode keymap.
258 ;;; View mode keymap.
259
259
260 (defvar hg-view-mode-map
260 (defvar hg-view-mode-map
261 (let ((map (make-sparse-keymap)))
261 (let ((map (make-sparse-keymap)))
262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
262 (hg-feature-cond (xemacs (set-keymap-name map 'hg-view-mode-map))) ; XEmacs
263 (define-key map (hg-feature-cond (xemacs [button2])
263 (define-key map (hg-feature-cond (xemacs [button2])
264 (t [mouse-2]))
264 (t [mouse-2]))
265 'hg-buffer-mouse-clicked)
265 'hg-buffer-mouse-clicked)
266 map))
266 map))
267
267
268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
268 (add-minor-mode 'hg-view-mode "" hg-view-mode-map)
269
269
270
270
271 ;;; Commit mode keymaps.
271 ;;; Commit mode keymaps.
272
272
273 (defvar hg-commit-mode-map
273 (defvar hg-commit-mode-map
274 (let ((map (make-sparse-keymap)))
274 (let ((map (make-sparse-keymap)))
275 (define-key map "\C-c\C-c" 'hg-commit-finish)
275 (define-key map "\C-c\C-c" 'hg-commit-finish)
276 (define-key map "\C-c\C-k" 'hg-commit-kill)
276 (define-key map "\C-c\C-k" 'hg-commit-kill)
277 (define-key map "\C-xv=" 'hg-diff-repo)
277 (define-key map "\C-xv=" 'hg-diff-repo)
278 map))
278 map))
279
279
280 (defvar hg-commit-mode-file-map
280 (defvar hg-commit-mode-file-map
281 (let ((map (make-sparse-keymap)))
281 (let ((map (make-sparse-keymap)))
282 (define-key map (hg-feature-cond (xemacs [button2])
282 (define-key map (hg-feature-cond (xemacs [button2])
283 (t [mouse-2]))
283 (t [mouse-2]))
284 'hg-commit-mouse-clicked)
284 'hg-commit-mouse-clicked)
285 (define-key map " " 'hg-commit-toggle-file)
285 (define-key map " " 'hg-commit-toggle-file)
286 (define-key map "\r" 'hg-commit-toggle-file)
286 (define-key map "\r" 'hg-commit-toggle-file)
287 map))
287 map))
288
288
289
289
290 ;;; Convenience functions.
290 ;;; Convenience functions.
291
291
292 (defsubst hg-binary ()
292 (defsubst hg-binary ()
293 (if hg-binary
293 (if hg-binary
294 hg-binary
294 hg-binary
295 (error "No `hg' executable found!")))
295 (error "No `hg' executable found!")))
296
296
297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
297 (defsubst hg-replace-in-string (str regexp newtext &optional literal)
298 "Replace all matches in STR for REGEXP with NEWTEXT string.
298 "Replace all matches in STR for REGEXP with NEWTEXT string.
299 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
300 replacement.
300 replacement.
301
301
302 This function bridges yet another pointless impedance gap between
302 This function bridges yet another pointless impedance gap between
303 XEmacs and GNU Emacs."
303 XEmacs and GNU Emacs."
304 (hg-feature-cond
304 (hg-feature-cond
305 (xemacs (replace-in-string str regexp newtext literal))
305 (xemacs (replace-in-string str regexp newtext literal))
306 (t (replace-regexp-in-string regexp newtext str nil literal))))
306 (t (replace-regexp-in-string regexp newtext str nil literal))))
307
307
308 (defsubst hg-strip (str)
308 (defsubst hg-strip (str)
309 "Strip leading and trailing blank lines from a string."
309 "Strip leading and trailing blank lines from a string."
310 (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]*\\'" "")
311 "\\`[ \t\r\n]*[\r\n]" ""))
311 "\\`[ \t\r\n]*[\r\n]" ""))
312
312
313 (defsubst hg-chomp (str)
313 (defsubst hg-chomp (str)
314 "Strip trailing newlines from a string."
314 "Strip trailing newlines from a string."
315 (hg-replace-in-string str "[\r\n]+\\'" ""))
315 (hg-replace-in-string str "[\r\n]+\\'" ""))
316
316
317 (defun hg-run-command (command &rest args)
317 (defun hg-run-command (command &rest args)
318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
318 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
319 The list ARGS contains a list of arguments to pass to the command."
319 The list ARGS contains a list of arguments to pass to the command."
320 (let* (exit-code
320 (let* (exit-code
321 (output
321 (output
322 (with-output-to-string
322 (with-output-to-string
323 (with-current-buffer
323 (with-current-buffer
324 standard-output
324 standard-output
325 (setq exit-code
325 (setq exit-code
326 (apply 'call-process command nil t nil args))))))
326 (apply 'call-process command nil t nil args))))))
327 (cons exit-code output)))
327 (cons exit-code output)))
328
328
329 (defun hg-run (command &rest args)
329 (defun hg-run (command &rest args)
330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
330 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
331 (apply 'hg-run-command (hg-binary) command args))
331 (apply 'hg-run-command (hg-binary) command args))
332
332
333 (defun hg-run0 (command &rest args)
333 (defun hg-run0 (command &rest args)
334 "Run the Mercurial command COMMAND, returning its output.
334 "Run the Mercurial command COMMAND, returning its output.
335 If the command does not exit with a zero status code, raise an error."
335 If the command does not exit with a zero status code, raise an error."
336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
336 (let ((res (apply 'hg-run-command (hg-binary) command args)))
337 (if (not (eq (car res) 0))
337 (if (not (eq (car res) 0))
338 (error "Mercurial command failed %s - exit code %s"
338 (error "Mercurial command failed %s - exit code %s"
339 (cons command args)
339 (cons command args)
340 (car res))
340 (car res))
341 (cdr res))))
341 (cdr res))))
342
342
343 (defmacro hg-do-across-repo (path &rest body)
343 (defmacro hg-do-across-repo (path &rest body)
344 (let ((root-name (make-symbol "root-"))
344 (let ((root-name (make-symbol "root-"))
345 (buf-name (make-symbol "buf-")))
345 (buf-name (make-symbol "buf-")))
346 `(let ((,root-name (hg-root ,path)))
346 `(let ((,root-name (hg-root ,path)))
347 (save-excursion
347 (save-excursion
348 (dolist (,buf-name (buffer-list))
348 (dolist (,buf-name (buffer-list))
349 (set-buffer ,buf-name)
349 (set-buffer ,buf-name)
350 (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))
351 ,@body))))))
351 ,@body))))))
352
352
353 (put 'hg-do-across-repo 'lisp-indent-function 1)
353 (put 'hg-do-across-repo 'lisp-indent-function 1)
354
354
355 (defun hg-sync-buffers (path)
355 (defun hg-sync-buffers (path)
356 "Sync buffers visiting PATH with their on-disk copies.
356 "Sync buffers visiting PATH with their on-disk copies.
357 If PATH is not being visited, but is under the repository root, sync
357 If PATH is not being visited, but is under the repository root, sync
358 all buffers visiting files in the repository."
358 all buffers visiting files in the repository."
359 (let ((buf (find-buffer-visiting path)))
359 (let ((buf (find-buffer-visiting path)))
360 (if buf
360 (if buf
361 (with-current-buffer buf
361 (with-current-buffer buf
362 (vc-buffer-sync))
362 (vc-buffer-sync))
363 (hg-do-across-repo path
363 (hg-do-across-repo path
364 (vc-buffer-sync)))))
364 (vc-buffer-sync)))))
365
365
366 (defun hg-buffer-commands (pnt)
366 (defun hg-buffer-commands (pnt)
367 "Use the properties of a character to do something sensible."
367 "Use the properties of a character to do something sensible."
368 (interactive "d")
368 (interactive "d")
369 (let ((rev (get-char-property pnt 'rev))
369 (let ((rev (get-char-property pnt 'rev))
370 (file (get-char-property pnt 'file)))
370 (file (get-char-property pnt 'file)))
371 (cond
371 (cond
372 (file
372 (file
373 (find-file-other-window file))
373 (find-file-other-window file))
374 (rev
374 (rev
375 (hg-diff hg-view-file-name rev rev))
375 (hg-diff hg-view-file-name rev rev))
376 ((message "I don't know how to do that yet")))))
376 ((message "I don't know how to do that yet")))))
377
377
378 (defsubst hg-event-point (event)
378 (defsubst hg-event-point (event)
379 "Return the character position of the mouse event EVENT."
379 "Return the character position of the mouse event EVENT."
380 (hg-feature-cond (xemacs (event-point event))
380 (hg-feature-cond (xemacs (event-point event))
381 (t (posn-point (event-start event)))))
381 (t (posn-point (event-start event)))))
382
382
383 (defsubst hg-event-window (event)
383 (defsubst hg-event-window (event)
384 "Return the window over which mouse event EVENT occurred."
384 "Return the window over which mouse event EVENT occurred."
385 (hg-feature-cond (xemacs (event-window event))
385 (hg-feature-cond (xemacs (event-window event))
386 (t (posn-window (event-start event)))))
386 (t (posn-window (event-start event)))))
387
387
388 (defun hg-buffer-mouse-clicked (event)
388 (defun hg-buffer-mouse-clicked (event)
389 "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.
390 These are then handed off to `hg-buffer-commands'.
390 These are then handed off to `hg-buffer-commands'.
391
391
392 Handle frickin' frackin' gratuitous event-related incompatibilities."
392 Handle frickin' frackin' gratuitous event-related incompatibilities."
393 (interactive "e")
393 (interactive "e")
394 (select-window (hg-event-window event))
394 (select-window (hg-event-window event))
395 (hg-buffer-commands (hg-event-point event)))
395 (hg-buffer-commands (hg-event-point event)))
396
396
397 (defsubst hg-abbrev-file-name (file)
397 (defsubst hg-abbrev-file-name (file)
398 "Portable wrapper around abbreviate-file-name."
398 "Portable wrapper around abbreviate-file-name."
399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
399 (hg-feature-cond (xemacs (abbreviate-file-name file t))
400 (t (abbreviate-file-name file))))
400 (t (abbreviate-file-name file))))
401
401
402 (defun hg-read-file-name (&optional prompt default)
402 (defun hg-read-file-name (&optional prompt default)
403 "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."
404 (save-excursion
404 (save-excursion
405 (while hg-prev-buffer
405 (while hg-prev-buffer
406 (set-buffer hg-prev-buffer))
406 (set-buffer hg-prev-buffer))
407 (let ((path (or default
407 (let ((path (or default
408 (buffer-file-name)
408 (buffer-file-name)
409 (expand-file-name default-directory))))
409 (expand-file-name default-directory))))
410 (if (or (not path) current-prefix-arg)
410 (if (or (not path) current-prefix-arg)
411 (expand-file-name
411 (expand-file-name
412 (eval (list* 'read-file-name
412 (eval (list* 'read-file-name
413 (format "File, directory or pattern%s: "
413 (format "File, directory or pattern%s: "
414 (or prompt ""))
414 (or prompt ""))
415 (and path (file-name-directory path))
415 (and path (file-name-directory path))
416 nil nil
416 nil nil
417 (and path (file-name-nondirectory path))
417 (and path (file-name-nondirectory path))
418 (hg-feature-cond
418 (hg-feature-cond
419 (xemacs (cons (quote 'hg-file-history) nil))
419 (xemacs (cons (quote 'hg-file-history) nil))
420 (t nil)))))
420 (t nil)))))
421 path))))
421 path))))
422
422
423 (defun hg-read-number (&optional prompt default)
423 (defun hg-read-number (&optional prompt default)
424 "Read a integer value."
424 "Read a integer value."
425 (save-excursion
425 (save-excursion
426 (if (or (not default) current-prefix-arg)
426 (if (or (not default) current-prefix-arg)
427 (string-to-number
427 (string-to-number
428 (eval (list* 'read-string
428 (eval (list* 'read-string
429 (or prompt "")
429 (or prompt "")
430 (if default (cons (format "%d" default) nil) nil))))
430 (if default (cons (format "%d" default) nil) nil))))
431 default)))
431 default)))
432
432
433 (defun hg-read-config ()
433 (defun hg-read-config ()
434 "Return an alist of (key . value) pairs of Mercurial config data.
434 "Return an alist of (key . value) pairs of Mercurial config data.
435 Each key is of the form (section . name)."
435 Each key is of the form (section . name)."
436 (let (items)
436 (let (items)
437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
437 (dolist (line (split-string (hg-chomp (hg-run0 "debugconfig")) "\n") items)
438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
438 (string-match "^\\([^=]*\\)=\\(.*\\)" line)
439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
439 (let* ((left (substring line (match-beginning 1) (match-end 1)))
440 (right (substring line (match-beginning 2) (match-end 2)))
440 (right (substring line (match-beginning 2) (match-end 2)))
441 (key (split-string left "\\."))
441 (key (split-string left "\\."))
442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
442 (value (hg-replace-in-string right "\\\\n" "\n" t)))
443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
443 (setq items (cons (cons (cons (car key) (cadr key)) value) items))))))
444
444
445 (defun hg-config-section (section config)
445 (defun hg-config-section (section config)
446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
446 "Return an alist of (name . value) pairs for SECTION of CONFIG."
447 (let (items)
447 (let (items)
448 (dolist (item config items)
448 (dolist (item config items)
449 (when (equal (caar item) section)
449 (when (equal (caar item) section)
450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
450 (setq items (cons (cons (cdar item) (cdr item)) items))))))
451
451
452 (defun hg-string-starts-with (sub str)
452 (defun hg-string-starts-with (sub str)
453 "Indicate whether string STR starts with the substring or character SUB."
453 "Indicate whether string STR starts with the substring or character SUB."
454 (if (not (stringp sub))
454 (if (not (stringp sub))
455 (and (> (length str) 0) (equal (elt str 0) sub))
455 (and (> (length str) 0) (equal (elt str 0) sub))
456 (let ((sub-len (length sub)))
456 (let ((sub-len (length sub)))
457 (and (<= sub-len (length str))
457 (and (<= sub-len (length str))
458 (string= sub (substring str 0 sub-len))))))
458 (string= sub (substring str 0 sub-len))))))
459
459
460 (defun hg-complete-repo (string predicate all)
460 (defun hg-complete-repo (string predicate all)
461 "Attempt to complete a repository name.
461 "Attempt to complete a repository name.
462 We complete on either symbolic names from Mercurial's config or real
462 We complete on either symbolic names from Mercurial's config or real
463 directory names from the file system. We do not penalise URLs."
463 directory names from the file system. We do not penalise URLs."
464 (or (if all
464 (or (if all
465 (all-completions string hg-repo-completion-table predicate)
465 (all-completions string hg-repo-completion-table predicate)
466 (try-completion string hg-repo-completion-table predicate))
466 (try-completion string hg-repo-completion-table predicate))
467 (let* ((str (expand-file-name string))
467 (let* ((str (expand-file-name string))
468 (dir (file-name-directory str))
468 (dir (file-name-directory str))
469 (file (file-name-nondirectory str)))
469 (file (file-name-nondirectory str)))
470 (if all
470 (if all
471 (let (completions)
471 (let (completions)
472 (dolist (name (delete "./" (file-name-all-completions file dir))
472 (dolist (name (delete "./" (file-name-all-completions file dir))
473 completions)
473 completions)
474 (let ((path (concat dir name)))
474 (let ((path (concat dir name)))
475 (when (file-directory-p path)
475 (when (file-directory-p path)
476 (setq completions (cons name completions))))))
476 (setq completions (cons name completions))))))
477 (let ((comp (file-name-completion file dir)))
477 (let ((comp (file-name-completion file dir)))
478 (if comp
478 (if comp
479 (hg-abbrev-file-name (concat dir comp))))))))
479 (hg-abbrev-file-name (concat dir comp))))))))
480
480
481 (defun hg-read-repo-name (&optional prompt initial-contents default)
481 (defun hg-read-repo-name (&optional prompt initial-contents default)
482 "Read the location of a repository."
482 "Read the location of a repository."
483 (save-excursion
483 (save-excursion
484 (while hg-prev-buffer
484 (while hg-prev-buffer
485 (set-buffer hg-prev-buffer))
485 (set-buffer hg-prev-buffer))
486 (let (hg-repo-completion-table)
486 (let (hg-repo-completion-table)
487 (if current-prefix-arg
487 (if current-prefix-arg
488 (progn
488 (progn
489 (dolist (path (hg-config-section "paths" (hg-read-config)))
489 (dolist (path (hg-config-section "paths" (hg-read-config)))
490 (setq hg-repo-completion-table
490 (setq hg-repo-completion-table
491 (cons (cons (car path) t) hg-repo-completion-table))
491 (cons (cons (car path) t) hg-repo-completion-table))
492 (unless (hg-string-starts-with (hg-feature-cond
492 (unless (hg-string-starts-with (hg-feature-cond
493 (xemacs directory-sep-char)
493 (xemacs directory-sep-char)
494 (t ?/))
494 (t ?/))
495 (cdr path))
495 (cdr path))
496 (setq hg-repo-completion-table
496 (setq hg-repo-completion-table
497 (cons (cons (cdr path) t) hg-repo-completion-table))))
497 (cons (cons (cdr path) t) hg-repo-completion-table))))
498 (completing-read (format "Repository%s: " (or prompt ""))
498 (completing-read (format "Repository%s: " (or prompt ""))
499 'hg-complete-repo
499 'hg-complete-repo
500 nil
500 nil
501 nil
501 nil
502 initial-contents
502 initial-contents
503 'hg-repo-history
503 'hg-repo-history
504 default))
504 default))
505 default))))
505 default))))
506
506
507 (defun hg-read-rev (&optional prompt default)
507 (defun hg-read-rev (&optional prompt default)
508 "Read a revision or tag, offering completions."
508 "Read a revision or tag, offering completions."
509 (save-excursion
509 (save-excursion
510 (while hg-prev-buffer
510 (while hg-prev-buffer
511 (set-buffer hg-prev-buffer))
511 (set-buffer hg-prev-buffer))
512 (let ((rev (or default "tip")))
512 (let ((rev (or default "tip")))
513 (if current-prefix-arg
513 (if current-prefix-arg
514 (let ((revs (split-string
514 (let ((revs (split-string
515 (hg-chomp
515 (hg-chomp
516 (hg-run0 "-q" "log" "-l"
516 (hg-run0 "-q" "log" "-l"
517 (format "%d" hg-rev-completion-limit)))
517 (format "%d" hg-rev-completion-limit)))
518 "[\n:]")))
518 "[\n:]")))
519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
519 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
520 (setq revs (cons (car (split-string line "\\s-")) revs)))
520 (setq revs (cons (car (split-string line "\\s-")) revs)))
521 (completing-read (format "Revision%s (%s): "
521 (completing-read (format "Revision%s (%s): "
522 (or prompt "")
522 (or prompt "")
523 (or default "tip"))
523 (or default "tip"))
524 (map 'list 'cons revs revs)
524 (map 'list 'cons revs revs)
525 nil
525 nil
526 nil
526 nil
527 nil
527 nil
528 'hg-rev-history
528 'hg-rev-history
529 (or default "tip")))
529 (or default "tip")))
530 rev))))
530 rev))))
531
531
532 (defun hg-parents-for-mode-line (root)
532 (defun hg-parents-for-mode-line (root)
533 "Format the parents of the working directory for the mode line."
533 "Format the parents of the working directory for the mode line."
534 (let ((parents (split-string (hg-chomp
534 (let ((parents (split-string (hg-chomp
535 (hg-run0 "--cwd" root "parents" "--template"
535 (hg-run0 "--cwd" root "parents" "--template"
536 "{rev}\n")) "\n")))
536 "{rev}\n")) "\n")))
537 (mapconcat 'identity parents "+")))
537 (mapconcat 'identity parents "+")))
538
538
539 (defun hg-buffers-visiting-repo (&optional path)
539 (defun hg-buffers-visiting-repo (&optional path)
540 "Return a list of buffers visiting the repository containing PATH."
540 "Return a list of buffers visiting the repository containing PATH."
541 (let ((root-name (hg-root (or path (buffer-file-name))))
541 (let ((root-name (hg-root (or path (buffer-file-name))))
542 bufs)
542 bufs)
543 (save-excursion
543 (save-excursion
544 (dolist (buf (buffer-list) bufs)
544 (dolist (buf (buffer-list) bufs)
545 (set-buffer buf)
545 (set-buffer buf)
546 (let ((name (buffer-file-name)))
546 (let ((name (buffer-file-name)))
547 (when (and hg-status name (equal (hg-root name) root-name))
547 (when (and hg-status name (equal (hg-root name) root-name))
548 (setq bufs (cons buf bufs))))))))
548 (setq bufs (cons buf bufs))))))))
549
549
550 (defun hg-update-mode-lines (path)
550 (defun hg-update-mode-lines (path)
551 "Update the mode lines of all buffers visiting the same repository as PATH."
551 "Update the mode lines of all buffers visiting the same repository as PATH."
552 (let* ((root (hg-root path))
552 (let* ((root (hg-root path))
553 (parents (hg-parents-for-mode-line root)))
553 (parents (hg-parents-for-mode-line root)))
554 (save-excursion
554 (save-excursion
555 (dolist (info (hg-path-status
555 (dolist (info (hg-path-status
556 root
556 root
557 (mapcar
557 (mapcar
558 (function
558 (function
559 (lambda (buf)
559 (lambda (buf)
560 (substring (buffer-file-name buf) (length root))))
560 (substring (buffer-file-name buf) (length root))))
561 (hg-buffers-visiting-repo root))))
561 (hg-buffers-visiting-repo root))))
562 (let* ((name (car info))
562 (let* ((name (car info))
563 (status (cdr info))
563 (status (cdr info))
564 (buf (find-buffer-visiting (concat root name))))
564 (buf (find-buffer-visiting (concat root name))))
565 (when buf
565 (when buf
566 (set-buffer buf)
566 (set-buffer buf)
567 (hg-mode-line-internal status parents)))))))
567 (hg-mode-line-internal status parents)))))))
568
568
569
569
570 ;;; View mode bits.
570 ;;; View mode bits.
571
571
572 (defun hg-exit-view-mode (buf)
572 (defun hg-exit-view-mode (buf)
573 "Exit from hg-view-mode.
573 "Exit from hg-view-mode.
574 We delete the current window if entering hg-view-mode split the
574 We delete the current window if entering hg-view-mode split the
575 current frame."
575 current frame."
576 (when (and (eq buf (current-buffer))
576 (when (and (eq buf (current-buffer))
577 (> (length (window-list)) 1))
577 (> (length (window-list)) 1))
578 (delete-window))
578 (delete-window))
579 (when (buffer-live-p buf)
579 (when (buffer-live-p buf)
580 (kill-buffer buf)))
580 (kill-buffer buf)))
581
581
582 (defun hg-view-mode (prev-buffer &optional file-name)
582 (defun hg-view-mode (prev-buffer &optional file-name)
583 (goto-char (point-min))
583 (goto-char (point-min))
584 (set-buffer-modified-p nil)
584 (set-buffer-modified-p nil)
585 (toggle-read-only t)
585 (toggle-read-only t)
586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
586 (hg-feature-cond (xemacs (view-minor-mode prev-buffer 'hg-exit-view-mode))
587 (t (view-mode-enter nil 'hg-exit-view-mode)))
587 (t (view-mode-enter nil 'hg-exit-view-mode)))
588 (setq hg-view-mode t)
588 (setq hg-view-mode t)
589 (setq truncate-lines t)
589 (setq truncate-lines t)
590 (when file-name
590 (when file-name
591 (setq hg-view-file-name
591 (setq hg-view-file-name
592 (hg-abbrev-file-name file-name))))
592 (hg-abbrev-file-name file-name))))
593
593
594 (defun hg-file-status (file)
594 (defun hg-file-status (file)
595 "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."
596 (let* ((s (hg-run "status" file))
596 (let* ((s (hg-run "status" file))
597 (exit (car s))
597 (exit (car s))
598 (output (cdr s)))
598 (output (cdr s)))
599 (if (= exit 0)
599 (if (= exit 0)
600 (let ((state (and (>= (length output) 2)
600 (let ((state (and (>= (length output) 2)
601 (= (aref output 1) ? )
601 (= (aref output 1) ? )
602 (assq (aref output 0) hg-state-alist))))
602 (assq (aref output 0) hg-state-alist))))
603 (if state
603 (if state
604 (cdr state)
604 (cdr state)
605 'normal)))))
605 'normal)))))
606
606
607 (defun hg-path-status (root paths)
607 (defun hg-path-status (root paths)
608 "Return status of PATHS in repo ROOT as an alist.
608 "Return status of PATHS in repo ROOT as an alist.
609 Each entry is a pair (FILE-NAME . STATUS)."
609 Each entry is a pair (FILE-NAME . STATUS)."
610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
610 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
611 result)
611 result)
612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
612 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
613 (let (state name)
613 (let (state name)
614 (cond ((= (aref entry 1) ? )
614 (cond ((= (aref entry 1) ? )
615 (setq state (assq (aref entry 0) hg-state-alist)
615 (setq state (assq (aref entry 0) hg-state-alist)
616 name (substring entry 2)))
616 name (substring entry 2)))
617 ((string-match "\\(.*\\): " entry)
617 ((string-match "\\(.*\\): " entry)
618 (setq name (match-string 1 entry))))
618 (setq name (match-string 1 entry))))
619 (setq result (cons (cons name state) result))))))
619 (setq result (cons (cons name state) result))))))
620
620
621 (defmacro hg-view-output (args &rest body)
621 (defmacro hg-view-output (args &rest body)
622 "Execute BODY in a clean buffer, then quickly display that buffer.
622 "Execute BODY in a clean buffer, then quickly display that buffer.
623 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
624 minibuffer. Otherwise, the buffer is displayed in view-mode.
624 minibuffer. Otherwise, the buffer is displayed in view-mode.
625 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
626 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
627 being viewed."
627 being viewed."
628 (let ((prev-buf (make-symbol "prev-buf-"))
628 (let ((prev-buf (make-symbol "prev-buf-"))
629 (v-b-name (car args))
629 (v-b-name (car args))
630 (v-m-rest (cdr args)))
630 (v-m-rest (cdr args)))
631 `(let ((view-buf-name ,v-b-name)
631 `(let ((view-buf-name ,v-b-name)
632 (,prev-buf (current-buffer)))
632 (,prev-buf (current-buffer)))
633 (get-buffer-create view-buf-name)
633 (get-buffer-create view-buf-name)
634 (kill-buffer view-buf-name)
634 (kill-buffer view-buf-name)
635 (get-buffer-create view-buf-name)
635 (get-buffer-create view-buf-name)
636 (set-buffer view-buf-name)
636 (set-buffer view-buf-name)
637 (save-excursion
637 (save-excursion
638 ,@body)
638 ,@body)
639 (case (count-lines (point-min) (point-max))
639 (case (count-lines (point-min) (point-max))
640 ((0)
640 ((0)
641 (kill-buffer view-buf-name)
641 (kill-buffer view-buf-name)
642 (message "(No output)"))
642 (message "(No output)"))
643 ((1)
643 ((1)
644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
644 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
645 (kill-buffer view-buf-name)
645 (kill-buffer view-buf-name)
646 (message "%s" msg)))
646 (message "%s" msg)))
647 (t
647 (t
648 (pop-to-buffer view-buf-name)
648 (pop-to-buffer view-buf-name)
649 (setq hg-prev-buffer ,prev-buf)
649 (setq hg-prev-buffer ,prev-buf)
650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
650 (hg-view-mode ,prev-buf ,@v-m-rest))))))
651
651
652 (put 'hg-view-output 'lisp-indent-function 1)
652 (put 'hg-view-output 'lisp-indent-function 1)
653
653
654 ;;; Context save and restore across revert and other operations.
654 ;;; Context save and restore across revert and other operations.
655
655
656 (defun hg-position-context (pos)
656 (defun hg-position-context (pos)
657 "Return information to help find the given position again."
657 "Return information to help find the given position again."
658 (let* ((end (min (point-max) (+ pos 98))))
658 (let* ((end (min (point-max) (+ pos 98))))
659 (list pos
659 (list pos
660 (buffer-substring (max (point-min) (- pos 2)) end)
660 (buffer-substring (max (point-min) (- pos 2)) end)
661 (- end pos))))
661 (- end pos))))
662
662
663 (defun hg-buffer-context ()
663 (defun hg-buffer-context ()
664 "Return information to help restore a user's editing context.
664 "Return information to help restore a user's editing context.
665 This is useful across reverts and merges, where a context is likely
665 This is useful across reverts and merges, where a context is likely
666 to have moved a little, but not really changed."
666 to have moved a little, but not really changed."
667 (let ((point-context (hg-position-context (point)))
667 (let ((point-context (hg-position-context (point)))
668 (mark-context (let ((mark (mark-marker)))
668 (mark-context (let ((mark (mark-marker)))
669 (and mark (hg-position-context mark)))))
669 (and mark (hg-position-context mark)))))
670 (list point-context mark-context)))
670 (list point-context mark-context)))
671
671
672 (defun hg-find-context (ctx)
672 (defun hg-find-context (ctx)
673 "Attempt to find a context in the given buffer.
673 "Attempt to find a context in the given buffer.
674 Always returns a valid, hopefully sane, position."
674 Always returns a valid, hopefully sane, position."
675 (let ((pos (nth 0 ctx))
675 (let ((pos (nth 0 ctx))
676 (str (nth 1 ctx))
676 (str (nth 1 ctx))
677 (fixup (nth 2 ctx)))
677 (fixup (nth 2 ctx)))
678 (save-excursion
678 (save-excursion
679 (goto-char (max (point-min) (- pos 15000)))
679 (goto-char (max (point-min) (- pos 15000)))
680 (if (and (not (equal str ""))
680 (if (and (not (equal str ""))
681 (search-forward str nil t))
681 (search-forward str nil t))
682 (- (point) fixup)
682 (- (point) fixup)
683 (max pos (point-min))))))
683 (max pos (point-min))))))
684
684
685 (defun hg-restore-context (ctx)
685 (defun hg-restore-context (ctx)
686 "Attempt to restore the user's editing context."
686 "Attempt to restore the user's editing context."
687 (let ((point-context (nth 0 ctx))
687 (let ((point-context (nth 0 ctx))
688 (mark-context (nth 1 ctx)))
688 (mark-context (nth 1 ctx)))
689 (goto-char (hg-find-context point-context))
689 (goto-char (hg-find-context point-context))
690 (when mark-context
690 (when mark-context
691 (set-mark (hg-find-context mark-context)))))
691 (set-mark (hg-find-context mark-context)))))
692
692
693
693
694 ;;; Hooks.
694 ;;; Hooks.
695
695
696 (defun hg-mode-line-internal (status parents)
696 (defun hg-mode-line-internal (status parents)
697 (setq hg-status status
697 (setq hg-status status
698 hg-mode (and status (concat " Hg:"
698 hg-mode (and status (concat " Hg:"
699 parents
699 parents
700 (cdr (assq status
700 (cdr (assq status
701 '((normal . "")
701 '((normal . "")
702 (removed . "r")
702 (removed . "r")
703 (added . "a")
703 (added . "a")
704 (deleted . "!")
704 (deleted . "!")
705 (modified . "m"))))))))
705 (modified . "m"))))))))
706
706
707 (defun hg-mode-line (&optional force)
707 (defun hg-mode-line (&optional force)
708 "Update the modeline with the current status of a file.
708 "Update the modeline with the current status of a file.
709 An update occurs if optional argument FORCE is non-nil,
709 An update occurs if optional argument FORCE is non-nil,
710 hg-update-modeline is non-nil, or we have not yet checked the state of
710 hg-update-modeline is non-nil, or we have not yet checked the state of
711 the file."
711 the file."
712 (let ((root (hg-root)))
712 (let ((root (hg-root)))
713 (when (and root (or force hg-update-modeline (not hg-mode)))
713 (when (and root (or force hg-update-modeline (not hg-mode)))
714 (let ((status (hg-file-status buffer-file-name))
714 (let ((status (hg-file-status buffer-file-name))
715 (parents (hg-parents-for-mode-line root)))
715 (parents (hg-parents-for-mode-line root)))
716 (hg-mode-line-internal status parents)
716 (hg-mode-line-internal status parents)
717 status))))
717 status))))
718
718
719 (defun hg-mode (&optional toggle)
719 (defun hg-mode (&optional toggle)
720 "Minor mode for Mercurial distributed SCM integration.
720 "Minor mode for Mercurial distributed SCM integration.
721
721
722 The Mercurial mode user interface is based on that of VC mode, so if
722 The Mercurial mode user interface is based on that of VC mode, so if
723 you're already familiar with VC, the same keybindings and functions
723 you're already familiar with VC, the same keybindings and functions
724 will generally work.
724 will generally work.
725
725
726 Below is a list of many common SCM tasks. In the list, `G/L\'
726 Below is a list of many common SCM tasks. In the list, `G/L\'
727 indicates whether a key binding is global (G) to a repository or
727 indicates whether a key binding is global (G) to a repository or
728 local (L) to a file. Many commands take a prefix argument.
728 local (L) to a file. Many commands take a prefix argument.
729
729
730 SCM Task G/L Key Binding Command Name
730 SCM Task G/L Key Binding Command Name
731 -------- --- ----------- ------------
731 -------- --- ----------- ------------
732 Help overview (what you are reading) G C-c h h hg-help-overview
732 Help overview (what you are reading) G C-c h h hg-help-overview
733
733
734 Tell Mercurial to manage a file G C-c h a hg-add
734 Tell Mercurial to manage a file G C-c h a hg-add
735 Commit changes to current file only L C-x v n hg-commit-start
735 Commit changes to current file only L C-x v n hg-commit-start
736 Undo changes to file since commit L C-x v u hg-revert-buffer
736 Undo changes to file since commit L C-x v u hg-revert-buffer
737
737
738 Diff file vs last checkin L C-x v = hg-diff
738 Diff file vs last checkin L C-x v = hg-diff
739
739
740 View file change history L C-x v l hg-log
740 View file change history L C-x v l hg-log
741 View annotated file L C-x v a hg-annotate
741 View annotated file L C-x v a hg-annotate
742
742
743 Diff repo vs last checkin G C-c h = hg-diff-repo
743 Diff repo vs last checkin G C-c h = hg-diff-repo
744 View status of files in repo G C-c h s hg-status
744 View status of files in repo G C-c h s hg-status
745 Commit all changes G C-c h c hg-commit-start
745 Commit all changes G C-c h c hg-commit-start
746
746
747 Undo all changes since last commit G C-c h U hg-revert
747 Undo all changes since last commit G C-c h U hg-revert
748 View repo change history G C-c h l hg-log-repo
748 View repo change history G C-c h l hg-log-repo
749
749
750 See changes that can be pulled G C-c h , hg-incoming
750 See changes that can be pulled G C-c h , hg-incoming
751 Pull changes G C-c h < hg-pull
751 Pull changes G C-c h < hg-pull
752 Update working directory after pull G C-c h u hg-update
752 Update working directory after pull G C-c h u hg-update
753 See changes that can be pushed G C-c h . hg-outgoing
753 See changes that can be pushed G C-c h . hg-outgoing
754 Push changes G C-c h > hg-push"
754 Push changes G C-c h > hg-push"
755 (unless vc-make-backup-files
755 (unless vc-make-backup-files
756 (set (make-local-variable 'backup-inhibited) t))
756 (set (make-local-variable 'backup-inhibited) t))
757 (run-hooks 'hg-mode-hook))
757 (run-hooks 'hg-mode-hook))
758
758
759 (defun hg-find-file-hook ()
759 (defun hg-find-file-hook ()
760 (ignore-errors
760 (ignore-errors
761 (when (hg-mode-line)
761 (when (hg-mode-line)
762 (hg-mode))))
762 (hg-mode))))
763
763
764 (add-hook 'find-file-hooks 'hg-find-file-hook)
764 (add-hook 'find-file-hooks 'hg-find-file-hook)
765
765
766 (defun hg-after-save-hook ()
766 (defun hg-after-save-hook ()
767 (let ((old-status hg-status))
767 (let ((old-status hg-status))
768 (hg-mode-line)
768 (hg-mode-line)
769 (if (and (not old-status) hg-status)
769 (if (and (not old-status) hg-status)
770 (hg-mode))))
770 (hg-mode))))
771
771
772 (add-hook 'after-save-hook 'hg-after-save-hook)
772 (add-hook 'after-save-hook 'hg-after-save-hook)
773
773
774
774
775 ;;; User interface functions.
775 ;;; User interface functions.
776
776
777 (defun hg-help-overview ()
777 (defun hg-help-overview ()
778 "This is an overview of the Mercurial SCM mode for Emacs.
778 "This is an overview of the Mercurial SCM mode for Emacs.
779
779
780 You can find the source code, license (GPL v2), and credits for this
780 You can find the source code, license (GPL v2), and credits for this
781 code by typing `M-x find-library mercurial RET'."
781 code by typing `M-x find-library mercurial RET'."
782 (interactive)
782 (interactive)
783 (hg-view-output ("Mercurial Help Overview")
783 (hg-view-output ("Mercurial Help Overview")
784 (insert (documentation 'hg-help-overview))
784 (insert (documentation 'hg-help-overview))
785 (let ((pos (point)))
785 (let ((pos (point)))
786 (insert (documentation 'hg-mode))
786 (insert (documentation 'hg-mode))
787 (goto-char pos)
787 (goto-char pos)
788 (end-of-line 1)
788 (end-of-line 1)
789 (delete-region pos (point)))
789 (delete-region pos (point)))
790 (let ((hg-root-dir (hg-root)))
790 (let ((hg-root-dir (hg-root)))
791 (if (not hg-root-dir)
791 (if (not hg-root-dir)
792 (error "error: %s: directory is not part of a Mercurial repository."
792 (error "error: %s: directory is not part of a Mercurial repository."
793 default-directory)
793 default-directory)
794 (cd hg-root-dir)))))
794 (cd hg-root-dir)))))
795
795
796 (defun hg-fix-paths ()
796 (defun hg-fix-paths ()
797 "Fix paths reported by some Mercurial commands."
797 "Fix paths reported by some Mercurial commands."
798 (save-excursion
798 (save-excursion
799 (goto-char (point-min))
799 (goto-char (point-min))
800 (while (re-search-forward " \\.\\.." nil t)
800 (while (re-search-forward " \\.\\.." nil t)
801 (replace-match " " nil nil))))
801 (replace-match " " nil nil))))
802
802
803 (defun hg-add (path)
803 (defun hg-add (path)
804 "Add PATH to the Mercurial repository on the next commit.
804 "Add PATH to the Mercurial repository on the next commit.
805 With a prefix argument, prompt for the path to add."
805 With a prefix argument, prompt for the path to add."
806 (interactive (list (hg-read-file-name " to add")))
806 (interactive (list (hg-read-file-name " to add")))
807 (let ((buf (current-buffer))
807 (let ((buf (current-buffer))
808 (update (equal buffer-file-name path)))
808 (update (equal buffer-file-name path)))
809 (hg-view-output (hg-output-buffer-name)
809 (hg-view-output (hg-output-buffer-name)
810 (apply 'call-process (hg-binary) nil t nil (list "add" path))
810 (apply 'call-process (hg-binary) nil t nil (list "add" path))
811 (hg-fix-paths)
811 (hg-fix-paths)
812 (goto-char (point-min))
812 (goto-char (point-min))
813 (cd (hg-root path)))
813 (cd (hg-root path)))
814 (when update
814 (when update
815 (unless vc-make-backup-files
815 (unless vc-make-backup-files
816 (set (make-local-variable 'backup-inhibited) t))
816 (set (make-local-variable 'backup-inhibited) t))
817 (with-current-buffer buf
817 (with-current-buffer buf
818 (hg-mode-line)))))
818 (hg-mode-line)))))
819
819
820 (defun hg-addremove ()
820 (defun hg-addremove ()
821 (interactive)
821 (interactive)
822 (error "not implemented"))
822 (error "not implemented"))
823
823
824 (defun hg-annotate ()
824 (defun hg-annotate ()
825 (interactive)
825 (interactive)
826 (error "not implemented"))
826 (error "not implemented"))
827
827
828 (defun hg-commit-toggle-file (pos)
828 (defun hg-commit-toggle-file (pos)
829 "Toggle whether or not the file at POS will be committed."
829 "Toggle whether or not the file at POS will be committed."
830 (interactive "d")
830 (interactive "d")
831 (save-excursion
831 (save-excursion
832 (goto-char pos)
832 (goto-char pos)
833 (let ((face (get-text-property pos 'face))
833 (let ((face (get-text-property pos 'face))
834 (inhibit-read-only t)
834 (inhibit-read-only t)
835 bol)
835 bol)
836 (beginning-of-line)
836 (beginning-of-line)
837 (setq bol (+ (point) 4))
837 (setq bol (+ (point) 4))
838 (end-of-line)
838 (end-of-line)
839 (if (eq face 'bold)
839 (if (eq face 'bold)
840 (progn
840 (progn
841 (remove-text-properties bol (point) '(face nil))
841 (remove-text-properties bol (point) '(face nil))
842 (message "%s will not be committed"
842 (message "%s will not be committed"
843 (buffer-substring bol (point))))
843 (buffer-substring bol (point))))
844 (add-text-properties bol (point) '(face bold))
844 (add-text-properties bol (point) '(face bold))
845 (message "%s will be committed"
845 (message "%s will be committed"
846 (buffer-substring bol (point)))))))
846 (buffer-substring bol (point)))))))
847
847
848 (defun hg-commit-mouse-clicked (event)
848 (defun hg-commit-mouse-clicked (event)
849 "Toggle whether or not the file at POS will be committed."
849 "Toggle whether or not the file at POS will be committed."
850 (interactive "@e")
850 (interactive "@e")
851 (hg-commit-toggle-file (hg-event-point event)))
851 (hg-commit-toggle-file (hg-event-point event)))
852
852
853 (defun hg-commit-kill ()
853 (defun hg-commit-kill ()
854 "Kill the commit currently being prepared."
854 "Kill the commit currently being prepared."
855 (interactive)
855 (interactive)
856 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
856 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this commit? "))
857 (let ((buf hg-prev-buffer))
857 (let ((buf hg-prev-buffer))
858 (kill-buffer nil)
858 (kill-buffer nil)
859 (switch-to-buffer buf))))
859 (switch-to-buffer buf))))
860
860
861 (defun hg-commit-finish ()
861 (defun hg-commit-finish ()
862 "Finish preparing a commit, and perform the actual commit.
862 "Finish preparing a commit, and perform the actual commit.
863 The hook hg-pre-commit-hook is run before anything else is done. If
863 The hook hg-pre-commit-hook is run before anything else is done. If
864 the commit message is empty and hg-commit-allow-empty-message is nil,
864 the commit message is empty and hg-commit-allow-empty-message is nil,
865 an error is raised. If the list of files to commit is empty and
865 an error is raised. If the list of files to commit is empty and
866 hg-commit-allow-empty-file-list is nil, an error is raised."
866 hg-commit-allow-empty-file-list is nil, an error is raised."
867 (interactive)
867 (interactive)
868 (let ((root hg-root))
868 (let ((root hg-root))
869 (save-excursion
869 (save-excursion
870 (run-hooks 'hg-pre-commit-hook)
870 (run-hooks 'hg-pre-commit-hook)
871 (goto-char (point-min))
871 (goto-char (point-min))
872 (search-forward hg-commit-message-start)
872 (search-forward hg-commit-message-start)
873 (let (message files)
873 (let (message files)
874 (let ((start (point)))
874 (let ((start (point)))
875 (goto-char (point-max))
875 (goto-char (point-max))
876 (search-backward hg-commit-message-end)
876 (search-backward hg-commit-message-end)
877 (setq message (hg-strip (buffer-substring start (point)))))
877 (setq message (hg-strip (buffer-substring start (point)))))
878 (when (and (= (length message) 0)
878 (when (and (= (length message) 0)
879 (not hg-commit-allow-empty-message))
879 (not hg-commit-allow-empty-message))
880 (error "Cannot proceed - commit message is empty"))
880 (error "Cannot proceed - commit message is empty"))
881 (forward-line 1)
881 (forward-line 1)
882 (beginning-of-line)
882 (beginning-of-line)
883 (while (< (point) (point-max))
883 (while (< (point) (point-max))
884 (let ((pos (+ (point) 4)))
884 (let ((pos (+ (point) 4)))
885 (end-of-line)
885 (end-of-line)
886 (when (eq (get-text-property pos 'face) 'bold)
886 (when (eq (get-text-property pos 'face) 'bold)
887 (end-of-line)
887 (end-of-line)
888 (setq files (cons (buffer-substring pos (point)) files))))
888 (setq files (cons (buffer-substring pos (point)) files))))
889 (forward-line 1))
889 (forward-line 1))
890 (when (and (= (length files) 0)
890 (when (and (= (length files) 0)
891 (not hg-commit-allow-empty-file-list))
891 (not hg-commit-allow-empty-file-list))
892 (error "Cannot proceed - no files to commit"))
892 (error "Cannot proceed - no files to commit"))
893 (setq message (concat message "\n"))
893 (setq message (concat message "\n"))
894 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
894 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))
895 (let ((buf hg-prev-buffer))
895 (let ((buf hg-prev-buffer))
896 (kill-buffer nil)
896 (kill-buffer nil)
897 (switch-to-buffer buf))
897 (switch-to-buffer buf))
898 (hg-update-mode-lines root))))
898 (hg-update-mode-lines root))))
899
899
900 (defun hg-commit-mode ()
900 (defun hg-commit-mode ()
901 "Mode for describing a commit of changes to a Mercurial repository.
901 "Mode for describing a commit of changes to a Mercurial repository.
902 This involves two actions: describing the changes with a commit
902 This involves two actions: describing the changes with a commit
903 message, and choosing the files to commit.
903 message, and choosing the files to commit.
904
904
905 To describe the commit, simply type some text in the designated area.
905 To describe the commit, simply type some text in the designated area.
906
906
907 By default, all modified, added and removed files are selected for
907 By default, all modified, added and removed files are selected for
908 committing. Files that will be committed are displayed in bold face\;
908 committing. Files that will be committed are displayed in bold face\;
909 those that will not are displayed in normal face.
909 those that will not are displayed in normal face.
910
910
911 To toggle whether a file will be committed, move the cursor over a
911 To toggle whether a file will be committed, move the cursor over a
912 particular file and hit space or return. Alternatively, middle click
912 particular file and hit space or return. Alternatively, middle click
913 on the file.
913 on the file.
914
914
915 Key bindings
915 Key bindings
916 ------------
916 ------------
917 \\[hg-commit-finish] proceed with commit
917 \\[hg-commit-finish] proceed with commit
918 \\[hg-commit-kill] kill commit
918 \\[hg-commit-kill] kill commit
919
919
920 \\[hg-diff-repo] view diff of pending changes"
920 \\[hg-diff-repo] view diff of pending changes"
921 (interactive)
921 (interactive)
922 (use-local-map hg-commit-mode-map)
922 (use-local-map hg-commit-mode-map)
923 (set-syntax-table text-mode-syntax-table)
923 (set-syntax-table text-mode-syntax-table)
924 (setq local-abbrev-table text-mode-abbrev-table
924 (setq local-abbrev-table text-mode-abbrev-table
925 major-mode 'hg-commit-mode
925 major-mode 'hg-commit-mode
926 mode-name "Hg-Commit")
926 mode-name "Hg-Commit")
927 (set-buffer-modified-p nil)
927 (set-buffer-modified-p nil)
928 (setq buffer-undo-list nil)
928 (setq buffer-undo-list nil)
929 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
929 (run-hooks 'text-mode-hook 'hg-commit-mode-hook))
930
930
931 (defun hg-commit-start ()
931 (defun hg-commit-start ()
932 "Prepare a commit of changes to the repository containing the current file."
932 "Prepare a commit of changes to the repository containing the current file."
933 (interactive)
933 (interactive)
934 (while hg-prev-buffer
934 (while hg-prev-buffer
935 (set-buffer hg-prev-buffer))
935 (set-buffer hg-prev-buffer))
936 (let ((root (hg-root))
936 (let ((root (hg-root))
937 (prev-buffer (current-buffer))
937 (prev-buffer (current-buffer))
938 modified-files)
938 modified-files)
939 (unless root
939 (unless root
940 (error "Cannot commit outside a repository!"))
940 (error "Cannot commit outside a repository!"))
941 (hg-sync-buffers root)
941 (hg-sync-buffers root)
942 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
942 (setq modified-files (hg-chomp (hg-run0 "--cwd" root "status" "-arm")))
943 (when (and (= (length modified-files) 0)
943 (when (and (= (length modified-files) 0)
944 (not hg-commit-allow-empty-file-list))
944 (not hg-commit-allow-empty-file-list))
945 (error "No pending changes to commit"))
945 (error "No pending changes to commit"))
946 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
946 (let* ((buf-name (format "*Mercurial: Commit %s*" root)))
947 (pop-to-buffer (get-buffer-create buf-name))
947 (pop-to-buffer (get-buffer-create buf-name))
948 (when (= (point-min) (point-max))
948 (when (= (point-min) (point-max))
949 (set (make-local-variable 'hg-root) root)
949 (set (make-local-variable 'hg-root) root)
950 (setq hg-prev-buffer prev-buffer)
950 (setq hg-prev-buffer prev-buffer)
951 (insert "\n")
951 (insert "\n")
952 (let ((bol (point)))
952 (let ((bol (point)))
953 (insert hg-commit-message-end)
953 (insert hg-commit-message-end)
954 (add-text-properties bol (point) '(face bold-italic)))
954 (add-text-properties bol (point) '(face bold-italic)))
955 (let ((file-area (point)))
955 (let ((file-area (point)))
956 (insert modified-files)
956 (insert modified-files)
957 (goto-char file-area)
957 (goto-char file-area)
958 (while (< (point) (point-max))
958 (while (< (point) (point-max))
959 (let ((bol (point)))
959 (let ((bol (point)))
960 (forward-char 1)
960 (forward-char 1)
961 (insert " ")
961 (insert " ")
962 (end-of-line)
962 (end-of-line)
963 (add-text-properties (+ bol 4) (point)
963 (add-text-properties (+ bol 4) (point)
964 '(face bold mouse-face highlight)))
964 '(face bold mouse-face highlight)))
965 (forward-line 1))
965 (forward-line 1))
966 (goto-char file-area)
966 (goto-char file-area)
967 (add-text-properties (point) (point-max)
967 (add-text-properties (point) (point-max)
968 `(keymap ,hg-commit-mode-file-map))
968 `(keymap ,hg-commit-mode-file-map))
969 (goto-char (point-min))
969 (goto-char (point-min))
970 (insert hg-commit-message-start)
970 (insert hg-commit-message-start)
971 (add-text-properties (point-min) (point) '(face bold-italic))
971 (add-text-properties (point-min) (point) '(face bold-italic))
972 (insert "\n\n")
972 (insert "\n\n")
973 (forward-line -1)
973 (forward-line -1)
974 (save-excursion
974 (save-excursion
975 (goto-char (point-max))
975 (goto-char (point-max))
976 (search-backward hg-commit-message-end)
976 (search-backward hg-commit-message-end)
977 (add-text-properties (match-beginning 0) (point-max)
977 (add-text-properties (match-beginning 0) (point-max)
978 '(read-only t))
978 '(read-only t))
979 (goto-char (point-min))
979 (goto-char (point-min))
980 (search-forward hg-commit-message-start)
980 (search-forward hg-commit-message-start)
981 (add-text-properties (match-beginning 0) (match-end 0)
981 (add-text-properties (match-beginning 0) (match-end 0)
982 '(read-only t)))
982 '(read-only t)))
983 (hg-commit-mode)
983 (hg-commit-mode)
984 (cd root))))))
984 (cd root))))))
985
985
986 (defun hg-diff (path &optional rev1 rev2)
986 (defun hg-diff (path &optional rev1 rev2)
987 "Show the differences between REV1 and REV2 of PATH.
987 "Show the differences between REV1 and REV2 of PATH.
988 When called interactively, the default behaviour is to treat REV1 as
988 When called interactively, the default behaviour is to treat REV1 as
989 the \"parent\" revision, REV2 as the current edited version of the file, and
989 the \"parent\" revision, REV2 as the current edited version of the file, and
990 PATH as the file edited in the current buffer.
990 PATH as the file edited in the current buffer.
991 With a prefix argument, prompt for all of these."
991 With a prefix argument, prompt for all of these."
992 (interactive (list (hg-read-file-name " to diff")
992 (interactive (list (hg-read-file-name " to diff")
993 (let ((rev1 (hg-read-rev " to start with" 'parent)))
993 (let ((rev1 (hg-read-rev " to start with" 'parent)))
994 (and (not (eq rev1 'parent)) rev1))
994 (and (not (eq rev1 'parent)) rev1))
995 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
995 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
996 (and (not (eq rev2 'working-dir)) rev2))))
996 (and (not (eq rev2 'working-dir)) rev2))))
997 (hg-sync-buffers path)
997 (hg-sync-buffers path)
998 (let ((a-path (hg-abbrev-file-name path))
998 (let ((a-path (hg-abbrev-file-name path))
999 ;; none revision is specified explicitly
999 ;; none revision is specified explicitly
1000 (none (and (not rev1) (not rev2)))
1000 (none (and (not rev1) (not rev2)))
1001 ;; only one revision is specified explicitly
1001 ;; only one revision is specified explicitly
1002 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
1002 (one (or (and (or (equal rev1 rev2) (not rev2)) rev1)
1003 (and (not rev1) rev2)))
1003 (and (not rev1) rev2)))
1004 diff)
1004 diff)
1005 (hg-view-output ((cond
1005 (hg-view-output ((cond
1006 (none
1006 (none
1007 (format "Mercurial: Diff against parent of %s" a-path))
1007 (format "Mercurial: Diff against parent of %s" a-path))
1008 (one
1008 (one
1009 (format "Mercurial: Diff of rev %s of %s" one a-path))
1009 (format "Mercurial: Diff of rev %s of %s" one a-path))
1010 (t
1010 (t
1011 (format "Mercurial: Diff from rev %s to %s of %s"
1011 (format "Mercurial: Diff from rev %s to %s of %s"
1012 rev1 rev2 a-path))))
1012 rev1 rev2 a-path))))
1013 (cond
1013 (cond
1014 (none
1014 (none
1015 (call-process (hg-binary) nil t nil "diff" path))
1015 (call-process (hg-binary) nil t nil "diff" path))
1016 (one
1016 (one
1017 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1017 (call-process (hg-binary) nil t nil "diff" "-r" one path))
1018 (t
1018 (t
1019 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1019 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)))
1020 (diff-mode)
1020 (diff-mode)
1021 (setq diff (not (= (point-min) (point-max))))
1021 (setq diff (not (= (point-min) (point-max))))
1022 (font-lock-fontify-buffer)
1022 (font-lock-fontify-buffer)
1023 (cd (hg-root path)))
1023 (cd (hg-root path)))
1024 diff))
1024 diff))
1025
1025
1026 (defun hg-diff-repo (path &optional rev1 rev2)
1026 (defun hg-diff-repo (path &optional rev1 rev2)
1027 "Show the differences between REV1 and REV2 of repository containing PATH.
1027 "Show the differences between REV1 and REV2 of repository containing PATH.
1028 When called interactively, the default behaviour is to treat REV1 as
1028 When called interactively, the default behaviour is to treat REV1 as
1029 the \"parent\" revision, REV2 as the current edited version of the file, and
1029 the \"parent\" revision, REV2 as the current edited version of the file, and
1030 PATH as the `hg-root' of the current buffer.
1030 PATH as the `hg-root' of the current buffer.
1031 With a prefix argument, prompt for all of these."
1031 With a prefix argument, prompt for all of these."
1032 (interactive (list (hg-read-file-name " to diff")
1032 (interactive (list (hg-read-file-name " to diff")
1033 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1033 (let ((rev1 (hg-read-rev " to start with" 'parent)))
1034 (and (not (eq rev1 'parent)) rev1))
1034 (and (not (eq rev1 'parent)) rev1))
1035 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1035 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
1036 (and (not (eq rev2 'working-dir)) rev2))))
1036 (and (not (eq rev2 'working-dir)) rev2))))
1037 (hg-diff (hg-root path) rev1 rev2))
1037 (hg-diff (hg-root path) rev1 rev2))
1038
1038
1039 (defun hg-forget (path)
1039 (defun hg-forget (path)
1040 "Lose track of PATH, which has been added, but not yet committed.
1040 "Lose track of PATH, which has been added, but not yet committed.
1041 This will prevent the file from being incorporated into the Mercurial
1041 This will prevent the file from being incorporated into the Mercurial
1042 repository on the next commit.
1042 repository on the next commit.
1043 With a prefix argument, prompt for the path to forget."
1043 With a prefix argument, prompt for the path to forget."
1044 (interactive (list (hg-read-file-name " to forget")))
1044 (interactive (list (hg-read-file-name " to forget")))
1045 (let ((buf (current-buffer))
1045 (let ((buf (current-buffer))
1046 (update (equal buffer-file-name path)))
1046 (update (equal buffer-file-name path)))
1047 (hg-view-output (hg-output-buffer-name)
1047 (hg-view-output (hg-output-buffer-name)
1048 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1048 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
1049 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1049 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
1050 (hg-fix-paths)
1050 (hg-fix-paths)
1051 (goto-char (point-min))
1051 (goto-char (point-min))
1052 (cd (hg-root path)))
1052 (cd (hg-root path)))
1053 (when update
1053 (when update
1054 (with-current-buffer buf
1054 (with-current-buffer buf
1055 (when (local-variable-p 'backup-inhibited)
1055 (when (local-variable-p 'backup-inhibited)
1056 (kill-local-variable 'backup-inhibited))
1056 (kill-local-variable 'backup-inhibited))
1057 (hg-mode-line)))))
1057 (hg-mode-line)))))
1058
1058
1059 (defun hg-incoming (&optional repo)
1059 (defun hg-incoming (&optional repo)
1060 "Display changesets present in REPO that are not present locally."
1060 "Display changesets present in REPO that are not present locally."
1061 (interactive (list (hg-read-repo-name " where changes would come from")))
1061 (interactive (list (hg-read-repo-name " where changes would come from")))
1062 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1062 (hg-view-output ((format "Mercurial: Incoming from %s to %s"
1063 (hg-abbrev-file-name (hg-root))
1063 (hg-abbrev-file-name (hg-root))
1064 (hg-abbrev-file-name
1064 (hg-abbrev-file-name
1065 (or repo hg-incoming-repository))))
1065 (or repo hg-incoming-repository))))
1066 (call-process (hg-binary) nil t nil "incoming"
1066 (call-process (hg-binary) nil t nil "incoming"
1067 (or repo hg-incoming-repository))
1067 (or repo hg-incoming-repository))
1068 (hg-log-mode)
1068 (hg-log-mode)
1069 (cd (hg-root))))
1069 (cd (hg-root))))
1070
1070
1071 (defun hg-init ()
1071 (defun hg-init ()
1072 (interactive)
1072 (interactive)
1073 (error "not implemented"))
1073 (error "not implemented"))
1074
1074
1075 (defun hg-log-mode ()
1075 (defun hg-log-mode ()
1076 "Mode for viewing a Mercurial change log."
1076 "Mode for viewing a Mercurial change log."
1077 (goto-char (point-min))
1077 (goto-char (point-min))
1078 (when (looking-at "^searching for changes.*$")
1078 (when (looking-at "^searching for changes.*$")
1079 (delete-region (match-beginning 0) (match-end 0)))
1079 (delete-region (match-beginning 0) (match-end 0)))
1080 (run-hooks 'hg-log-mode-hook))
1080 (run-hooks 'hg-log-mode-hook))
1081
1081
1082 (defun hg-log (path &optional rev1 rev2 log-limit)
1082 (defun hg-log (path &optional rev1 rev2 log-limit)
1083 "Display the revision history of PATH.
1083 "Display the revision history of PATH.
1084 History is displayed between REV1 and REV2.
1084 History is displayed between REV1 and REV2.
1085 Number of displayed changesets is limited to LOG-LIMIT.
1085 Number of displayed changesets is limited to LOG-LIMIT.
1086 REV1 defaults to the tip, while
1086 REV1 defaults to the tip, while REV2 defaults to 0.
1087 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1088 LOG-LIMIT defaults to `hg-log-limit'.
1087 LOG-LIMIT defaults to `hg-log-limit'.
1089 With a prefix argument, prompt for each parameter."
1088 With a prefix argument, prompt for each parameter."
1090 (interactive (list (hg-read-file-name " to log")
1089 (interactive (list (hg-read-file-name " to log")
1091 (hg-read-rev " to start with"
1090 (hg-read-rev " to start with"
1092 "tip")
1091 "tip")
1093 (hg-read-rev " to end with"
1092 (hg-read-rev " to end with"
1094 (format "%d" (- hg-rev-completion-limit)))
1093 "0")
1095 (hg-read-number "Output limited to: "
1094 (hg-read-number "Output limited to: "
1096 hg-log-limit)))
1095 hg-log-limit)))
1097 (let ((a-path (hg-abbrev-file-name path))
1096 (let ((a-path (hg-abbrev-file-name path))
1098 (r1 (or rev1 (format "-%d" hg-rev-completion-limit)))
1097 (r1 (or rev1 "tip"))
1099 (r2 (or rev2 rev1 "tip"))
1098 (r2 (or rev2 "0"))
1100 (limit (format "%d" (or log-limit hg-log-limit))))
1099 (limit (format "%d" (or log-limit hg-log-limit))))
1101 (hg-view-output ((if (equal r1 r2)
1100 (hg-view-output ((if (equal r1 r2)
1102 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1101 (format "Mercurial: Log of rev %s of %s" rev1 a-path)
1103 (format
1102 (format
1104 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1103 "Mercurial: at most %s log(s) from rev %s to %s of %s"
1105 limit r1 r2 a-path)))
1104 limit r1 r2 a-path)))
1106 (eval (list* 'call-process (hg-binary) nil t nil
1105 (eval (list* 'call-process (hg-binary) nil t nil
1107 "log"
1106 "log"
1108 "-r" (format "%s:%s" r1 r2)
1107 "-r" (format "%s:%s" r1 r2)
1109 "-l" limit
1108 "-l" limit
1110 (if (> (length path) (length (hg-root path)))
1109 (if (> (length path) (length (hg-root path)))
1111 (cons path nil)
1110 (cons path nil)
1112 nil)))
1111 nil)))
1113 (hg-log-mode)
1112 (hg-log-mode)
1114 (cd (hg-root path)))))
1113 (cd (hg-root path)))))
1115
1114
1116 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1115 (defun hg-log-repo (path &optional rev1 rev2 log-limit)
1117 "Display the revision history of the repository containing PATH.
1116 "Display the revision history of the repository containing PATH.
1118 History is displayed between REV1 and REV2.
1117 History is displayed between REV1 and REV2.
1119 Number of displayed changesets is limited to LOG-LIMIT,
1118 Number of displayed changesets is limited to LOG-LIMIT,
1120 REV1 defaults to the tip, while
1119 REV1 defaults to the tip, while REV2 defaults to 0.
1121 REV2 defaults to `hg-rev-completion-limit' changes from the tip revision.
1122 LOG-LIMIT defaults to `hg-log-limit'.
1120 LOG-LIMIT defaults to `hg-log-limit'.
1123 With a prefix argument, prompt for each parameter."
1121 With a prefix argument, prompt for each parameter."
1124 (interactive (list (hg-read-file-name " to log")
1122 (interactive (list (hg-read-file-name " to log")
1125 (hg-read-rev " to start with"
1123 (hg-read-rev " to start with"
1126 "tip")
1124 "tip")
1127 (hg-read-rev " to end with"
1125 (hg-read-rev " to end with"
1128 (format "%d" (- hg-rev-completion-limit)))
1126 "0")
1129 (hg-read-number "Output limited to: "
1127 (hg-read-number "Output limited to: "
1130 hg-log-limit)))
1128 hg-log-limit)))
1131 (hg-log (hg-root path) rev1 rev2 log-limit))
1129 (hg-log (hg-root path) rev1 rev2 log-limit))
1132
1130
1133 (defun hg-outgoing (&optional repo)
1131 (defun hg-outgoing (&optional repo)
1134 "Display changesets present locally that are not present in REPO."
1132 "Display changesets present locally that are not present in REPO."
1135 (interactive (list (hg-read-repo-name " where changes would go to" nil
1133 (interactive (list (hg-read-repo-name " where changes would go to" nil
1136 hg-outgoing-repository)))
1134 hg-outgoing-repository)))
1137 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1135 (hg-view-output ((format "Mercurial: Outgoing from %s to %s"
1138 (hg-abbrev-file-name (hg-root))
1136 (hg-abbrev-file-name (hg-root))
1139 (hg-abbrev-file-name
1137 (hg-abbrev-file-name
1140 (or repo hg-outgoing-repository))))
1138 (or repo hg-outgoing-repository))))
1141 (call-process (hg-binary) nil t nil "outgoing"
1139 (call-process (hg-binary) nil t nil "outgoing"
1142 (or repo hg-outgoing-repository))
1140 (or repo hg-outgoing-repository))
1143 (hg-log-mode)
1141 (hg-log-mode)
1144 (cd (hg-root))))
1142 (cd (hg-root))))
1145
1143
1146 (defun hg-pull (&optional repo)
1144 (defun hg-pull (&optional repo)
1147 "Pull changes from repository REPO.
1145 "Pull changes from repository REPO.
1148 This does not update the working directory."
1146 This does not update the working directory."
1149 (interactive (list (hg-read-repo-name " to pull from")))
1147 (interactive (list (hg-read-repo-name " to pull from")))
1150 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1148 (hg-view-output ((format "Mercurial: Pull to %s from %s"
1151 (hg-abbrev-file-name (hg-root))
1149 (hg-abbrev-file-name (hg-root))
1152 (hg-abbrev-file-name
1150 (hg-abbrev-file-name
1153 (or repo hg-incoming-repository))))
1151 (or repo hg-incoming-repository))))
1154 (call-process (hg-binary) nil t nil "pull"
1152 (call-process (hg-binary) nil t nil "pull"
1155 (or repo hg-incoming-repository))
1153 (or repo hg-incoming-repository))
1156 (cd (hg-root))))
1154 (cd (hg-root))))
1157
1155
1158 (defun hg-push (&optional repo)
1156 (defun hg-push (&optional repo)
1159 "Push changes to repository REPO."
1157 "Push changes to repository REPO."
1160 (interactive (list (hg-read-repo-name " to push to")))
1158 (interactive (list (hg-read-repo-name " to push to")))
1161 (hg-view-output ((format "Mercurial: Push from %s to %s"
1159 (hg-view-output ((format "Mercurial: Push from %s to %s"
1162 (hg-abbrev-file-name (hg-root))
1160 (hg-abbrev-file-name (hg-root))
1163 (hg-abbrev-file-name
1161 (hg-abbrev-file-name
1164 (or repo hg-outgoing-repository))))
1162 (or repo hg-outgoing-repository))))
1165 (call-process (hg-binary) nil t nil "push"
1163 (call-process (hg-binary) nil t nil "push"
1166 (or repo hg-outgoing-repository))
1164 (or repo hg-outgoing-repository))
1167 (cd (hg-root))))
1165 (cd (hg-root))))
1168
1166
1169 (defun hg-revert-buffer-internal ()
1167 (defun hg-revert-buffer-internal ()
1170 (let ((ctx (hg-buffer-context)))
1168 (let ((ctx (hg-buffer-context)))
1171 (message "Reverting %s..." buffer-file-name)
1169 (message "Reverting %s..." buffer-file-name)
1172 (hg-run0 "revert" buffer-file-name)
1170 (hg-run0 "revert" buffer-file-name)
1173 (revert-buffer t t t)
1171 (revert-buffer t t t)
1174 (hg-restore-context ctx)
1172 (hg-restore-context ctx)
1175 (hg-mode-line)
1173 (hg-mode-line)
1176 (message "Reverting %s...done" buffer-file-name)))
1174 (message "Reverting %s...done" buffer-file-name)))
1177
1175
1178 (defun hg-revert-buffer ()
1176 (defun hg-revert-buffer ()
1179 "Revert current buffer's file back to the latest committed version.
1177 "Revert current buffer's file back to the latest committed version.
1180 If the file has not changed, nothing happens. Otherwise, this
1178 If the file has not changed, nothing happens. Otherwise, this
1181 displays a diff and asks for confirmation before reverting."
1179 displays a diff and asks for confirmation before reverting."
1182 (interactive)
1180 (interactive)
1183 (let ((vc-suppress-confirm nil)
1181 (let ((vc-suppress-confirm nil)
1184 (obuf (current-buffer))
1182 (obuf (current-buffer))
1185 diff)
1183 diff)
1186 (vc-buffer-sync)
1184 (vc-buffer-sync)
1187 (unwind-protect
1185 (unwind-protect
1188 (setq diff (hg-diff buffer-file-name))
1186 (setq diff (hg-diff buffer-file-name))
1189 (when diff
1187 (when diff
1190 (unless (yes-or-no-p "Discard changes? ")
1188 (unless (yes-or-no-p "Discard changes? ")
1191 (error "Revert cancelled")))
1189 (error "Revert cancelled")))
1192 (when diff
1190 (when diff
1193 (let ((buf (current-buffer)))
1191 (let ((buf (current-buffer)))
1194 (delete-window (selected-window))
1192 (delete-window (selected-window))
1195 (kill-buffer buf))))
1193 (kill-buffer buf))))
1196 (set-buffer obuf)
1194 (set-buffer obuf)
1197 (when diff
1195 (when diff
1198 (hg-revert-buffer-internal))))
1196 (hg-revert-buffer-internal))))
1199
1197
1200 (defun hg-root (&optional path)
1198 (defun hg-root (&optional path)
1201 "Return the root of the repository that contains the given path.
1199 "Return the root of the repository that contains the given path.
1202 If the path is outside a repository, return nil.
1200 If the path is outside a repository, return nil.
1203 When called interactively, the root is printed. A prefix argument
1201 When called interactively, the root is printed. A prefix argument
1204 prompts for a path to check."
1202 prompts for a path to check."
1205 (interactive (list (hg-read-file-name)))
1203 (interactive (list (hg-read-file-name)))
1206 (if (or path (not hg-root))
1204 (if (or path (not hg-root))
1207 (let ((root (do ((prev nil dir)
1205 (let ((root (do ((prev nil dir)
1208 (dir (file-name-directory
1206 (dir (file-name-directory
1209 (or
1207 (or
1210 path
1208 path
1211 buffer-file-name
1209 buffer-file-name
1212 (expand-file-name default-directory)))
1210 (expand-file-name default-directory)))
1213 (file-name-directory (directory-file-name dir))))
1211 (file-name-directory (directory-file-name dir))))
1214 ((equal prev dir))
1212 ((equal prev dir))
1215 (when (file-directory-p (concat dir ".hg"))
1213 (when (file-directory-p (concat dir ".hg"))
1216 (return dir)))))
1214 (return dir)))))
1217 (when (interactive-p)
1215 (when (interactive-p)
1218 (if root
1216 (if root
1219 (message "The root of this repository is `%s'." root)
1217 (message "The root of this repository is `%s'." root)
1220 (message "The path `%s' is not in a Mercurial repository."
1218 (message "The path `%s' is not in a Mercurial repository."
1221 (hg-abbrev-file-name path))))
1219 (hg-abbrev-file-name path))))
1222 root)
1220 root)
1223 hg-root))
1221 hg-root))
1224
1222
1225 (defun hg-cwd (&optional path)
1223 (defun hg-cwd (&optional path)
1226 "Return the current directory of PATH within the repository."
1224 "Return the current directory of PATH within the repository."
1227 (do ((stack nil (cons (file-name-nondirectory
1225 (do ((stack nil (cons (file-name-nondirectory
1228 (directory-file-name dir))
1226 (directory-file-name dir))
1229 stack))
1227 stack))
1230 (prev nil dir)
1228 (prev nil dir)
1231 (dir (file-name-directory (or path buffer-file-name
1229 (dir (file-name-directory (or path buffer-file-name
1232 (expand-file-name default-directory)))
1230 (expand-file-name default-directory)))
1233 (file-name-directory (directory-file-name dir))))
1231 (file-name-directory (directory-file-name dir))))
1234 ((equal prev dir))
1232 ((equal prev dir))
1235 (when (file-directory-p (concat dir ".hg"))
1233 (when (file-directory-p (concat dir ".hg"))
1236 (let ((cwd (mapconcat 'identity stack "/")))
1234 (let ((cwd (mapconcat 'identity stack "/")))
1237 (unless (equal cwd "")
1235 (unless (equal cwd "")
1238 (return (file-name-as-directory cwd)))))))
1236 (return (file-name-as-directory cwd)))))))
1239
1237
1240 (defun hg-status (path)
1238 (defun hg-status (path)
1241 "Print revision control status of a file or directory.
1239 "Print revision control status of a file or directory.
1242 With prefix argument, prompt for the path to give status for.
1240 With prefix argument, prompt for the path to give status for.
1243 Names are displayed relative to the repository root."
1241 Names are displayed relative to the repository root."
1244 (interactive (list (hg-read-file-name " for status" (hg-root))))
1242 (interactive (list (hg-read-file-name " for status" (hg-root))))
1245 (let ((root (hg-root)))
1243 (let ((root (hg-root)))
1246 (hg-view-output ((format "Mercurial: Status of %s in %s"
1244 (hg-view-output ((format "Mercurial: Status of %s in %s"
1247 (let ((name (substring (expand-file-name path)
1245 (let ((name (substring (expand-file-name path)
1248 (length root))))
1246 (length root))))
1249 (if (> (length name) 0)
1247 (if (> (length name) 0)
1250 name
1248 name
1251 "*"))
1249 "*"))
1252 (hg-abbrev-file-name root)))
1250 (hg-abbrev-file-name root)))
1253 (apply 'call-process (hg-binary) nil t nil
1251 (apply 'call-process (hg-binary) nil t nil
1254 (list "--cwd" root "status" path))
1252 (list "--cwd" root "status" path))
1255 (cd (hg-root path)))))
1253 (cd (hg-root path)))))
1256
1254
1257 (defun hg-undo ()
1255 (defun hg-undo ()
1258 (interactive)
1256 (interactive)
1259 (error "not implemented"))
1257 (error "not implemented"))
1260
1258
1261 (defun hg-update ()
1259 (defun hg-update ()
1262 (interactive)
1260 (interactive)
1263 (error "not implemented"))
1261 (error "not implemented"))
1264
1262
1265 (defun hg-version-other-window ()
1263 (defun hg-version-other-window ()
1266 (interactive)
1264 (interactive)
1267 (error "not implemented"))
1265 (error "not implemented"))
1268
1266
1269
1267
1270 (provide 'mercurial)
1268 (provide 'mercurial)
1271
1269
1272
1270
1273 ;;; Local Variables:
1271 ;;; Local Variables:
1274 ;;; prompt-to-byte-compile: nil
1272 ;;; prompt-to-byte-compile: nil
1275 ;;; end:
1273 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now