##// END OF EJS Templates
mq.el: declare variable mq-top
NIIMI Satoshi -
r5466:87860c62 default
parent child Browse files
Show More
@@ -1,411 +1,414 b''
1 1 ;;; mq.el --- Emacs support for Mercurial Queues
2 2
3 3 ;; Copyright (C) 2006 Bryan O'Sullivan
4 4
5 5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6 6
7 7 ;; mq.el is free software; you can redistribute it and/or modify it
8 8 ;; under the terms of version 2 of the GNU General Public License as
9 9 ;; published by the Free Software Foundation.
10 10
11 11 ;; mq.el is distributed in the hope that it will be useful, but
12 12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 14 ;; General Public License for more details.
15 15
16 16 ;; You should have received a copy of the GNU General Public License
17 17 ;; along with mq.el, GNU Emacs, or XEmacs; see the file COPYING (`C-h
18 18 ;; C-l'). If not, write to the Free Software Foundation, Inc., 59
19 19 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 20
21 21 (eval-when-compile (require 'cl))
22 22 (require 'mercurial)
23 23
24 24
25 25 (defcustom mq-mode-hook nil
26 26 "Hook run when a buffer enters mq-mode."
27 27 :type 'sexp
28 28 :group 'mercurial)
29 29
30 30 (defcustom mq-global-prefix "\C-cq"
31 31 "The global prefix for Mercurial Queues keymap bindings."
32 32 :type 'sexp
33 33 :group 'mercurial)
34 34
35 35 (defcustom mq-edit-mode-hook nil
36 36 "Hook run after a buffer is populated to edit a patch description."
37 37 :type 'sexp
38 38 :group 'mercurial)
39 39
40 40 (defcustom mq-edit-finish-hook nil
41 41 "Hook run before a patch description is finished up with."
42 42 :type 'sexp
43 43 :group 'mercurial)
44 44
45 45 (defcustom mq-signoff-address nil
46 46 "Address with which to sign off on a patch."
47 47 :type 'string
48 48 :group 'mercurial)
49 49
50 50
51 51 ;;; Internal variables.
52 52
53 53 (defvar mq-mode nil
54 54 "Is this file managed by MQ?")
55 55 (make-variable-buffer-local 'mq-mode)
56 56 (put 'mq-mode 'permanent-local t)
57 57
58 58 (defvar mq-patch-history nil)
59 59
60 60 (defvar mq-top-patch '(nil))
61 61
62 62 (defvar mq-prev-buffer nil)
63 63 (make-variable-buffer-local 'mq-prev-buffer)
64 64 (put 'mq-prev-buffer 'permanent-local t)
65 65
66 (defvar mq-top nil)
67 (make-variable-buffer-local 'mq-top)
68 (put 'mq-top 'permanent-local t)
66 69
67 70 ;;; Global keymap.
68 71
69 72 (defvar mq-global-map (make-sparse-keymap))
70 73 (fset 'mq-global-map mq-global-map)
71 74 (global-set-key mq-global-prefix 'mq-global-map)
72 75 (define-key mq-global-map "." 'mq-push)
73 76 (define-key mq-global-map ">" 'mq-push-all)
74 77 (define-key mq-global-map "," 'mq-pop)
75 78 (define-key mq-global-map "<" 'mq-pop-all)
76 79 (define-key mq-global-map "=" 'mq-diff)
77 80 (define-key mq-global-map "r" 'mq-refresh)
78 81 (define-key mq-global-map "e" 'mq-refresh-edit)
79 82 (define-key mq-global-map "i" 'mq-new)
80 83 (define-key mq-global-map "n" 'mq-next)
81 84 (define-key mq-global-map "o" 'mq-signoff)
82 85 (define-key mq-global-map "p" 'mq-previous)
83 86 (define-key mq-global-map "s" 'mq-edit-series)
84 87 (define-key mq-global-map "t" 'mq-top)
85 88
86 89 (add-minor-mode 'mq-mode 'mq-mode)
87 90
88 91
89 92 ;;; Refresh edit mode keymap.
90 93
91 94 (defvar mq-edit-mode-map (make-sparse-keymap))
92 95 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
93 96 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
94 97 (define-key mq-edit-mode-map "\C-c\C-s" 'mq-signoff)
95 98
96 99
97 100 ;;; Helper functions.
98 101
99 102 (defun mq-read-patch-name (&optional source prompt force)
100 103 "Read a patch name to use with a command.
101 104 May return nil, meaning \"use the default\"."
102 105 (let ((patches (split-string
103 106 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
104 107 (when force
105 108 (completing-read (format "Patch%s: " (or prompt ""))
106 109 (mapcar (lambda (x) (cons x x)) patches)
107 110 nil
108 111 nil
109 112 nil
110 113 'mq-patch-history))))
111 114
112 115 (defun mq-refresh-buffers (root)
113 116 (save-excursion
114 117 (dolist (buf (hg-buffers-visiting-repo root))
115 118 (when (not (verify-visited-file-modtime buf))
116 119 (set-buffer buf)
117 120 (let ((ctx (hg-buffer-context)))
118 121 (message "Refreshing %s..." (buffer-name))
119 122 (revert-buffer t t t)
120 123 (hg-restore-context ctx)
121 124 (message "Refreshing %s...done" (buffer-name))))))
122 125 (hg-update-mode-lines root)
123 126 (mq-update-mode-lines root))
124 127
125 128 (defun mq-last-line ()
126 129 (goto-char (point-max))
127 130 (beginning-of-line)
128 131 (when (looking-at "^$")
129 132 (forward-line -1))
130 133 (let ((bol (point)))
131 134 (end-of-line)
132 135 (let ((line (buffer-substring bol (point))))
133 136 (when (> (length line) 0)
134 137 line))))
135 138
136 139 (defun mq-push (&optional patch)
137 140 "Push patches until PATCH is reached.
138 141 If PATCH is nil, push at most one patch."
139 142 (interactive (list (mq-read-patch-name "qunapplied" " to push"
140 143 current-prefix-arg)))
141 144 (let ((root (hg-root))
142 145 (prev-buf (current-buffer))
143 146 last-line ok)
144 147 (unless root
145 148 (error "Cannot push outside a repository!"))
146 149 (hg-sync-buffers root)
147 150 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
148 151 (kill-buffer (get-buffer-create buf-name))
149 152 (split-window-vertically)
150 153 (other-window 1)
151 154 (switch-to-buffer (get-buffer-create buf-name))
152 155 (cd root)
153 156 (message "Pushing...")
154 157 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
155 158 (if patch (list patch))))
156 159 last-line (mq-last-line))
157 160 (let ((lines (count-lines (point-min) (point-max))))
158 161 (if (or (<= lines 1)
159 162 (and (equal lines 2) (string-match "Now at:" last-line)))
160 163 (progn
161 164 (kill-buffer (current-buffer))
162 165 (delete-window))
163 166 (hg-view-mode prev-buf))))
164 167 (mq-refresh-buffers root)
165 168 (sit-for 0)
166 169 (when last-line
167 170 (if ok
168 171 (message "Pushing... %s" last-line)
169 172 (error "Pushing... %s" last-line)))))
170 173
171 174 (defun mq-push-all ()
172 175 "Push patches until all are applied."
173 176 (interactive)
174 177 (mq-push "-a"))
175 178
176 179 (defun mq-pop (&optional patch)
177 180 "Pop patches until PATCH is reached.
178 181 If PATCH is nil, pop at most one patch."
179 182 (interactive (list (mq-read-patch-name "qapplied" " to pop to"
180 183 current-prefix-arg)))
181 184 (let ((root (hg-root))
182 185 last-line ok)
183 186 (unless root
184 187 (error "Cannot pop outside a repository!"))
185 188 (hg-sync-buffers root)
186 189 (set-buffer (generate-new-buffer "qpop"))
187 190 (cd root)
188 191 (message "Popping...")
189 192 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
190 193 (if patch (list patch))))
191 194 last-line (mq-last-line))
192 195 (kill-buffer (current-buffer))
193 196 (mq-refresh-buffers root)
194 197 (sit-for 0)
195 198 (when last-line
196 199 (if ok
197 200 (message "Popping... %s" last-line)
198 201 (error "Popping... %s" last-line)))))
199 202
200 203 (defun mq-pop-all ()
201 204 "Push patches until none are applied."
202 205 (interactive)
203 206 (mq-pop "-a"))
204 207
205 208 (defun mq-refresh-internal (root &rest args)
206 209 (hg-sync-buffers root)
207 210 (let ((patch (mq-patch-info "qtop")))
208 211 (message "Refreshing %s..." patch)
209 212 (let ((ret (apply 'hg-run "qrefresh" args)))
210 213 (if (equal (car ret) 0)
211 214 (message "Refreshing %s... done." patch)
212 215 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
213 216
214 217 (defun mq-refresh (&optional git)
215 218 "Refresh the topmost applied patch.
216 219 With a prefix argument, generate a git-compatible patch."
217 220 (interactive "P")
218 221 (let ((root (hg-root)))
219 222 (unless root
220 223 (error "Cannot refresh outside of a repository!"))
221 224 (apply 'mq-refresh-internal root (if git '("--git")))))
222 225
223 226 (defun mq-patch-info (cmd &optional msg)
224 227 (let* ((ret (hg-run cmd))
225 228 (info (hg-chomp (cdr ret))))
226 229 (if (equal (car ret) 0)
227 230 (if msg
228 231 (message "%s patch: %s" msg info)
229 232 info)
230 233 (error "%s" info))))
231 234
232 235 (defun mq-top ()
233 236 "Print the name of the topmost applied patch."
234 237 (interactive)
235 238 (mq-patch-info "qtop" "Top"))
236 239
237 240 (defun mq-next ()
238 241 "Print the name of the next patch to be pushed."
239 242 (interactive)
240 243 (mq-patch-info "qnext" "Next"))
241 244
242 245 (defun mq-previous ()
243 246 "Print the name of the first patch below the topmost applied patch.
244 247 This would become the active patch if popped to."
245 248 (interactive)
246 249 (mq-patch-info "qprev" "Previous"))
247 250
248 251 (defun mq-edit-finish ()
249 252 "Finish editing the description of this patch, and refresh the patch."
250 253 (interactive)
251 254 (unless (equal (mq-patch-info "qtop") mq-top)
252 255 (error "Topmost patch has changed!"))
253 256 (hg-sync-buffers hg-root)
254 257 (run-hooks 'mq-edit-finish-hook)
255 258 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
256 259 (let ((buf mq-prev-buffer))
257 260 (kill-buffer nil)
258 261 (switch-to-buffer buf)))
259 262
260 263 (defun mq-edit-kill ()
261 264 "Kill the edit currently being prepared."
262 265 (interactive)
263 266 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
264 267 (let ((buf mq-prev-buffer))
265 268 (kill-buffer nil)
266 269 (switch-to-buffer buf))))
267 270
268 271 (defun mq-get-top (root)
269 272 (let ((entry (assoc root mq-top-patch)))
270 273 (if entry
271 274 (cdr entry))))
272 275
273 276 (defun mq-set-top (root patch)
274 277 (let ((entry (assoc root mq-top-patch)))
275 278 (if entry
276 279 (if patch
277 280 (setcdr entry patch)
278 281 (setq mq-top-patch (delq entry mq-top-patch)))
279 282 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
280 283
281 284 (defun mq-update-mode-lines (root)
282 285 (let ((cwd default-directory))
283 286 (cd root)
284 287 (condition-case nil
285 288 (mq-set-top root (mq-patch-info "qtop"))
286 289 (error (mq-set-top root nil)))
287 290 (cd cwd))
288 291 (let ((patch (mq-get-top root)))
289 292 (save-excursion
290 293 (dolist (buf (hg-buffers-visiting-repo root))
291 294 (set-buffer buf)
292 295 (if mq-mode
293 296 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
294 297
295 298 (defun mq-mode (&optional arg)
296 299 "Minor mode for Mercurial repositories with an MQ patch queue"
297 300 (interactive "i")
298 301 (cond ((hg-root)
299 302 (setq mq-mode (if (null arg) (not mq-mode)
300 303 arg))
301 304 (mq-update-mode-lines (hg-root))))
302 305 (run-hooks 'mq-mode-hook))
303 306
304 307 (defun mq-edit-mode ()
305 308 "Mode for editing the description of a patch.
306 309
307 310 Key bindings
308 311 ------------
309 312 \\[mq-edit-finish] use this description
310 313 \\[mq-edit-kill] abandon this description"
311 314 (interactive)
312 315 (use-local-map mq-edit-mode-map)
313 316 (set-syntax-table text-mode-syntax-table)
314 317 (setq local-abbrev-table text-mode-abbrev-table
315 318 major-mode 'mq-edit-mode
316 319 mode-name "MQ-Edit")
317 320 (set-buffer-modified-p nil)
318 321 (setq buffer-undo-list nil)
319 322 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
320 323
321 324 (defun mq-refresh-edit ()
322 325 "Refresh the topmost applied patch, editing the patch description."
323 326 (interactive)
324 327 (while mq-prev-buffer
325 328 (set-buffer mq-prev-buffer))
326 329 (let ((root (hg-root))
327 330 (prev-buffer (current-buffer))
328 331 (patch (mq-patch-info "qtop")))
329 332 (hg-sync-buffers root)
330 333 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
331 334 (switch-to-buffer (get-buffer-create buf-name))
332 335 (when (= (point-min) (point-max))
333 336 (set (make-local-variable 'hg-root) root)
334 337 (set (make-local-variable 'mq-top) patch)
335 338 (setq mq-prev-buffer prev-buffer)
336 339 (insert (hg-run0 "qheader"))
337 340 (goto-char (point-min)))
338 341 (mq-edit-mode)
339 342 (cd root)))
340 343 (message "Type `C-c C-c' to finish editing and refresh the patch."))
341 344
342 345 (defun mq-new (name)
343 346 "Create a new empty patch named NAME.
344 347 The patch is applied on top of the current topmost patch.
345 348 With a prefix argument, forcibly create the patch even if the working
346 349 directory is modified."
347 350 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
348 351 (message "Creating patch...")
349 352 (let ((ret (if current-prefix-arg
350 353 (hg-run "qnew" "-f" name)
351 354 (hg-run "qnew" name))))
352 355 (if (equal (car ret) 0)
353 356 (progn
354 357 (hg-update-mode-lines (buffer-file-name))
355 358 (message "Creating patch... done."))
356 359 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
357 360
358 361 (defun mq-edit-series ()
359 362 "Edit the MQ series file directly."
360 363 (interactive)
361 364 (let ((root (hg-root)))
362 365 (unless root
363 366 (error "Not in an MQ repository!"))
364 367 (find-file (concat root ".hg/patches/series"))))
365 368
366 369 (defun mq-diff (&optional git)
367 370 "Display a diff of the topmost applied patch.
368 371 With a prefix argument, display a git-compatible diff."
369 372 (interactive "P")
370 373 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
371 374 (if git
372 375 (call-process (hg-binary) nil t nil "qdiff" "--git")
373 376 (call-process (hg-binary) nil t nil "qdiff"))
374 377 (diff-mode)
375 378 (font-lock-fontify-buffer)))
376 379
377 380 (defun mq-signoff ()
378 381 "Sign off on the current patch, in the style used by the Linux kernel.
379 382 If the variable mq-signoff-address is non-nil, it will be used, otherwise
380 383 the value of the ui.username item from your hgrc will be used."
381 384 (interactive)
382 385 (let ((was-editing (eq major-mode 'mq-edit-mode))
383 386 signed)
384 387 (unless was-editing
385 388 (mq-refresh-edit))
386 389 (save-excursion
387 390 (let* ((user (or mq-signoff-address
388 391 (hg-run0 "debugconfig" "ui.username")))
389 392 (signoff (concat "Signed-off-by: " user)))
390 393 (if (search-forward signoff nil t)
391 394 (message "You have already signed off on this patch.")
392 395 (goto-char (point-max))
393 396 (let ((case-fold-search t))
394 397 (if (re-search-backward "^Signed-off-by: " nil t)
395 398 (forward-line 1)
396 399 (insert "\n")))
397 400 (insert signoff)
398 401 (message "%s" signoff)
399 402 (setq signed t))))
400 403 (unless was-editing
401 404 (if signed
402 405 (mq-edit-finish)
403 406 (mq-edit-kill)))))
404 407
405 408
406 409 (provide 'mq)
407 410
408 411
409 412 ;;; Local Variables:
410 413 ;;; prompt-to-byte-compile: nil
411 414 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now