;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
:type 'boolean
:group 'diff-mode)
-(defcustom diff-auto-refine t
- "Automatically highlight changes in detail as the user visits hunks."
- :type 'boolean
- :group 'diff-mode)
-
(defcustom diff-mode-hook nil
"Run after setting up the `diff-mode' major mode."
:type 'hook
`((,diff-minor-mode-prefix . ,diff-mode-shared-map))
"Keymap for `diff-minor-mode'. See also `diff-mode-shared-map'.")
+(define-minor-mode diff-auto-refine-mode
+ "Automatically highlight changes in detail as the user visits hunks.
+When transitioning from disabled to enabled,
+try to refine the current hunk, as well."
+ :group 'diff-mode :init-value t :lighter nil ;; " Auto-Refine"
+ (when diff-auto-refine-mode
+ (condition-case-no-debug nil (diff-refine-hunk) (error nil))))
;;;;
;;;; font-lock support
(defface diff-header
'((((class color) (min-colors 88) (background light))
- :background "grey85")
+ :background "grey80")
(((class color) (min-colors 88) (background dark))
:background "grey45")
(((class color) (background light))
(replace-match "" t t)))))))
(defconst diff-hunk-header-re-unified
- "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\) @@")
+ "^@@ -\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? \\+\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? @@")
+(defconst diff-context-mid-hunk-header-re
+ "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$")
(defvar diff-font-lock-keywords
`((,(concat "\\(" diff-hunk-header-re-unified "\\)\\(.*\\)$")
("^\\(\\*\\{15\\}\\)\\(.*\\)$" ;context
(1 diff-hunk-header-face) (2 diff-function-face))
("^\\*\\*\\* .+ \\*\\*\\*\\*". diff-hunk-header-face) ;context
- ("^--- .+ ----$" . diff-hunk-header-face) ;context
+ (,diff-context-mid-hunk-header-re . diff-hunk-header-face) ;context
("^[0-9,]+[acd][0-9,]+$" . diff-hunk-header-face) ;normal
("^---$" . diff-hunk-header-face) ;normal
;; For file headers, accept files with spaces, but be careful to rule
(defconst diff-hunk-header-re
(concat "^\\(?:" diff-hunk-header-re-unified ".*\\|\\*\\{15\\}.*\n\\*\\*\\* .+ \\*\\*\\*\\*\\|[0-9]+\\(,[0-9]+\\)?[acd][0-9]+\\(,[0-9]+\\)?\\)$"))
-(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* ]\\).+\n" (substring diff-hunk-header-re 1)))
+(defconst diff-file-header-re (concat "^\\(--- .+\n\\+\\+\\+ \\|\\*\\*\\* .+\n--- \\|[^-+!<>0-9@* \n]\\).+\n" (substring diff-hunk-header-re 1)))
(defvar diff-narrowed-to nil)
(defun diff-hunk-style (&optional style)
(setq style (diff-hunk-style style))
(goto-char (match-end 0))
(when (and (not donttrustheader) (match-end 2))
+ (let* ((nold (string-to-number (or (match-string 2) "1")))
+ (nnew (string-to-number (or (match-string 4) "1")))
+ (endold
(save-excursion
(re-search-forward (if diff-valid-unified-empty-line
"^[- \n]" "^[- ]")
- nil t
- (string-to-number (match-string 2)))
- (setq end (line-beginning-position 2)))))
+ nil t nold)
+ (line-beginning-position 2)))
+ (endnew
+ ;; The hunk may end with a bunch of "+" lines, so the `end' is
+ ;; then further than computed above.
+ (save-excursion
+ (re-search-forward (if diff-valid-unified-empty-line
+ "^[+ \n]" "^[+ ]")
+ nil t nnew)
+ (line-beginning-position 2))))
+ (setq end (max endold endnew)))))
;; We may have a first evaluation of `end' thanks to the hunk header.
(unless end
(setq end (and (re-search-forward
;; Define diff-{hunk,file}-{prev,next}
(easy-mmode-define-navigation
diff-hunk diff-hunk-header-re "hunk" diff-end-of-hunk diff-restrict-view
- (if diff-auto-refine
+ (if diff-auto-refine-mode
(condition-case-no-debug nil (diff-refine-hunk) (error nil))))
(easy-mmode-define-navigation
(diff-end-of-hunk)
(kill-region start (point)))))
-(defconst diff-file-junk-re "diff \\|index ") ; "index " is output by git-diff.
+;; "index ", "old mode", "new mode", "new file mode" and
+;; "deleted file mode" are output by git-diff.
+(defconst diff-file-junk-re
+ "diff \\|index \\|\\(?:deleted file\\|new\\(?: file\\)?\\|old\\) mode")
(defun diff-beginning-of-file-and-junk ()
"Go to the beginning of file-related diff-info.
;;;;
(defvar diff-remembered-files-alist nil)
+(defvar diff-remembered-defdir nil)
(defun diff-filename-drop-dir (file)
(when (string-match "/" file) (substring file (match-end 0))))
Non-nil BATCH means to prefer returning an incorrect answer than to prompt
the user.
PREFIX is only used internally: don't use it."
+ (unless (equal diff-remembered-defdir default-directory)
+ ;; Flush diff-remembered-files-alist if the default-directory is changed.
+ (set (make-local-variable 'diff-remembered-defdir) default-directory)
+ (set (make-local-variable 'diff-remembered-files-alist) nil))
(save-excursion
(unless (looking-at diff-file-header-re)
(or (ignore-errors (diff-beginning-of-file))
(replace-match "***" t t nil 2))
;; we matched a hunk header
(let ((line1 (match-string 4))
- (lines1 (if (match-end 5)
- (string-to-number (match-string 5)) 1))
+ (lines1 (or (match-string 5) "1"))
(line2 (match-string 6))
- (lines2 (if (match-end 7)
- (string-to-number (match-string 7)) 1))
+ (lines2 (or (match-string 7) "1"))
;; Variables to use the special undo function.
(old-undo buffer-undo-list)
(old-end (marker-position end))
(replace-match
(concat "***************\n*** " line1 ","
(number-to-string (+ (string-to-number line1)
- lines1 -1)) " ****"))
+ (string-to-number lines1)
+ -1))
+ " ****"))
(save-restriction
(narrow-to-region (line-beginning-position 2)
;; Call diff-end-of-hunk from just before
(save-excursion
(insert "--- " line2 ","
(number-to-string (+ (string-to-number line2)
- lines2 -1))
+ (string-to-number lines2)
+ -1))
" ----\n" hunk))
;;(goto-char (point-min))
(forward-line 1)
(reversible t))
(replace-match "")
(unless (re-search-forward
- "^--- \\([0-9]+\\),\\(-?[0-9]+\\) ----$" nil t)
+ diff-context-mid-hunk-header-re nil t)
(error "Can't find matching `--- n1,n2 ----' line"))
(let ((line2s (match-string 1))
(line2e (match-string 2))
(when (= (char-after) ?-) (delete-char 1) (insert "+"))
(forward-line 1))
(let ((half1 (delete-and-extract-region half1s (point))))
- (unless (looking-at "^--- \\([0-9]+,-?[0-9]+\\) ----$")
+ (unless (looking-at diff-context-mid-hunk-header-re)
(insert half1)
(error "Can't find matching `--- n1,n2 ----' line"))
- (let ((str1 (match-string 1)))
- (replace-match lines1 nil nil nil 1)
+ (let* ((str1end (or (match-end 2) (match-end 1)))
+ (str1 (buffer-substring (match-beginning 1) str1end)))
+ (goto-char str1end)
+ (insert lines1)
+ (delete-region (match-beginning 1) str1end)
(forward-line 1)
(let ((half2s (point)))
(while (looking-at "[!+ \\][ \t]\\|#")
(if old1
(unless (string= new1 old1) (replace-match new1 t t nil 2))
(goto-char (match-end 2)) (insert "," new1))))
- ((looking-at "--- \\([0-9]+\\),\\([0-9]*\\) ----$")
+ ((looking-at diff-context-mid-hunk-header-re)
(when (> (+ space bang plus) 0)
(let* ((old1 (match-string 1))
(old2 (match-string 2))
(goto-char (car diff-unhandled-changes))
;; Maybe we've cut the end of the hunk before point.
(if (and (bolp) (not (bobp))) (backward-char 1))
- ;; We used to fixup modifs on all the changes, but it turns out
- ;; that it's safer not to do it on big changes, for example
- ;; when yanking a big diff, since we might then screw up perfectly
- ;; correct values. -stef
- ;; (unless (ignore-errors
- ;; (diff-beginning-of-hunk)
- ;; (save-excursion
- ;; (diff-end-of-hunk)
- ;; (> (point) (car diff-unhandled-changes))))
- ;; (goto-char (car diff-unhandled-changes))
- ;; (re-search-forward diff-hunk-header-re (cdr diff-unhandled-changes))
- ;; (diff-beginning-of-hunk))
- ;; (diff-fixup-modifs (point) (cdr diff-unhandled-changes))
+ ;; We used to fixup modifs on all the changes, but it turns out that
+ ;; it's safer not to do it on big changes, e.g. when yanking a big
+ ;; diff, or when the user edits the header, since we might then
+ ;; screw up perfectly correct values. --Stef
(diff-beginning-of-hunk)
- (when (save-excursion
+ (let* ((style (if (looking-at "\\*\\*\\*") 'context))
+ (start (line-beginning-position (if (eq style 'context) 3 2)))
+ (mid (if (eq style 'context)
+ (save-excursion
+ (re-search-forward diff-context-mid-hunk-header-re
+ nil t)))))
+ (when (and ;; Don't try to fixup changes in the hunk header.
+ (> (car diff-unhandled-changes) start)
+ ;; Don't try to fixup changes in the mid-hunk header either.
+ (or (not mid)
+ (< (cdr diff-unhandled-changes) (match-beginning 0))
+ (> (car diff-unhandled-changes) (match-end 0)))
+ (save-excursion
(diff-end-of-hunk nil 'donttrustheader)
- (>= (point) (cdr diff-unhandled-changes)))
+ ;; Don't try to fixup changes past the end of the hunk.
+ (>= (point) (cdr diff-unhandled-changes))))
(diff-fixup-modifs (point) (cdr diff-unhandled-changes)))))
- (setq diff-unhandled-changes nil)))
+ (setq diff-unhandled-changes nil))))
(defun diff-next-error (arg reset)
;; Select a window that displays the current buffer so that point
;; compile support
(set (make-local-variable 'next-error-function) 'diff-next-error)
+ (set (make-local-variable 'beginning-of-defun-function)
+ 'diff-beginning-of-file-and-junk)
+ (set (make-local-variable 'end-of-defun-function)
+ 'diff-end-of-file)
+
(setq buffer-read-only diff-default-read-only)
;; setup change hooks
(if (not diff-update-on-the-fly)
(1+ (- (string-to-number (match-string 2))
(string-to-number (match-string 1))))
1))
- (if (not (looking-at "--- \\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)? ----$"))
+ (if (not (looking-at diff-context-mid-hunk-header-re))
(error "Unrecognized context diff second hunk header format")
(forward-line)
(diff-sanity-check-context-hunk-half
((eq (char-after) ?@)
(if (not (looking-at diff-hunk-header-re-unified))
(error "Unrecognized unified diff hunk header format")
- (let ((before (if (match-end 2)
- (string-to-number (match-string 2)) 1))
- (after (if (match-end 4)
- (string-to-number (match-string 4)) 1)))
+ (let ((before (string-to-number (or (match-string 2) "1")))
+ (after (string-to-number (or (match-string 4) "1"))))
(forward-line)
(while
(case (char-after)
;; context diff
(forward-line 2)
(setq src-pos (point))
- (re-search-forward "^--- " nil t)
+ (re-search-forward diff-context-mid-hunk-header-re nil t)
(forward-line 0)
(setq divider-pos (point))
(forward-line 1)
(error "Can't find the hunk header")
(if other (match-string 1)
(if (match-end 3) (match-string 3)
- (unless (re-search-forward "^--- \\([0-9,]+\\)" nil t)
+ (unless (re-search-forward
+ diff-context-mid-hunk-header-re nil t)
(error "Can't find the hunk separator"))
(match-string 1)))))
(file (or (diff-find-file-name other) (error "Can't find the file")))
(let ((old (if switched dst src)))
(with-temp-buffer
(insert (car old))
- (funcall (with-current-buffer buf major-mode))
+ (funcall (buffer-local-value 'major-mode buf))
(goto-char (+ (point-min) (cdr old)))
(add-log-current-defun))))
(with-current-buffer buf
(defface diff-refine-change
'((((class color) (min-colors 88) (background light))
- :background "grey90")
+ :background "grey85")
(((class color) (min-colors 88) (background dark))
- :background "grey40")
+ :background "grey60")
(((class color) (background light))
:background "yellow")
(((class color) (background dark))
"\\( .*\n\\)*[+]\\)?")
nil t))
(save-excursion
- (add-change-log-entry nil nil t t)))
+ (add-change-log-entry nil nil t nil t)))
;; When there's no more hunks, diff-hunk-next signals an error.
(error nil)))))
+(defun diff-show-trailing-whitespaces ()
+ "Show trailing whitespaces in modified lines for diff-mode."
+ (interactive)
+ (let ((whitespace-style '(trailing))
+ (whitespace-trailing-regexp "^[-\+!<>].*?\\([\t ]+\\)$"))
+ (whitespace-mode 1))) ; display trailing blanks in diff buffer
+
;; provide the package
(provide 'diff-mode)