##// END OF EJS Templates
In-progress Emacs snapshot.
Bryan O'Sullivan -
r947:4cabedfa default
parent child Browse files
Show More
@@ -1,15 +1,16 b''
1 \.elc$
1 \.orig$
2 \.orig$
2 \.rej$
3 \.rej$
3 ~$
4 ~$
4 \.so$
5 \.so$
5 \.pyc$
6 \.pyc$
6 \.swp$
7 \.swp$
7 \.prof$
8 \.prof$
8 ^tests/.*\.err$
9 ^tests/.*\.err$
9 ^build/
10 ^build/
10 ^dist/
11 ^dist/
11 ^doc/.*\.[0-9](\.(x|ht)ml)?$
12 ^doc/.*\.[0-9](\.(x|ht)ml)?$
12 ^MANIFEST$
13 ^MANIFEST$
13 ^\.pc/
14 ^\.pc/
14 ^patches/
15 ^patches/
15 ^mercurial/__version__.py$
16 ^mercurial/__version__.py$
@@ -1,447 +1,590 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 Bryan O'Sullivan
3 ;; Copyright (C) 2005 Bryan O'Sullivan
4
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
6
7 ;; $Id$
7 ;; $Id$
8
8
9 ;; mercurial.el ("this file") is free software; you can redistribute
9 ;; mercurial.el is free software; you can redistribute it and/or
10 ;; it and/or modify it under the terms of version 2 of the GNU General
10 ;; modify it under the terms of version 2 of the GNU General Public
11 ;; Public License as published by the Free Software Foundation.
11 ;; License as published by the Free Software Foundation.
12
12
13 ;; This file is distributed in the hope that it will be useful, but
13 ;; mercurial.el is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details.
16 ;; General Public License for more details.
17
17
18 ;; You should have received a copy of the GNU General Public License
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this file, GNU Emacs, or XEmacs; see the file COPYING
19 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
20 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
20 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
21 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
22
23 ;;; Commentary:
23 ;;; Commentary:
24
24
25 ;; This mode builds upon Emacs's VC mode to provide flexible
25 ;; This mode builds upon Emacs's VC mode to provide flexible
26 ;; integration with the Mercurial distributed SCM tool.
26 ;; integration with the Mercurial distributed SCM tool.
27
27
28 ;; To get going as quickly as possible, load this file into Emacs and
28 ;; To get going as quickly as possible, load mercurial.el into Emacs and
29 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
29 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
30 ;; usage overview.
30 ;; usage overview.
31
31
32 ;; Much of the inspiration for mercurial.el comes from Rajesh
32 ;; Much of the inspiration for mercurial.el comes from Rajesh
33 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
33 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
34 ;; job for the commercial Perforce SCM product. In fact, substantial
34 ;; job for the commercial Perforce SCM product. In fact, substantial
35 ;; chunks of code are adapted from p4.el.
35 ;; chunks of code are adapted from p4.el.
36
36
37 ;; This code has been developed under XEmacs 21.5, and may will not
37 ;; This code has been developed under XEmacs 21.5, and may will not
38 ;; work as well under GNU Emacs (albeit tested under 21.2). Patches
38 ;; work as well under GNU Emacs (albeit tested under 21.2). Patches
39 ;; to enhance the portability of this code, fix bugs, and add features
39 ;; to enhance the portability of this code, fix bugs, and add features
40 ;; are most welcome. You can clone a Mercurial repository for this
40 ;; are most welcome. You can clone a Mercurial repository for this
41 ;; package from http://www.serpentine.com/hg/hg-emacs
41 ;; package from http://www.serpentine.com/hg/hg-emacs
42
42
43 ;; Please send problem reports and suggestions to bos@serpentine.com.
43 ;; Please send problem reports and suggestions to bos@serpentine.com.
44
44
45
45
46 ;;; Code:
46 ;;; Code:
47
47
48 (require 'advice)
48 (require 'advice)
49 (require 'cl)
49 (require 'cl)
50 (require 'diff-mode)
50 (require 'diff-mode)
51 (require 'easymenu)
51 (require 'easymenu)
52 (require 'vc)
52 (require 'vc)
53
53
54
54
55 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
55 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
56
56
57 (condition-case nil
57 (condition-case nil
58 (require 'view-less)
58 (require 'view-less)
59 (error nil))
59 (error nil))
60 (condition-case nil
60 (condition-case nil
61 (require 'view)
61 (require 'view)
62 (error nil))
62 (error nil))
63
63
64
64
65 ;;; Variables accessible through the custom system.
65 ;;; Variables accessible through the custom system.
66
66
67 (defgroup hg nil
67 (defgroup mercurial nil
68 "Mercurial distributed SCM."
68 "Mercurial distributed SCM."
69 :group 'tools)
69 :group 'tools)
70
70
71 (defcustom hg-binary
71 (defcustom hg-binary
72 (dolist (path '("~/bin/hg"
72 (dolist (path '("~/bin/hg"
73 "/usr/bin/hg"
73 "/usr/bin/hg"
74 "/usr/local/bin/hg"))
74 "/usr/local/bin/hg"))
75 (when (file-executable-p path)
75 (when (file-executable-p path)
76 (return path)))
76 (return path)))
77 "The path to Mercurial's hg executable."
77 "The path to Mercurial's hg executable."
78 :type '(file :must-match t)
78 :type '(file :must-match t)
79 :group 'hg)
79 :group 'mercurial)
80
80
81 (defcustom hg-mode-hook nil
81 (defcustom hg-mode-hook nil
82 "Hook run when a buffer enters hg-mode."
82 "Hook run when a buffer enters hg-mode."
83 :type 'sexp
83 :type 'sexp
84 :group 'hg)
84 :group 'mercurial)
85
85
86 (defcustom hg-global-prefix "\C-ch"
86 (defcustom hg-global-prefix "\C-ch"
87 "The global prefix for Mercurial keymap bindings."
87 "The global prefix for Mercurial keymap bindings."
88 :type 'sexp
88 :type 'sexp
89 :group 'hg)
89 :group 'mercurial)
90
91 (defcustom hg-rev-completion-limit 100
92 "The maximum number of revisions that hg-read-rev will offer to complete.
93 This affects memory usage and performance when prompting for revisions
94 in a repository with a lot of history."
95 :type 'integer
96 :group 'mercurial)
97
98 (defcustom hg-log-limit 50
99 "The maximum number of revisions that hg-log will display."
100 :type 'integer
101 :group 'mercurial)
90
102
91
103
92 ;;; Other variables.
104 ;;; Other variables.
93
105
94 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
106 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
95 "Is mercurial.el running under XEmacs?")
107 "Is mercurial.el running under XEmacs?")
96
108
97 (defvar hg-mode nil
109 (defvar hg-mode nil
98 "Is this file managed by Mercurial?")
110 "Is this file managed by Mercurial?")
111 (make-variable-buffer-local 'hg-mode)
112 (put 'hg-mode 'permanent-local t)
113
114 (defvar hg-status nil)
115 (make-variable-buffer-local 'hg-status)
116 (put 'hg-status 'permanent-local t)
99
117
100 (defvar hg-output-buffer-name "*Hg*"
118 (defvar hg-output-buffer-name "*Hg*"
101 "The name to use for Mercurial output buffers.")
119 "The name to use for Mercurial output buffers.")
102
120
103 (defvar hg-file-name-history nil)
121 (defvar hg-file-history nil)
122 (defvar hg-rev-history nil)
104
123
105
124
106 ;;; hg-mode keymap.
125 ;;; hg-mode keymap.
107
126
108 (defvar hg-prefix-map
127 (defvar hg-prefix-map
109 (let ((map (copy-keymap vc-prefix-map)))
128 (let ((map (copy-keymap vc-prefix-map)))
110 (set-keymap-name map 'hg-prefix-map)
129 (set-keymap-name map 'hg-prefix-map)
111 map)
130 map)
112 "This keymap overrides some default vc-mode bindings.")
131 "This keymap overrides some default vc-mode bindings.")
113 (fset 'hg-prefix-map hg-prefix-map)
132 (fset 'hg-prefix-map hg-prefix-map)
114 (define-key hg-prefix-map "=" 'hg-diff-file)
133 (define-key hg-prefix-map "=" 'hg-diff)
115 (define-key hg-prefix-map "c" 'hg-undo)
134 (define-key hg-prefix-map "c" 'hg-undo)
116 (define-key hg-prefix-map "g" 'hg-annotate)
135 (define-key hg-prefix-map "g" 'hg-annotate)
117 (define-key hg-prefix-map "l" 'hg-log-file)
136 (define-key hg-prefix-map "l" 'hg-log)
137 (define-key hg-prefix-map "n" 'hg-commit-file)
118 ;; (define-key hg-prefix-map "r" 'hg-update)
138 ;; (define-key hg-prefix-map "r" 'hg-update)
119 (define-key hg-prefix-map "u" 'hg-revert-file)
139 (define-key hg-prefix-map "u" 'hg-revert-file)
120 (define-key hg-prefix-map "~" 'hg-version-other-window)
140 (define-key hg-prefix-map "~" 'hg-version-other-window)
121
141
122 (defvar hg-mode-map (make-sparse-keymap))
142 (defvar hg-mode-map (make-sparse-keymap))
123 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
143 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
124
144
145 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
146
125
147
126 ;;; Global keymap.
148 ;;; Global keymap.
127
149
128 (global-set-key "\C-xvi" 'hg-add-file)
150 (global-set-key "\C-xvi" 'hg-add)
129
151
130 (defvar hg-global-map (make-sparse-keymap))
152 (defvar hg-global-map (make-sparse-keymap))
131 (fset 'hg-global-map hg-global-map)
153 (fset 'hg-global-map hg-global-map)
132 (global-set-key hg-global-prefix 'hg-global-map)
154 (global-set-key hg-global-prefix 'hg-global-map)
133 (define-key hg-global-map "," 'hg-incoming)
155 (define-key hg-global-map "," 'hg-incoming)
134 (define-key hg-global-map "." 'hg-outgoing)
156 (define-key hg-global-map "." 'hg-outgoing)
135 (define-key hg-global-map "<" 'hg-pull)
157 (define-key hg-global-map "<" 'hg-pull)
136 (define-key hg-global-map "=" 'hg-diff)
158 (define-key hg-global-map "=" 'hg-diff)
137 (define-key hg-global-map ">" 'hg-push)
159 (define-key hg-global-map ">" 'hg-push)
138 (define-key hg-global-map "?" 'hg-help-overview)
160 (define-key hg-global-map "?" 'hg-help-overview)
139 (define-key hg-global-map "A" 'hg-addremove)
161 (define-key hg-global-map "A" 'hg-addremove)
140 (define-key hg-global-map "U" 'hg-revert)
162 (define-key hg-global-map "U" 'hg-revert)
141 (define-key hg-global-map "a" 'hg-add)
163 (define-key hg-global-map "a" 'hg-add)
142 (define-key hg-global-map "c" 'hg-commit)
164 (define-key hg-global-map "c" 'hg-commit)
165 (define-key hg-global-map "f" 'hg-forget)
143 (define-key hg-global-map "h" 'hg-help-overview)
166 (define-key hg-global-map "h" 'hg-help-overview)
144 (define-key hg-global-map "i" 'hg-init)
167 (define-key hg-global-map "i" 'hg-init)
145 (define-key hg-global-map "l" 'hg-log)
168 (define-key hg-global-map "l" 'hg-log)
146 (define-key hg-global-map "r" 'hg-root)
169 (define-key hg-global-map "r" 'hg-root)
147 (define-key hg-global-map "s" 'hg-status)
170 (define-key hg-global-map "s" 'hg-status)
148 (define-key hg-global-map "u" 'hg-update)
171 (define-key hg-global-map "u" 'hg-update)
149
172
150
173
151 ;;; View mode keymap.
174 ;;; View mode keymap.
152
175
153 (defvar hg-view-mode-map
176 (defvar hg-view-mode-map
154 (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
177 (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
155 view-minor-mode-map
178 view-minor-mode-map
156 view-mode-map))))
179 view-mode-map))))
157 (set-keymap-name map 'hg-view-mode-map)
180 (set-keymap-name map 'hg-view-mode-map)
158 map))
181 map))
159 (fset 'hg-view-mode-map hg-view-mode-map)
182 (fset 'hg-view-mode-map hg-view-mode-map)
160 (define-key hg-view-mode-map
183 (define-key hg-view-mode-map
161 (if hg-running-xemacs [button2] [mouse-2])
184 (if hg-running-xemacs [button2] [mouse-2])
162 'hg-buffer-mouse-clicked)
185 'hg-buffer-mouse-clicked)
163
186
164
187
165 ;;; Convenience functions.
188 ;;; Convenience functions.
166
189
167 (defun hg-binary ()
190 (defun hg-binary ()
168 (if hg-binary
191 (if hg-binary
169 hg-binary
192 hg-binary
170 (error "No `hg' executable found!")))
193 (error "No `hg' executable found!")))
171
194
172 (defun hg-replace-in-string (str regexp newtext &optional literal)
195 (defun hg-replace-in-string (str regexp newtext &optional literal)
173 "Replace all matches in STR for REGEXP with NEWTEXT string.
196 "Replace all matches in STR for REGEXP with NEWTEXT string.
174 Return the new string. Optional LITERAL non-nil means do a literal
197 Return the new string. Optional LITERAL non-nil means do a literal
175 replacement.
198 replacement.
176
199
177 This function bridges yet another pointless impedance gap between
200 This function bridges yet another pointless impedance gap between
178 XEmacs and GNU Emacs."
201 XEmacs and GNU Emacs."
179 (if (fboundp 'replace-in-string)
202 (if (fboundp 'replace-in-string)
180 (replace-in-string str regexp newtext literal)
203 (replace-in-string str regexp newtext literal)
181 (replace-regexp-in-string regexp newtext str nil literal)))
204 (replace-regexp-in-string regexp newtext str nil literal)))
182
205
183 (defun hg-chomp (str)
206 (defun hg-chomp (str)
184 "Strip trailing newlines from a string."
207 "Strip trailing newlines from a string."
185 (hg-replace-in-string str "[\r\n]+$" ""))
208 (hg-replace-in-string str "[\r\n]+$" ""))
186
209
187 (defun hg-run-command (command &rest args)
210 (defun hg-run-command (command &rest args)
188 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
211 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
189 The list ARGS contains a list of arguments to pass to the command."
212 The list ARGS contains a list of arguments to pass to the command."
190 (let* (exit-code
213 (let* (exit-code
191 (output
214 (output
192 (with-output-to-string
215 (with-output-to-string
193 (with-current-buffer
216 (with-current-buffer
194 standard-output
217 standard-output
195 (setq exit-code
218 (setq exit-code
196 (apply 'call-process command nil t nil args))))))
219 (apply 'call-process command nil t nil args))))))
197 (cons exit-code output)))
220 (cons exit-code output)))
198
221
199 (defun hg-run (command &rest args)
222 (defun hg-run (command &rest args)
200 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
223 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
201 (apply 'hg-run-command (hg-binary) command args))
224 (apply 'hg-run-command (hg-binary) command args))
202
225
203 (defun hg-run0 (command &rest args)
226 (defun hg-run0 (command &rest args)
204 "Run the Mercurial command COMMAND, returning its output.
227 "Run the Mercurial command COMMAND, returning its output.
205 If the command does not exit with a zero status code, raise an error."
228 If the command does not exit with a zero status code, raise an error."
206 (let ((res (apply 'hg-run-command (hg-binary) command args)))
229 (let ((res (apply 'hg-run-command (hg-binary) command args)))
207 (if (not (eq (car res) 0))
230 (if (not (eq (car res) 0))
208 (error "Mercurial command failed %s - exit code %s"
231 (error "Mercurial command failed %s - exit code %s"
209 (cons command args)
232 (cons command args)
210 (car res))
233 (car res))
211 (cdr res))))
234 (cdr res))))
212
235
213 (defun hg-buffer-commands (pnt)
236 (defun hg-buffer-commands (pnt)
214 "Use the properties of a character to do something sensible."
237 "Use the properties of a character to do something sensible."
215 (interactive "d")
238 (interactive "d")
216 (let ((rev (get-char-property pnt 'rev))
239 (let ((rev (get-char-property pnt 'rev))
217 (file (get-char-property pnt 'file))
240 (file (get-char-property pnt 'file))
218 (date (get-char-property pnt 'date))
241 (date (get-char-property pnt 'date))
219 (user (get-char-property pnt 'user))
242 (user (get-char-property pnt 'user))
220 (host (get-char-property pnt 'host))
243 (host (get-char-property pnt 'host))
221 (prev-buf (current-buffer)))
244 (prev-buf (current-buffer)))
222 (cond
245 (cond
223 (file
246 (file
224 (find-file-other-window file))
247 (find-file-other-window file))
225 (rev
248 (rev
226 (hg-diff hg-view-file-name rev rev prev-buf))
249 (hg-diff hg-view-file-name rev rev prev-buf))
227 ((message "I don't know how to do that yet")))))
250 ((message "I don't know how to do that yet")))))
228
251
229 (defun hg-buffer-mouse-clicked (event)
252 (defun hg-buffer-mouse-clicked (event)
230 "Translate the mouse clicks in a HG log buffer to character events.
253 "Translate the mouse clicks in a HG log buffer to character events.
231 These are then handed off to `hg-buffer-commands'.
254 These are then handed off to `hg-buffer-commands'.
232
255
233 Handle frickin' frackin' gratuitous event-related incompatibilities."
256 Handle frickin' frackin' gratuitous event-related incompatibilities."
234 (interactive "e")
257 (interactive "e")
235 (if hg-running-xemacs
258 (if hg-running-xemacs
236 (progn
259 (progn
237 (select-window (event-window event))
260 (select-window (event-window event))
238 (hg-buffer-commands (event-point event)))
261 (hg-buffer-commands (event-point event)))
239 (select-window (posn-window (event-end event)))
262 (select-window (posn-window (event-end event)))
240 (hg-buffer-commands (posn-point (event-start event)))))
263 (hg-buffer-commands (posn-point (event-start event)))))
241
264
242 (unless (fboundp 'view-minor-mode)
265 (unless (fboundp 'view-minor-mode)
243 (defun view-minor-mode (prev-buffer exit-func)
266 (defun view-minor-mode (prev-buffer exit-func)
244 (view-mode)))
267 (view-mode)))
245
268
246 (defun hg-abbrev-file-name (file)
269 (defun hg-abbrev-file-name (file)
247 (if hg-running-xemacs
270 (if hg-running-xemacs
248 (abbreviate-file-name file t)
271 (abbreviate-file-name file t)
249 (abbreviate-file-name file)))
272 (abbreviate-file-name file)))
250
273
274 (defun hg-read-file-name (&optional prompt default)
275 "Read a file or directory name, or a pattern, to use with a command."
276 (let ((path (or default (buffer-file-name))))
277 (if (or (not path) current-prefix-arg)
278 (expand-file-name
279 (read-file-name (format "File, directory or pattern%s: "
280 (or prompt ""))
281 (and path (file-name-directory path))
282 nil nil
283 (and path (file-name-nondirectory path))
284 'hg-file-history))
285 path)))
286
287 (defun hg-read-rev (&optional prompt default)
288 "Read a revision or tag, offering completions."
289 (let ((rev (or default "tip")))
290 (if (or (not rev) current-prefix-arg)
291 (let ((revs (split-string (hg-chomp
292 (hg-run0 "-q" "log" "-r"
293 (format "-%d"
294 hg-rev-completion-limit)
295 "-r" "tip"))
296 "[\n:]")))
297 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
298 (setq revs (cons (car (split-string line "\\s-")) revs)))
299 (completing-read (format "Revision%s (%s): "
300 (or prompt "")
301 (or default "tip"))
302 (map 'list 'cons revs revs)
303 nil
304 nil
305 nil
306 'hg-rev-history
307 (or default "tip")))
308 rev)))
251
309
252 ;;; View mode bits.
310 ;;; View mode bits.
253
311
254 (defun hg-exit-view-mode (buf)
312 (defun hg-exit-view-mode (buf)
255 "Exit from hg-view-mode.
313 "Exit from hg-view-mode.
256 We delete the current window if entering hg-view-mode split the
314 We delete the current window if entering hg-view-mode split the
257 current frame."
315 current frame."
258 (when (and (eq buf (current-buffer))
316 (when (and (eq buf (current-buffer))
259 (> (length (window-list)) 1))
317 (> (length (window-list)) 1))
260 (delete-window))
318 (delete-window))
261 (when (buffer-live-p buf)
319 (when (buffer-live-p buf)
262 (kill-buffer buf)))
320 (kill-buffer buf)))
263
321
264 (defun hg-view-mode (prev-buffer &optional file-name)
322 (defun hg-view-mode (prev-buffer &optional file-name)
265 (goto-char (point-min))
323 (goto-char (point-min))
266 (set-buffer-modified-p nil)
324 (set-buffer-modified-p nil)
267 (toggle-read-only t)
325 (toggle-read-only t)
268 (view-minor-mode prev-buffer 'hg-exit-view-mode)
326 (view-minor-mode prev-buffer 'hg-exit-view-mode)
269 (use-local-map hg-view-mode-map)
327 (use-local-map hg-view-mode-map)
270 (setq truncate-lines t)
328 (setq truncate-lines t)
271 (when file-name
329 (when file-name
272 (set (make-local-variable 'hg-view-file-name)
330 (set (make-local-variable 'hg-view-file-name)
273 (hg-abbrev-file-name file-name))))
331 (hg-abbrev-file-name file-name))))
274
332
333 (defun hg-file-status (file)
334 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
335 (let* ((s (hg-run "status" file))
336 (exit (car s))
337 (output (cdr s)))
338 (if (= exit 0)
339 (let ((state (assoc (substring output 0 (min (length output) 2))
340 '(("M " . modified)
341 ("A " . added)
342 ("R " . removed)))))
343 (if state
344 (cdr state)
345 'normal)))))
346
347 (defun hg-tip ()
348 (split-string (hg-chomp (hg-run0 "-q" "tip")) ":"))
349
275 (defmacro hg-view-output (args &rest body)
350 (defmacro hg-view-output (args &rest body)
276 "Execute BODY in a clean buffer, then switch that buffer to view-mode.
351 "Execute BODY in a clean buffer, then quickly display that buffer.
352 If the buffer contains one line, its contents are displayed in the
353 minibuffer. Otherwise, the buffer is displayed in view-mode.
277 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
354 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
278 the name of the buffer to create, and FILE is the name of the file
355 the name of the buffer to create, and FILE is the name of the file
279 being viewed."
356 being viewed."
280 (let ((prev-buf (gensym "prev-buf-"))
357 (let ((prev-buf (gensym "prev-buf-"))
281 (v-b-name (car args))
358 (v-b-name (car args))
282 (v-m-rest (cdr args)))
359 (v-m-rest (cdr args)))
283 `(let ((view-buf-name ,v-b-name)
360 `(let ((view-buf-name ,v-b-name)
284 (,prev-buf (current-buffer)))
361 (,prev-buf (current-buffer)))
285 (get-buffer-create view-buf-name)
362 (get-buffer-create view-buf-name)
286 (kill-buffer view-buf-name)
363 (kill-buffer view-buf-name)
287 (pop-to-buffer view-buf-name)
364 (get-buffer-create view-buf-name)
365 (set-buffer view-buf-name)
288 (save-excursion
366 (save-excursion
289 ,@body)
367 ,@body)
290 (hg-view-mode ,prev-buf ,@v-m-rest))))
368 (case (count-lines (point-min) (point-max))
369 ((0)
370 (kill-buffer view-buf-name)
371 (message "(No output)"))
372 ((1)
373 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
374 (kill-buffer view-buf-name)
375 (message "%s" msg)))
376 (t
377 (pop-to-buffer view-buf-name)
378 (hg-view-mode ,prev-buf ,@v-m-rest))))))
291
379
292 (put 'hg-view-output 'lisp-indent-function 1)
380 (put 'hg-view-output 'lisp-indent-function 1)
381
382 ;;; Hooks.
383
384 (defun hg-mode-line ()
385 (when (hg-root)
386 (let ((status (hg-file-status buffer-file-name)))
387 (setq hg-status status
388 hg-mode (and status (concat " Hg:"
389 (car (hg-tip))
390 (cdr (assq status
391 '((normal . "")
392 (removed . "r")
393 (added . "a")
394 (modified . "m")))))))
395 status)))
396
397 (defun hg-find-file-hook ()
398 (when (hg-mode-line)
399 (run-hooks 'hg-mode-hook)))
400
401 (add-hook 'find-file-hooks 'hg-find-file-hook)
402
403 (defun hg-after-save-hook ()
404 (let ((old-status hg-status))
405 (hg-mode-line)
406 (if (and (not old-status) hg-status)
407 (run-hooks 'hg-mode-hook))))
408
409 (add-hook 'after-save-hook 'hg-after-save-hook)
293
410
294
411
295 ;;; User interface functions.
412 ;;; User interface functions.
296
413
297 (defun hg-help-overview ()
414 (defun hg-help-overview ()
298 "This is an overview of the Mercurial SCM mode for Emacs.
415 "This is an overview of the Mercurial SCM mode for Emacs.
299
416
300 You can find the source code, license (GPL v2), and credits for this
417 You can find the source code, license (GPL v2), and credits for this
301 code by typing `M-x find-library mercurial RET'.
418 code by typing `M-x find-library mercurial RET'.
302
419
303 The Mercurial mode user interface is based on that of the older VC
420 The Mercurial mode user interface is based on that of the older VC
304 mode, so if you're already familiar with VC, the same keybindings and
421 mode, so if you're already familiar with VC, the same keybindings and
305 functions will generally work.
422 functions will generally work.
306
423
307 Below is a list of common SCM tasks, with the key bindings needed to
424 Below is a list of common SCM tasks, with the key bindings needed to
308 perform them, and the command names. This list is not exhaustive.
425 perform them, and the command names. This list is not exhaustive.
309
426
310 In the list below, `G/L' indicates whether a key binding is global (G)
427 In the list below, `G/L' indicates whether a key binding is global (G)
311 or local (L). Global keybindings work on any file inside a Mercurial
428 or local (L). Global keybindings work on any file inside a Mercurial
312 repository. Local keybindings only apply to files under the control
429 repository. Local keybindings only apply to files under the control
313 of Mercurial. Many commands take a prefix argument.
430 of Mercurial. Many commands take a prefix argument.
314
431
315
432
316 SCM Task G/L Key Binding Command Name
433 SCM Task G/L Key Binding Command Name
317 -------- --- ----------- ------------
434 -------- --- ----------- ------------
318 Help overview (what you are reading) G C-c h h hg-help-overview
435 Help overview (what you are reading) G C-c h h hg-help-overview
319
436
320 Tell Mercurial to manage a file G C-x v i hg-add-file
437 Tell Mercurial to manage a file G C-c h a hg-add
321 Commit changes to current file only L C-x C-q vc-toggle-read-only
438 Commit changes to current file only L C-x v n hg-commit
322 Undo changes to file since commit L C-x v u hg-revert-file
439 Undo changes to file since commit L C-x v u hg-revert-file
323
440
324 Diff file vs last checkin L C-x v = hg-diff-file
441 Diff file vs last checkin L C-x v = hg-diff
325
442
326 View file change history L C-x v l hg-log-file
443 View file change history L C-x v l hg-log
327 View annotated file L C-x v a hg-annotate
444 View annotated file L C-x v a hg-annotate
328
445
329 Diff repo vs last checkin G C-c h = hg-diff
446 Diff repo vs last checkin G C-c h = hg-diff
330 View status of files in repo G C-c h s hg-status
447 View status of files in repo G C-c h s hg-status
331 Commit all changes G C-c h c hg-commit
448 Commit all changes G C-c h c hg-commit
332
449
333 Undo all changes since last commit G C-c h U hg-revert
450 Undo all changes since last commit G C-c h U hg-revert
334 View repo change history G C-c h l hg-log
451 View repo change history G C-c h l hg-log
335
452
336 See changes that can be pulled G C-c h , hg-incoming
453 See changes that can be pulled G C-c h , hg-incoming
337 Pull changes G C-c h < hg-pull
454 Pull changes G C-c h < hg-pull
338 Update working directory after pull G C-c h u hg-update
455 Update working directory after pull G C-c h u hg-update
339 See changes that can be pushed G C-c h . hg-outgoing
456 See changes that can be pushed G C-c h . hg-outgoing
340 Push changes G C-c h > hg-push"
457 Push changes G C-c h > hg-push"
341 (interactive)
458 (interactive)
342 (hg-view-output ("Mercurial Help Overview")
459 (hg-view-output ("Mercurial Help Overview")
343 (insert (documentation 'hg-help-overview))))
460 (insert (documentation 'hg-help-overview))))
344
461
345 (defun hg-add ()
462 (defun hg-add (path)
346 (interactive)
463 (interactive (list (hg-read-file-name " to add")))
347 (error "not implemented"))
464 (let ((buf (current-buffer))
348
465 (update (equal buffer-file-name path)))
349 (defun hg-add-file ()
466 (hg-view-output (hg-output-buffer-name)
350 (interactive)
467 (apply 'call-process (hg-binary) nil t nil (list "add" path)))
351 (error "not implemented"))
468 (when update
469 (with-current-buffer buf
470 (hg-mode-line)))))
352
471
353 (defun hg-addremove ()
472 (defun hg-addremove ()
354 (interactive)
473 (interactive)
355 (error "not implemented"))
474 (error "not implemented"))
356
475
357 (defun hg-annotate ()
476 (defun hg-annotate ()
358 (interactive)
477 (interactive)
359 (error "not implemented"))
478 (error "not implemented"))
360
479
361 (defun hg-commit ()
480 (defun hg-commit ()
362 (interactive)
481 (interactive)
363 (error "not implemented"))
482 (error "not implemented"))
364
483
365 (defun hg-diff ()
484 (defun hg-diff (path &optional rev1 rev2)
366 (interactive)
485 (interactive (list (hg-read-file-name " to diff")
367 (error "not implemented"))
486 (hg-read-rev " to start with")
487 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
488 (and (not (eq rev2 'working-dir)) rev2))))
489 (let ((a-path (hg-abbrev-file-name path)))
490 (hg-view-output ((if (equal rev1 rev2)
491 (format "Mercurial: Rev %s of %s" rev1 a-path)
492 (format "Mercurial: Rev %s to %s of %s"
493 rev1 (or rev2 "Current") a-path)))
494 (if rev2
495 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)
496 (call-process (hg-binary) nil t nil "diff" "-r" rev1 path))
497 (diff-mode)
498 (font-lock-fontify-buffer))))
368
499
369 (defun hg-diff-file ()
500 (defun hg-forget (path)
370 (interactive)
501 (interactive (list (hg-read-file-name " to forget")))
371 (error "not implemented"))
502 (let ((buf (current-buffer))
372
503 (update (equal buffer-file-name path)))
504 (hg-view-output (hg-output-buffer-name)
505 (apply 'call-process (hg-binary) nil t nil (list "forget" path)))
506 (when update
507 (with-current-buffer buf
508 (hg-mode-line)))))
509
373 (defun hg-incoming ()
510 (defun hg-incoming ()
374 (interactive)
511 (interactive)
375 (error "not implemented"))
512 (error "not implemented"))
376
513
377 (defun hg-init ()
514 (defun hg-init ()
378 (interactive)
515 (interactive)
379 (error "not implemented"))
516 (error "not implemented"))
380
517
381 (defun hg-log-file ()
518 (defun hg-log (path &optional rev1 rev2)
382 (interactive)
519 (interactive (list (hg-read-file-name " to log")
383 (error "not implemented"))
520 (hg-read-rev " to start with" "-1")
384
521 (hg-read-rev " to end with" (format "-%d" hg-log-limit))))
385 (defun hg-log ()
522 (message "log %s %s" rev1 rev2)
386 (interactive)
523 (sit-for 1)
387 (error "not implemented"))
524 (let ((a-path (hg-abbrev-file-name path)))
525 (hg-view-output ((if (equal rev1 rev2)
526 (format "Mercurial: Rev %s of %s" rev1 a-path)
527 (format "Mercurial: Rev %s to %s of %s"
528 rev1 (or rev2 "Current") a-path)))
529 (call-process (hg-binary) nil t nil "log" "-r" rev1 "-r" rev2 path)
530 (diff-mode)
531 (font-lock-fontify-buffer))))
388
532
389 (defun hg-outgoing ()
533 (defun hg-outgoing ()
390 (interactive)
534 (interactive)
391 (error "not implemented"))
535 (error "not implemented"))
392
536
393 (defun hg-pull ()
537 (defun hg-pull ()
394 (interactive)
538 (interactive)
395 (error "not implemented"))
539 (error "not implemented"))
396
540
397 (defun hg-push ()
541 (defun hg-push ()
398 (interactive)
542 (interactive)
399 (error "not implemented"))
543 (error "not implemented"))
400
544
401 (defun hg-revert ()
545 (defun hg-revert ()
402 (interactive)
546 (interactive)
403 (error "not implemented"))
547 (error "not implemented"))
404
548
405 (defun hg-revert-file ()
549 (defun hg-revert-file ()
406 (interactive)
550 (interactive)
407 (error "not implemented"))
551 (error "not implemented"))
408
552
409 (defun hg-root (&optional path)
553 (defun hg-root (&optional path)
410 (interactive)
554 (interactive (list (hg-read-file-name)))
411 (unless path
412 (setq path (if (and (interactive-p) current-prefix-arg)
413 (expand-file-name (read-file-name "Path name: "))
414 (or (buffer-file-name) "(none)"))))
415 (let ((root (do ((prev nil dir)
555 (let ((root (do ((prev nil dir)
416 (dir (file-name-directory path)
556 (dir (file-name-directory (or path (buffer-file-name)))
417 (file-name-directory (directory-file-name dir))))
557 (file-name-directory (directory-file-name dir))))
418 ((equal prev dir))
558 ((equal prev dir))
419 (when (file-directory-p (concat dir ".hg"))
559 (when (file-directory-p (concat dir ".hg"))
420 (return dir)))))
560 (return dir)))))
421 (when (interactive-p)
561 (when (interactive-p)
422 (if root
562 (if root
423 (message "The root of this repository is `%s'." root)
563 (message "The root of this repository is `%s'." root)
424 (message "The path `%s' is not in a Mercurial repository."
564 (message "The path `%s' is not in a Mercurial repository."
425 (abbreviate-file-name path t))))
565 (abbreviate-file-name path t))))
426 root))
566 root))
427
567
428 (defun hg-status ()
568 (defun hg-status (path)
429 (interactive)
569 (interactive (list (hg-read-file-name " for status" (hg-root))))
430 (error "not implemented"))
570 (let ((root (hg-root)))
571 (hg-view-output (hg-output-buffer-name)
572 (apply 'call-process (hg-binary) nil t nil
573 (list "-C" root "status" path)))))
431
574
432 (defun hg-undo ()
575 (defun hg-undo ()
433 (interactive)
576 (interactive)
434 (error "not implemented"))
577 (error "not implemented"))
435
578
436 (defun hg-version-other-window ()
579 (defun hg-version-other-window ()
437 (interactive)
580 (interactive)
438 (error "not implemented"))
581 (error "not implemented"))
439
582
440
583
441 (provide 'mercurial)
584 (provide 'mercurial)
442
585
443
586
444 ;;; Local Variables:
587 ;;; Local Variables:
445 ;;; mode: emacs-lisp
588 ;;; mode: emacs-lisp
446 ;;; prompt-to-byte-compile: nil
589 ;;; prompt-to-byte-compile: nil
447 ;;; end:
590 ;;; end:
General Comments 0
You need to be logged in to leave comments. Login now