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