##// END OF EJS Templates
imported patch mq-refresh-git.patch
Bryan O'Sullivan -
r4426:807c7b99 default
parent child Browse files
Show More
@@ -1,367 +1,368
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 (defun mq-refresh ()
201 "Refresh the topmost applied patch."
202 (interactive)
200 (defun mq-refresh (&optional git)
201 "Refresh the topmost applied patch.
202 With a prefix argument, generate a git-compatible patch."
203 (interactive "P")
203 204 (let ((root (hg-root)))
204 205 (unless root
205 206 (error "Cannot refresh outside of a repository!"))
206 (mq-refresh-internal root)))
207 (apply 'mq-refresh-internal root (if git '("--git")))))
207 208
208 209 (defun mq-patch-info (cmd &optional msg)
209 210 (let* ((ret (hg-run cmd))
210 211 (info (hg-chomp (cdr ret))))
211 212 (if (equal (car ret) 0)
212 213 (if msg
213 214 (message "%s patch: %s" msg info)
214 215 info)
215 216 (error "%s" info))))
216 217
217 218 (defun mq-top ()
218 219 "Print the name of the topmost applied patch."
219 220 (interactive)
220 221 (mq-patch-info "qtop" "Top"))
221 222
222 223 (defun mq-next ()
223 224 "Print the name of the next patch to be pushed."
224 225 (interactive)
225 226 (mq-patch-info "qnext" "Next"))
226 227
227 228 (defun mq-previous ()
228 229 "Print the name of the first patch below the topmost applied patch.
229 230 This would become the active patch if popped to."
230 231 (interactive)
231 232 (mq-patch-info "qprev" "Previous"))
232 233
233 234 (defun mq-edit-finish ()
234 235 "Finish editing the description of this patch, and refresh the patch."
235 236 (interactive)
236 237 (unless (equal (mq-patch-info "qtop") mq-top)
237 238 (error "Topmost patch has changed!"))
238 239 (hg-sync-buffers hg-root)
239 240 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
240 241 (let ((buf mq-prev-buffer))
241 242 (kill-buffer nil)
242 243 (switch-to-buffer buf)))
243 244
244 245 (defun mq-edit-kill ()
245 246 "Kill the edit currently being prepared."
246 247 (interactive)
247 248 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
248 249 (let ((buf mq-prev-buffer))
249 250 (kill-buffer nil)
250 251 (switch-to-buffer buf))))
251 252
252 253 (defun mq-get-top (root)
253 254 (let ((entry (assoc root mq-top-patch)))
254 255 (if entry
255 256 (cdr entry))))
256 257
257 258 (defun mq-set-top (root patch)
258 259 (let ((entry (assoc root mq-top-patch)))
259 260 (if entry
260 261 (if patch
261 262 (setcdr entry patch)
262 263 (setq mq-top-patch (delq entry mq-top-patch)))
263 264 (setq mq-top-patch (cons (cons root patch) mq-top-patch)))))
264 265
265 266 (defun mq-update-mode-lines (root)
266 267 (let ((cwd default-directory))
267 268 (cd root)
268 269 (condition-case nil
269 270 (mq-set-top root (mq-patch-info "qtop"))
270 271 (error (mq-set-top root nil)))
271 272 (cd cwd))
272 273 (let ((patch (mq-get-top root)))
273 274 (save-excursion
274 275 (dolist (buf (hg-buffers-visiting-repo root))
275 276 (set-buffer buf)
276 277 (if mq-mode
277 278 (setq mq-mode (or (and patch (concat " MQ:" patch)) " MQ")))))))
278 279
279 280 (defun mq-mode (&optional arg)
280 281 "Minor mode for Mercurial repositories with an MQ patch queue"
281 282 (interactive "i")
282 283 (cond ((hg-root)
283 284 (setq mq-mode (if (null arg) (not mq-mode)
284 285 arg))
285 286 (mq-update-mode-lines (hg-root))))
286 287 (run-hooks 'mq-mode-hook))
287 288
288 289 (defun mq-edit-mode ()
289 290 "Mode for editing the description of a patch.
290 291
291 292 Key bindings
292 293 ------------
293 294 \\[mq-edit-finish] use this description
294 295 \\[mq-edit-kill] abandon this description"
295 296 (interactive)
296 297 (use-local-map mq-edit-mode-map)
297 298 (set-syntax-table text-mode-syntax-table)
298 299 (setq local-abbrev-table text-mode-abbrev-table
299 300 major-mode 'mq-edit-mode
300 301 mode-name "MQ-Edit")
301 302 (set-buffer-modified-p nil)
302 303 (setq buffer-undo-list nil)
303 304 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
304 305
305 306 (defun mq-refresh-edit ()
306 307 "Refresh the topmost applied patch, editing the patch description."
307 308 (interactive)
308 309 (while mq-prev-buffer
309 310 (set-buffer mq-prev-buffer))
310 311 (let ((root (hg-root))
311 312 (prev-buffer (current-buffer))
312 313 (patch (mq-patch-info "qtop")))
313 314 (hg-sync-buffers root)
314 315 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
315 316 (switch-to-buffer (get-buffer-create buf-name))
316 317 (when (= (point-min) (point-max))
317 318 (set (make-local-variable 'hg-root) root)
318 319 (set (make-local-variable 'mq-top) patch)
319 320 (setq mq-prev-buffer prev-buffer)
320 321 (insert (hg-run0 "qheader"))
321 322 (goto-char (point-min)))
322 323 (mq-edit-mode)
323 324 (cd root)))
324 325 (message "Type `C-c C-c' to finish editing and refresh the patch."))
325 326
326 327 (defun mq-new (name)
327 328 "Create a new empty patch named NAME.
328 329 The patch is applied on top of the current topmost patch.
329 330 With a prefix argument, forcibly create the patch even if the working
330 331 directory is modified."
331 332 (interactive (list (mq-read-patch-name "qseries" " to create" t)))
332 333 (message "Creating patch...")
333 334 (let ((ret (if current-prefix-arg
334 335 (hg-run "qnew" "-f" name)
335 336 (hg-run "qnew" name))))
336 337 (if (equal (car ret) 0)
337 338 (progn
338 339 (hg-update-mode-lines (buffer-file-name))
339 340 (message "Creating patch... done."))
340 341 (error "Creating patch... %s" (hg-chomp (cdr ret))))))
341 342
342 343 (defun mq-edit-series ()
343 344 "Edit the MQ series file directly."
344 345 (interactive)
345 346 (let ((root (hg-root)))
346 347 (unless root
347 348 (error "Not in an MQ repository!"))
348 349 (find-file (concat root ".hg/patches/series"))))
349 350
350 351 (defun mq-diff (&optional git)
351 352 "Display a diff of the topmost applied patch.
352 353 With a prefix argument, display a git-compatible diff."
353 354 (interactive "P")
354 355 (hg-view-output ((format "MQ: Diff of %s" (mq-patch-info "qtop")))
355 356 (if git
356 357 (call-process (hg-binary) nil t nil "qdiff" "--git")
357 358 (call-process (hg-binary) nil t nil "qdiff"))
358 359 (diff-mode)
359 360 (font-lock-fontify-buffer)))
360 361
361 362
362 363 (provide 'mq)
363 364
364 365
365 366 ;;; Local Variables:
366 367 ;;; prompt-to-byte-compile: nil
367 368 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now