Mercurial > hg
comparison contrib/mercurial.el @ 999:bb391518bc28
Emacs: first cut at commit support.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Mon, 22 Aug 2005 03:16:32 -0700 |
parents | 5ed566574486 |
children | 3362b410c219 |
comparison
equal
deleted
inserted
replaced
998:c37dd58a444a | 999:bb391518bc28 |
---|---|
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 'mercurial) | 89 :group 'mercurial) |
90 | 90 |
91 (defcustom hg-commit-allow-empty-message nil | |
92 "Whether to allow changes to be committed with empty descriptions." | |
93 :type 'boolean | |
94 :group 'mercurial) | |
95 | |
96 (defcustom hg-commit-allow-empty-file-list nil | |
97 "Whether to allow changes to be committed without any modified files." | |
98 :type 'boolean | |
99 :group 'mercurial) | |
100 | |
91 (defcustom hg-rev-completion-limit 100 | 101 (defcustom hg-rev-completion-limit 100 |
92 "The maximum number of revisions that hg-read-rev will offer to complete. | 102 "The maximum number of revisions that hg-read-rev will offer to complete. |
93 This affects memory usage and performance when prompting for revisions | 103 This affects memory usage and performance when prompting for revisions |
94 in a repository with a lot of history." | 104 in a repository with a lot of history." |
95 :type 'integer | 105 :type 'integer |
124 (defvar hg-output-buffer-name "*Hg*" | 134 (defvar hg-output-buffer-name "*Hg*" |
125 "The name to use for Mercurial output buffers.") | 135 "The name to use for Mercurial output buffers.") |
126 | 136 |
127 (defvar hg-file-history nil) | 137 (defvar hg-file-history nil) |
128 (defvar hg-rev-history nil) | 138 (defvar hg-rev-history nil) |
139 | |
140 | |
141 ;;; Random constants. | |
142 | |
143 (defconst hg-commit-message-start | |
144 "--- Enter your commit message. Type `C-c C-c' to commit. ---\n") | |
145 | |
146 (defconst hg-commit-message-end | |
147 "--- Files in bold will be committed. Click to toggle selection. ---\n") | |
129 | 148 |
130 | 149 |
131 ;;; hg-mode keymap. | 150 ;;; hg-mode keymap. |
132 | 151 |
133 (defvar hg-prefix-map | 152 (defvar hg-prefix-map |
191 (define-key hg-view-mode-map | 210 (define-key hg-view-mode-map |
192 (if hg-running-xemacs [button2] [mouse-2]) | 211 (if hg-running-xemacs [button2] [mouse-2]) |
193 'hg-buffer-mouse-clicked) | 212 'hg-buffer-mouse-clicked) |
194 | 213 |
195 | 214 |
215 ;;; Commit mode keymaps. | |
216 | |
217 (defvar hg-commit-mode-map (make-sparse-keymap)) | |
218 (define-key hg-commit-mode-map "\C-c\C-c" 'hg-commit-finish) | |
219 (define-key hg-commit-mode-map "\C-c\C-k" 'hg-commit-abort) | |
220 | |
221 (defvar hg-commit-mode-file-map (make-sparse-keymap)) | |
222 (define-key hg-commit-mode-file-map | |
223 (if hg-running-xemacs [button2] [mouse-2]) | |
224 'hg-commit-mouse-clicked) | |
225 (define-key hg-commit-mode-file-map " " 'hg-commit-toggle-file) | |
226 (define-key hg-commit-mode-file-map "\r" 'hg-commit-toggle-file) | |
227 | |
228 | |
196 ;;; Convenience functions. | 229 ;;; Convenience functions. |
197 | 230 |
198 (defsubst hg-binary () | 231 (defsubst hg-binary () |
199 (if hg-binary | 232 (if hg-binary |
200 hg-binary | 233 hg-binary |
208 This function bridges yet another pointless impedance gap between | 241 This function bridges yet another pointless impedance gap between |
209 XEmacs and GNU Emacs." | 242 XEmacs and GNU Emacs." |
210 (if (fboundp 'replace-in-string) | 243 (if (fboundp 'replace-in-string) |
211 (replace-in-string str regexp newtext literal) | 244 (replace-in-string str regexp newtext literal) |
212 (replace-regexp-in-string regexp newtext str nil literal))) | 245 (replace-regexp-in-string regexp newtext str nil literal))) |
246 | |
247 (defsubst hg-strip (str) | |
248 "Strip leading and trailing white space from a string." | |
249 (hg-replace-in-string (hg-replace-in-string str "[ \t\r\n]+$" "") | |
250 "^[ \t\r\n]+" "")) | |
213 | 251 |
214 (defsubst hg-chomp (str) | 252 (defsubst hg-chomp (str) |
215 "Strip trailing newlines from a string." | 253 "Strip trailing newlines from a string." |
216 (hg-replace-in-string str "[\r\n]+$" "")) | 254 (hg-replace-in-string str "[\r\n]+$" "")) |
217 | 255 |
313 nil | 351 nil |
314 nil | 352 nil |
315 'hg-rev-history | 353 'hg-rev-history |
316 (or default "tip"))) | 354 (or default "tip"))) |
317 rev))) | 355 rev))) |
356 | |
357 (defmacro hg-do-across-repo (path &rest body) | |
358 (let ((root-name (gensym "root-")) | |
359 (buf-name (gensym "buf-"))) | |
360 `(let ((,root-name (hg-root ,path))) | |
361 (save-excursion | |
362 (dolist (,buf-name (buffer-list)) | |
363 (set-buffer ,buf-name) | |
364 (when (and hg-status (equal (hg-root buffer-file-name) ,root-name)) | |
365 ,@body)))))) | |
366 | |
367 (put 'hg-do-across-repo 'lisp-indent-function 1) | |
368 | |
318 | 369 |
319 ;;; View mode bits. | 370 ;;; View mode bits. |
320 | 371 |
321 (defun hg-exit-view-mode (buf) | 372 (defun hg-exit-view-mode (buf) |
322 "Exit from hg-view-mode. | 373 "Exit from hg-view-mode. |
531 | 582 |
532 (defun hg-annotate () | 583 (defun hg-annotate () |
533 (interactive) | 584 (interactive) |
534 (error "not implemented")) | 585 (error "not implemented")) |
535 | 586 |
587 (defun hg-commit-toggle-file (pos) | |
588 "Toggle whether or not the file at POS will be committed." | |
589 (interactive "d") | |
590 (save-excursion | |
591 (goto-char pos) | |
592 (let ((face (get-text-property pos 'face)) | |
593 bol) | |
594 (beginning-of-line) | |
595 (setq bol (+ (point) 4)) | |
596 (end-of-line) | |
597 (if (eq face 'bold) | |
598 (progn | |
599 (remove-text-properties bol (point) '(face nil)) | |
600 (message "%s will not be committed" | |
601 (buffer-substring bol (point)))) | |
602 (add-text-properties bol (point) '(face bold)) | |
603 (message "%s will be committed" | |
604 (buffer-substring bol (point))))))) | |
605 | |
606 (defun hg-commit-mouse-clicked (event) | |
607 "Toggle whether or not the file at POS will be committed." | |
608 (interactive "@e") | |
609 (hg-commit-toggle-file (event-point event))) | |
610 | |
611 (defun hg-commit-abort () | |
612 (interactive) | |
613 (error "not implemented")) | |
614 | |
615 (defun hg-commit-finish () | |
616 (interactive) | |
617 (goto-char (point-min)) | |
618 (search-forward hg-commit-message-start) | |
619 (let (message files) | |
620 (let ((start (point))) | |
621 (goto-char (point-max)) | |
622 (search-backward hg-commit-message-end) | |
623 (setq message (hg-strip (buffer-substring start (point))))) | |
624 (when (and (= (length message) 0) | |
625 (not hg-commit-allow-empty-message)) | |
626 (error "Cannot proceed - commit message is empty")) | |
627 (forward-line 1) | |
628 (beginning-of-line) | |
629 (while (< (point) (point-max)) | |
630 (let ((pos (+ (point) 4))) | |
631 (end-of-line) | |
632 (when (eq (get-text-property pos 'face) 'bold) | |
633 (end-of-line) | |
634 (setq files (cons (buffer-substring pos (point)) files)))) | |
635 (forward-line 1)) | |
636 (when (and (= (length files) 0) | |
637 (not hg-commit-allow-empty-file-list)) | |
638 (error "Cannot proceed - no files to commit")) | |
639 (setq message (concat message "\n")) | |
640 (apply 'hg-run0 "--cwd" hg-root "commit" "-m" message files))) | |
641 | |
642 (defun hg-commit-mode () | |
643 "Mode for describing a commit of changes to a Mercurial repository. | |
644 This involves two actions: describing the changes with a commit | |
645 message, and choosing the files to commit. | |
646 | |
647 To describe the commit, simply type some text in the designated area. | |
648 | |
649 By default, all modified, added and removed files are selected for | |
650 committing. Files that will be committed are displayed in bold face\; | |
651 those that will not are displayed in normal face. | |
652 | |
653 To toggle whether a file will be committed, move the cursor over a | |
654 particular file and hit space or return. Alternatively, middle click | |
655 on the file. | |
656 | |
657 When you are finished with preparations, type \\[hg-commit-finish] to | |
658 proceed with the commit." | |
659 (interactive) | |
660 (use-local-map hg-commit-mode-map) | |
661 (set-syntax-table text-mode-syntax-table) | |
662 (setq local-abbrev-table text-mode-abbrev-table | |
663 major-mode 'hg-commit-mode | |
664 mode-name "Hg-Commit") | |
665 (set-buffer-modified-p nil) | |
666 (setq buffer-undo-list nil) | |
667 (run-hooks 'text-mode-hook 'hg-commit-mode-hook)) | |
668 | |
536 (defun hg-commit () | 669 (defun hg-commit () |
537 (interactive) | 670 (interactive) |
538 (error "not implemented")) | 671 (let ((root (hg-root)) |
672 (prev-buffer (current-buffer))) | |
673 (unless root | |
674 (error "Cannot commit outside a repository!")) | |
675 (hg-do-across-repo | |
676 (vc-buffer-sync)) | |
677 (let* ((buf-name (format "*Mercurial: Commit %s*" root))) | |
678 (pop-to-buffer (get-buffer-create buf-name)) | |
679 (when (= (point-min) (point-max)) | |
680 (set (make-local-variable 'hg-root) root) | |
681 (set (make-local-variable 'hg-prev-buffer) prev-buffer) | |
682 (insert "\n") | |
683 (let ((bol (point))) | |
684 (insert hg-commit-message-end) | |
685 (add-text-properties bol (point) '(read-only t face bold-italic))) | |
686 (let ((file-area (point))) | |
687 (insert (hg-chomp (hg-run0 "--cwd" root "status" "-arm"))) | |
688 (goto-char file-area) | |
689 (while (< (point) (point-max)) | |
690 (let ((bol (point))) | |
691 (forward-char 1) | |
692 (insert " ") | |
693 (end-of-line) | |
694 (add-text-properties (+ bol 4) (point) | |
695 '(face bold mouse-face highlight))) | |
696 (forward-line 1)) | |
697 (goto-char file-area) | |
698 (add-text-properties (point) (point-max) | |
699 `(read-only t keymap ,hg-commit-mode-file-map)) | |
700 (goto-char (point-min)) | |
701 (insert hg-commit-message-start) | |
702 (add-text-properties (point-min) (point) | |
703 '(read-only t face bold-italic)) | |
704 (insert "\n\n") | |
705 (forward-line -1) | |
706 (hg-commit-mode)))))) | |
539 | 707 |
540 (defun hg-diff (path &optional rev1 rev2) | 708 (defun hg-diff (path &optional rev1 rev2) |
541 "Show the differences between REV1 and REV2 of PATH. | 709 "Show the differences between REV1 and REV2 of PATH. |
542 When called interactively, the default behaviour is to treat REV1 as | 710 When called interactively, the default behaviour is to treat REV1 as |
543 the tip revision, REV2 as the current edited version of the file, and | 711 the tip revision, REV2 as the current edited version of the file, and |
649 If the path is outside a repository, return nil. | 817 If the path is outside a repository, return nil. |
650 When called interactively, the root is printed. A prefix argument | 818 When called interactively, the root is printed. A prefix argument |
651 prompts for a path to check." | 819 prompts for a path to check." |
652 (interactive (list (hg-read-file-name))) | 820 (interactive (list (hg-read-file-name))) |
653 (let ((root (do ((prev nil dir) | 821 (let ((root (do ((prev nil dir) |
654 (dir (file-name-directory (or path (buffer-file-name))) | 822 (dir (file-name-directory (or path buffer-file-name "")) |
655 (file-name-directory (directory-file-name dir)))) | 823 (file-name-directory (directory-file-name dir)))) |
656 ((equal prev dir)) | 824 ((equal prev dir)) |
657 (when (file-directory-p (concat dir ".hg")) | 825 (when (file-directory-p (concat dir ".hg")) |
658 (return dir))))) | 826 (return dir))))) |
659 (when (interactive-p) | 827 (when (interactive-p) |