##// END OF EJS Templates
mq.el: allow mq-diff to take a git option.
Bryan O'Sullivan -
r4425:a57ac604 default
parent child Browse files
Show More
@@ -1,364 +1,367 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 (require 'mercurial)
22 22
23 23
24 24 (defcustom mq-mode-hook nil
25 25 "Hook run when a buffer enters mq-mode."
26 26 :type 'sexp
27 27 :group 'mercurial)
28 28
29 29 (defcustom mq-global-prefix "\C-cq"
30 30 "The global prefix for Mercurial Queues keymap bindings."
31 31 :type 'sexp
32 32 :group 'mercurial)
33 33
34 34 (defcustom mq-edit-mode-hook nil
35 35 "Hook run after a buffer is populated to edit a patch description."
36 36 :type 'sexp
37 37 :group 'mercurial)
38 38
39 39
40 40 ;;; Internal variables.
41 41
42 42 (defvar mq-mode nil
43 43 "Is this file managed by MQ?")
44 44 (make-variable-buffer-local 'mq-mode)
45 45 (put 'mq-mode 'permanent-local t)
46 46
47 47 (defvar mq-patch-history nil)
48 48
49 49 (defvar mq-top-patch '(nil))
50 50
51 51 (defvar mq-prev-buffer nil)
52 52 (make-variable-buffer-local 'mq-prev-buffer)
53 53 (put 'mq-prev-buffer 'permanent-local t)
54 54
55 55
56 56 ;;; Global keymap.
57 57
58 58 (defvar mq-global-map (make-sparse-keymap))
59 59 (fset 'mq-global-map mq-global-map)
60 60 (global-set-key mq-global-prefix 'mq-global-map)
61 61 (define-key mq-global-map "." 'mq-push)
62 62 (define-key mq-global-map ">" 'mq-push-all)
63 63 (define-key mq-global-map "," 'mq-pop)
64 64 (define-key mq-global-map "<" 'mq-pop-all)
65 65 (define-key mq-global-map "=" 'mq-diff)
66 66 (define-key mq-global-map "r" 'mq-refresh)
67 67 (define-key mq-global-map "e" 'mq-refresh-edit)
68 68 (define-key mq-global-map "i" 'mq-new)
69 69 (define-key mq-global-map "n" 'mq-next)
70 70 (define-key mq-global-map "p" 'mq-previous)
71 71 (define-key mq-global-map "s" 'mq-edit-series)
72 72 (define-key mq-global-map "t" 'mq-top)
73 73
74 74 (add-minor-mode 'mq-mode 'mq-mode)
75 75
76 76
77 77 ;;; Refresh edit mode keymap.
78 78
79 79 (defvar mq-edit-mode-map (make-sparse-keymap))
80 80 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
81 81 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
82 82
83 83
84 84 ;;; Helper functions.
85 85
86 86 (defun mq-read-patch-name (&optional source prompt force)
87 87 "Read a patch name to use with a command.
88 88 May return nil, meaning \"use the default\"."
89 89 (let ((patches (split-string
90 90 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
91 91 (when force
92 92 (completing-read (format "Patch%s: " (or prompt ""))
93 93 (map 'list 'cons patches patches)
94 94 nil
95 95 nil
96 96 nil
97 97 'mq-patch-history))))
98 98
99 99 (defun mq-refresh-buffers (root)
100 100 (save-excursion
101 101 (dolist (buf (hg-buffers-visiting-repo root))
102 102 (when (not (verify-visited-file-modtime buf))
103 103 (set-buffer buf)
104 104 (let ((ctx (hg-buffer-context)))
105 105 (message "Refreshing %s..." (buffer-name))
106 106 (revert-buffer t t t)
107 107 (hg-restore-context ctx)
108 108 (message "Refreshing %s...done" (buffer-name))))))
109 109 (hg-update-mode-lines root)
110 110 (mq-update-mode-lines root))
111 111
112 112 (defun mq-last-line ()
113 113 (goto-char (point-max))
114 114 (beginning-of-line)
115 115 (when (looking-at "^$")
116 116 (forward-line -1))
117 117 (let ((bol (point)))
118 118 (end-of-line)
119 119 (let ((line (buffer-substring bol (point))))
120 120 (when (> (length line) 0)
121 121 line))))
122 122
123 123 (defun mq-push (&optional patch)
124 124 "Push patches until PATCH is reached.
125 125 If PATCH is nil, push at most one patch."
126 126 (interactive (list (mq-read-patch-name "qunapplied" " to push"
127 127 current-prefix-arg)))
128 128 (let ((root (hg-root))
129 129 (prev-buf (current-buffer))
130 130 last-line ok)
131 131 (unless root
132 132 (error "Cannot push outside a repository!"))
133 133 (hg-sync-buffers root)
134 134 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
135 135 (kill-buffer (get-buffer-create buf-name))
136 136 (split-window-vertically)
137 137 (other-window 1)
138 138 (switch-to-buffer (get-buffer-create buf-name))
139 139 (cd root)
140 140 (message "Pushing...")
141 141 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
142 142 (if patch (list patch))))
143 143 last-line (mq-last-line))
144 144 (let ((lines (count-lines (point-min) (point-max))))
145 145 (if (and (equal lines 2) (string-match "Now at:" last-line))
146 146 (progn
147 147 (kill-buffer (current-buffer))
148 148 (delete-window))
149 149 (hg-view-mode prev-buf))))
150 150 (mq-refresh-buffers root)
151 151 (sit-for 0)
152 152 (when last-line
153 153 (if ok
154 154 (message "Pushing... %s" last-line)
155 155 (error "Pushing... %s" last-line)))))
156 156
157 157 (defun mq-push-all ()
158 158 "Push patches until all are applied."
159 159 (interactive)
160 160 (mq-push "-a"))
161 161
162 162 (defun mq-pop (&optional patch)
163 163 "Pop patches until PATCH is reached.
164 164 If PATCH is nil, pop at most one patch."
165 165 (interactive (list (mq-read-patch-name "qapplied" " to pop to"
166 166 current-prefix-arg)))
167 167 (let ((root (hg-root))
168 168 last-line ok)
169 169 (unless root
170 170 (error "Cannot pop outside a repository!"))
171 171 (hg-sync-buffers root)
172 172 (set-buffer (generate-new-buffer "qpop"))
173 173 (cd root)
174 174 (message "Popping...")
175 175 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
176 176 (if patch (list patch))))
177 177 last-line (mq-last-line))
178 178 (kill-buffer (current-buffer))
179 179 (mq-refresh-buffers root)
180 180 (sit-for 0)
181 181 (when last-line
182 182 (if ok
183 183 (message "Popping... %s" last-line)
184 184 (error "Popping... %s" last-line)))))
185 185
186 186 (defun mq-pop-all ()
187 187 "Push patches until none are applied."
188 188 (interactive)
189 189 (mq-pop "-a"))
190 190
191 191 (defun mq-refresh-internal (root &rest args)
192 192 (hg-sync-buffers root)
193 193 (let ((patch (mq-patch-info "qtop")))
194 194 (message "Refreshing %s..." patch)
195 195 (let ((ret (apply 'hg-run "qrefresh" args)))
196 196 (if (equal (car ret) 0)
197 197 (message "Refreshing %s... done." patch)
198 198 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
199 199
200 200 (defun mq-refresh ()
201 201 "Refresh the topmost applied patch."
202 202 (interactive)
203 203 (let ((root (hg-root)))
204 204 (unless root
205 205 (error "Cannot refresh outside of a repository!"))
206 206 (mq-refresh-internal root)))
207 207
208 208 (defun mq-patch-info (cmd &optional msg)
209 209 (let* ((ret (hg-run cmd))
210 210 (info (hg-chomp (cdr ret))))
211 211 (if (equal (car ret) 0)
212 212 (if msg
213 213 (message "%s patch: %s" msg info)
214 214 info)
215 215 (error "%s" info))))
216 216
217 217 (defun mq-top ()
218 218 "Print the name of the topmost applied patch."
219 219 (interactive)
220 220 (mq-patch-info "qtop" "Top"))
221 221
222 222 (defun mq-next ()
223 223 "Print the name of the next patch to be pushed."
224 224 (interactive)
225 225 (mq-patch-info "qnext" "Next"))
226 226
227 227 (defun mq-previous ()
228 228 "Print the name of the first patch below the topmost applied patch.
229 229 This would become the active patch if popped to."
230 230 (interactive)
231 231 (mq-patch-info "qprev" "Previous"))
232 232
233 233 (defun mq-edit-finish ()
234 234 "Finish editing the description of this patch, and refresh the patch."
235 235 (interactive)
236 236 (unless (equal (mq-patch-info "qtop") mq-top)
237 237 (error "Topmost patch has changed!"))
238 238 (hg-sync-buffers hg-root)
239 239 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
240 240 (let ((buf mq-prev-buffer))
241 241 (kill-buffer nil)
242 242 (switch-to-buffer buf)))
243 243
244 244 (defun mq-edit-kill ()
245 245 "Kill the edit currently being prepared."
246 246 (interactive)
247 247 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
248 248 (let ((buf mq-prev-buffer))
249 249 (kill-buffer nil)
250 250 (switch-to-buffer buf))))
251 251
252 252 (defun mq-get-top (root)
253 253 (let ((entry (assoc root mq-top-patch)))
254 254 (if entry
255 255 (cdr entry))))
256 256
257 257 (defun mq-set-top (root patch)
258 258 (let ((entry (assoc root mq-top-patch)))
259 259 (if entry
260 260 (if patch
261 261 (setcdr entry patch)
262 262 (setq mq-top-patch (delq entry mq-top-patch)))
263 263 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
264 264
265 265 (defun mq-update-mode-lines (root)
266 266 (let ((cwd default-directory))
267 267 (cd root)
268 268 (condition-case nil
269 269 (mq-set-top root (mq-patch-info "qtop"))
270 270 (error (mq-set-top root nil)))
271 271 (cd cwd))
272 272 (let ((patch (mq-get-top root)))
273 273 (save-excursion
274 274 (dolist (buf (hg-buffers-visiting-repo root))
275 275 (set-buffer buf)
276 276 (if mq-mode
277 277 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
278 278
279 279 (defun mq-mode (&optional arg)
280 280 "Minor mode for Mercurial repositories with an MQ patch queue"
281 281 (interactive "i")
282 282 (cond ((hg-root)
283 283 (setq mq-mode (if (null arg) (not mq-mode)
284 284 arg))
285 285 (mq-update-mode-lines (hg-root))))
286 286 (run-hooks 'mq-mode-hook))
287 287
288 288 (defun mq-edit-mode ()
289 289 "Mode for editing the description of a patch.
290 290
291 291 Key bindings
292 292 ------------
293 293 \\[mq-edit-finish] use this description
294 294 \\[mq-edit-kill] abandon this description"
295 295 (interactive)
296 296 (use-local-map mq-edit-mode-map)
297 297 (set-syntax-table text-mode-syntax-table)
298 298 (setq local-abbrev-table text-mode-abbrev-table
299 299 major-mode 'mq-edit-mode
300 300 mode-name "MQ-Edit")
301 301 (set-buffer-modified-p nil)
302 302 (setq buffer-undo-list nil)
303 303 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
304 304
305 305 (defun mq-refresh-edit ()
306 306 "Refresh the topmost applied patch, editing the patch description."
307 307 (interactive)
308 308 (while mq-prev-buffer
309 309 (set-buffer mq-prev-buffer))
310 310 (let ((root (hg-root))
311 311 (prev-buffer (current-buffer))
312 312 (patch (mq-patch-info "qtop")))
313 313 (hg-sync-buffers root)
314 314 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
315 315 (switch-to-buffer (get-buffer-create buf-name))
316 316 (when (= (point-min) (point-max))
317 317 (set (make-local-variable 'hg-root) root)
318 318 (set (make-local-variable 'mq-top) patch)
319 319 (setq mq-prev-buffer prev-buffer)
320 320 (insert (hg-run0 "qheader"))
321 321 (goto-char (point-min)))
322 322 (mq-edit-mode)
323 323 (cd root)))
324 324 (message "Type `C-c C-c' to finish editing and refresh the patch."))
325 325
326 326 (defun mq-new (name)
327 327 "Create a new empty patch named NAME.
328 328 The patch is applied on top of the current topmost patch.
329 329 With a prefix argument, forcibly create the patch even if the working
330 330 directory is modified."
331 331 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
332 332 (message "Creating patch...")
333 333 (let ((ret (if current-prefix-arg
334 334 (hg-run "qnew" "-f" name)
335 335 (hg-run "qnew" name))))
336 336 (if (equal (car ret) 0)
337 337 (progn
338 338 (hg-update-mode-lines (buffer-file-name))
339 339 (message "Creating patch... done."))
340 340 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
341 341
342 342 (defun mq-edit-series ()
343 343 "Edit the MQ series file directly."
344 344 (interactive)
345 345 (let ((root (hg-root)))
346 346 (unless root
347 347 (error "Not in an MQ repository!"))
348 348 (find-file (concat root ".hg/patches/series"))))
349 349
350 (defun mq-diff ()
351 "Display a diff of the topmost applied patch."
352 (interactive)
350 (defun mq-diff (&optional git)
351 "Display a diff of the topmost applied patch.
352 With a prefix argument, display a git-compatible diff."
353 (interactive "P")
353 354 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
354 (call-process (hg-binary) nil t nil "qdiff")
355 (if git
356 (call-process (hg-binary) nil t nil "qdiff" "--git")
357 (call-process (hg-binary) nil t nil "qdiff"))
355 358 (diff-mode)
356 359 (font-lock-fontify-buffer)))
357 360
358 361
359 362 (provide 'mq)
360 363
361 364
362 365 ;;; Local Variables:
363 366 ;;; prompt-to-byte-compile: nil
364 367 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now