Commit 646bd331 authored by Roland McGrath's avatar Roland McGrath
Browse files

*** empty log message ***

parent c958d90f
;;; diff.el --- "DIFF" mode for handling output from unix diff utility.
;;; diff.el --- Run `diff' in compilation-mode.
;; Copyright (C) 1990 Free Software Foundation, Inc.
;; Copyright (C) 1992 Free Software Foundation, Inc.
;; Author: Frank P. Bresz <fpb@ittc.wec.com>
;; Maintainer: FSF
;; Created: 27 Jan 1989
;; Keyword: unix, tools
;; This file is part of GNU Emacs.
......@@ -23,84 +20,180 @@
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Commentary:
;; todo: diff-switches flexibility:
;; (defconst diff-switches-function
;; '(lambda (file)
;; (if (string-match "\\.el$" file)
;; "-c -F\"^(\""
;; "-p"))
;; "Function to return switches to pass to the `diff' utility, in \\[diff].
;; This function is called with one arg, a file name, and returns a string
;; containing 0 or more arguments which are passed on to `diff'.
;; NOTE: This is not an ordinary hook; it may not be a list of functions.")
;; - fpb@ittc.wec.com - Sep 25, 1990
;; Added code to support sccs diffing.
;; also fixed one minor glitch in the
;; search for the pattern. If you only 1 addition you won't find the end
;; of the pattern (minor)
;;; Code:
(defvar diff-switches nil
"*A list of switches to pass to the diff program.")
(defvar diff-search-pattern "^\\([0-9]\\|\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*\\)"
"Regular expression that delineates difference regions in diffs.")
(defvar diff-rcs-extension ",v"
"*Extension to find RCS file, some systems do not use ,v")
(require 'compile)
;; Initialize the keymap if it isn't already
(if (boundp 'diff-mode-map)
nil
(setq diff-mode-map (make-keymap))
(suppress-keymap diff-mode-map)
(define-key diff-mode-map "?" 'describe-mode)
(define-key diff-mode-map "." 'diff-beginning-of-diff)
(define-key diff-mode-map " " 'scroll-up)
(define-key diff-mode-map "\177" 'scroll-down)
(define-key diff-mode-map "n" 'diff-next-difference)
(define-key diff-mode-map "p" 'diff-previous-difference)
(define-key diff-mode-map "j" 'diff-show-difference))
(defvar diff-switches nil
"*A string or list of strings specifying switches to be be passed to diff.")
(defvar diff-regexp-alist
'(
;; -u format: @@ -OLDSTART,OLDEND +NEWSTART,NEWEND @@
("^@@ -\\([0-9]+\\),[0-9]+ \\+\\([0-9]+\\),[0-9]+ @@$" 1 2)
;; -c format: *** OLDSTART,OLDEND ****
("^\\*\\*\\* \\([0-9]+\\),[0-9]+ \\*\\*\\*\\*$" 1 nil)
;; --- NEWSTART,NEWEND ----
("^--- \\([0-9]+\\),[0-9]+ ----$" nil 1)
;; plain diff format: OLDSTART[,OLDEND]{a,d,c}NEWSTART[,NEWEND]
("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]\\([0-9]+\\)\\(,[0-9]+\\)?$" 1 3)
;; -e (ed) format: OLDSTART[,OLDEND]{a,d,c}
("^\\([0-9]+\\)\\(,[0-9]+\\)?[adc]$" 1)
;; -f format: {a,d,c}OLDSTART[ OLDEND]
;; -n format: {a,d,c}OLDSTART LINES-CHANGED
("^[adc]\\([0-9]+\\)\\( [0-9]+\\)?$" 1)
)
"Alist (REGEXP OLD-IDX NEW-IDX) of regular expressions to match difference
sections in \\[diff] output. If REGEXP matches, the OLD-IDX'th
subexpression gives the line number in the old file, and NEW-IDX'th
subexpression gives the line number in the new file. If OLD-IDX or NEW-IDX
is nil, REGEXP matches only half a section.")
;; See compilation-parse-errors-function (compile.el).
(defun diff-parse-differences (limit-search)
(setq compilation-error-list nil)
(message "Parsing differences...")
;; Don't reparse diffs already seen at last parse.
(goto-char compilation-parsing-end)
;; Construct in REGEXP a regexp composed of all those in dired-regexp-alist.
(let ((regexp (mapconcat (lambda (elt)
(concat "\\(" (car elt) "\\)"))
diff-regexp-alist
"\\|"))
;; (GROUP-IDX OLD-IDX NEW-IDX)
(groups (let ((subexpr 1))
(mapcar (lambda (elt)
(prog1
(cons subexpr
(mapcar (lambda (n)
(and n
(+ subexpr n)))
(cdr elt)))
(setq subexpr (+ subexpr 1
(count-regexp-groupings
(car elt))))))
diff-regexp-alist)))
(new-error
(function (lambda (file subexpr)
(setq compilation-error-list
(cons
(cons (set-marker (make-marker)
(match-beginning subexpr)
(current-buffer))
(let ((line (string-to-int
(buffer-substring
(match-beginning subexpr)
(match-end subexpr)))))
(save-excursion
(set-buffer (find-file-noselect file))
(save-excursion
(goto-line line)
(point-marker)))))
compilation-error-list)))))
(found-desired nil)
g)
(while (and (not found-desired)
;; We don't just pass LIMIT-SEARCH to re-search-forward
;; because we want to find matches containing LIMIT-SEARCH
;; but which extend past it.
(re-search-forward regexp nil t))
;; Find which individual regexp matched.
(setq g groups)
(while (and g (null (match-beginning (car (car g)))))
(setq g (cdr g)))
(setq g (car g))
(if (nth 1 g) ;OLD-IDX
(funcall new-error diff-old-file (nth 1 g)))
(if (nth 2 g) ;NEW-IDX
(funcall new-error diff-new-file (nth 2 g)))
(and limit-search (>= (point) limit-search)
;; The user wanted a specific diff, and we're past it.
(setq found-desired t)))
(if found-desired
(setq compilation-parsing-end (point))
;; Set to point-max, not point, so we don't perpetually
;; parse the last bit of text when it isn't a diff header.
(setq compilation-parsing-end (point-max))
(message "Parsing differences...done")))
(setq compilation-error-list (nreverse compilation-error-list)))
;;;###autoload
(defun diff (old new)
(defun diff (old new &optional switches)
"Find and display the differences between OLD and NEW files.
Interactively the current buffer's file name is the default for for NEW
and a backup file for NEW is the default for OLD."
and a backup file for NEW is the default for OLD.
With prefix arg, prompt for diff switches."
(interactive
(let (oldf newf)
(reverse
(list
(setq newf (buffer-file-name)
newf (if (and newf (file-exists-p newf))
(read-file-name
(concat "Diff new file: ("
(file-name-nondirectory newf) ") ")
nil newf t)
(read-file-name "Diff new file: " nil nil t)))
(setq oldf (file-newest-backup newf)
oldf (if (and oldf (file-exists-p oldf))
(read-file-name
(concat "Diff original file: ("
(file-name-nondirectory oldf) ") ")
(file-name-directory oldf) oldf t)
(read-file-name "Diff original file: "
(file-name-directory newf) nil t)))))))
(nconc
(let (oldf newf)
(nreverse
(list
(setq newf (buffer-file-name)
newf (if (and newf (file-exists-p newf))
(read-file-name
(concat "Diff new file: ("
(file-name-nondirectory newf) ") ")
nil newf t)
(read-file-name "Diff new file: " nil nil t)))
(setq oldf (file-newest-backup newf)
oldf (if (and oldf (file-exists-p oldf))
(read-file-name
(concat "Diff original file: ("
(file-name-nondirectory oldf) ") ")
(file-name-directory oldf) oldf t)
(read-file-name "Diff original file: "
(file-name-directory newf) nil t))))))
(if current-prefix-arg
(list (read-string "Diff switches: "
(if (stringp diff-switches)
diff-switches
(mapconcat 'identity diff-switches " "))))
nil)))
(message "Comparing files %s %s..." new old)
(setq new (expand-file-name new)
old (expand-file-name old))
(diff-internal-diff "diff" (append diff-switches (list new old)) nil))
(let ((buf (compile-internal (mapconcat 'identity
(append '("diff")
(if (consp diff-switches)
diff-switches
(list diff-switches))
(list old)
(list new))
" ")
"No more differences" "Diff"
'diff-parse-differences)))
(save-excursion
(set-buffer buf)
(set (make-local-variable 'diff-old-file) old)
(set (make-local-variable 'diff-new-file) new))
buf))
(defun diff-backup (file)
;;;###autoload
(defun diff-backup (file &optional switches)
"Diff this file with its backup file or vice versa.
Uses the latest backup, if there are several numerical backups.
If this file is a backup, diff it with its original.
The backup file is the first file given to `diff'."
(interactive "fDiff (file with backup): ")
(interactive (list (read-file-name "Diff (file with backup): ")
(if current-prefix-arg
(read-string "Diff switches: "
(if (stringp diff-switches)
diff-switches
(mapconcat 'identity
diff-switches " ")))
nil)))
(let (bak ori)
(if (backup-file-name-p file)
(setq bak file
......@@ -108,7 +201,7 @@ The backup file is the first file given to `diff'."
(setq bak (or (diff-latest-backup-file file)
(error "No backup found for %s" file))
ori file))
(diff bak ori)))
(diff bak ori switches)))
(defun diff-latest-backup-file (fn) ; actually belongs into files.el
"Return the latest existing backup of FILE, or nil."
......@@ -133,138 +226,4 @@ The backup file is the first file given to `diff'."
(> (backup-extract-version fn1)
(backup-extract-version fn2))))))))))
(defun diff-internal-diff (diff-command sw strip)
(let ((buffer-read-only nil))
(with-output-to-temp-buffer "*Diff Output*"
(buffer-disable-undo standard-output)
(save-excursion
(set-buffer standard-output)
(erase-buffer)
(apply 'call-process diff-command nil t nil sw)))
(set-buffer "*Diff Output*")
(goto-char (point-min))
(while sw
(if (string= (car sw) "-c")
;; strip leading filenames from context diffs
(progn (forward-line 2) (delete-region (point-min) (point))))
(if (and (string= (car sw) "-C") (string= "sccs" diff-command))
;; strip stuff from SCCS context diffs
(progn (forward-line 2) (delete-region (point-min) (point))))
(setq sw (cdr sw)))
(if strip
;; strip stuff from SCCS context diffs
(progn (forward-line strip) (delete-region (point-min) (point)))))
(diff-mode)
(if (string= "0" diff-total-differences)
(let ((buffer-read-only nil))
(insert (message "There are no differences.")))
(narrow-to-region (point) (progn
(forward-line 1)
(if (re-search-forward diff-search-pattern
nil t)
(goto-char (match-beginning 0))
(goto-char (point-max)))))
(setq diff-current-difference "1")))
;; Take a buffer full of Unix diff output and go into a mode to easily
;; see the next and previous difference
(defun diff-mode ()
"Diff Mode is used by \\[diff] for perusing the output from the diff program.
All normal editing commands are turned off. Instead, these are available:
\\<diff-mode-map>
\\[diff-beginning-of-diff] Move point to start of this difference.
\\[scroll-up] Scroll to next screen of this difference.
\\[scroll-down] Scroll to previous screen of this difference.
\\[diff-next-difference] Move to Next Difference.
\\[diff-previous-difference] Move to Previous Difference.
\\[diff-show-difference] Jump to difference specified by numeric position.
"
(interactive)
(use-local-map diff-mode-map)
(setq buffer-read-only t
major-mode 'diff-mode
mode-name "Diff"
mode-line-modified "--- "
mode-line-process
'(" " diff-current-difference "/" diff-total-differences))
(make-local-variable 'diff-current-difference)
(set (make-local-variable 'diff-total-differences)
(int-to-string (diff-count-differences))))
(defun diff-next-difference (n)
"Go to the beginning of the next difference.
Differences are delimited by `diff-search-pattern'."
(interactive "p")
(if (< n 0) (diff-previous-difference (- n))
(if (zerop n) ()
(goto-char (point-min))
(forward-line 1) ; to get past the match for the start of this diff
(widen)
(if (re-search-forward diff-search-pattern nil 'move n)
(let ((start (goto-char (match-beginning 0))))
(forward-line 1)
(if (re-search-forward diff-search-pattern nil 'move)
(goto-char (match-beginning 0)))
(narrow-to-region start (point))
(setq diff-current-difference
(int-to-string (+ n (string-to-int
diff-current-difference)))))
(re-search-backward diff-search-pattern nil)
(narrow-to-region (point) (point-max))
(message "No following differences.")
(setq diff-current-difference diff-total-differences))
(goto-char (point-min)))))
(defun diff-previous-difference (n)
"Go the the beginning of the previous difference.
Differences are delimited by `diff-search-pattern'."
(interactive "p")
(if (< n 0) (diff-next-difference (- n))
(if (zerop n) ()
(goto-char (point-min))
(widen)
(if (re-search-backward diff-search-pattern nil 'move n)
(setq diff-current-difference
(int-to-string (- (string-to-int diff-current-difference) n)))
(message "No previous differences.")
(setq diff-current-difference "1"))
(narrow-to-region (point) (progn
(forward-line 1)
(re-search-forward diff-search-pattern nil)
(goto-char (match-beginning 0))))
(goto-char (point-min)))))
(defun diff-show-difference (n)
"Show difference number N (prefix argument)."
(interactive "p")
(let ((cur (string-to-int diff-current-difference)))
(cond ((or (= n cur)
(zerop n)
(not (natnump n))) ; should signal an error perhaps.
;; just redisplay.
(goto-char (point-min)))
((< n cur)
(diff-previous-difference (- cur n)))
((> n cur)
(diff-next-difference (- n cur))))))
(defun diff-beginning-of-diff ()
"Go to beginning of current difference."
(interactive)
(goto-char (point-min)))
;; This function counts up the number of differences in the buffer.
(defun diff-count-differences ()
"Count number of differences in the current buffer."
(message "Counting differences...")
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(let ((cnt 0))
(while (re-search-forward diff-search-pattern nil t)
(setq cnt (1+ cnt)))
(message "Counting differences...done (%d)" cnt)
cnt))))
;;; diff.el ends here
......@@ -6,8 +6,6 @@
;; Maintainer: FSF
;; Keyword: tools, processes
;;;!!! dup removal is broken.
;; This file is part of GNU Emacs.
;; GNU Emacs is free software; you can redistribute it and/or modify
......@@ -36,12 +34,12 @@
(defvar compilation-error-list nil
"List of error message descriptors for visiting erring functions.
Each error descriptor is a cons (or nil).
Its car is a marker pointing to an error message.
If its cdr is a marker, it points to the text of the line the message is about.
If its cdr is a cons, that cons's car is a cons (DIRECTORY . FILE), specifying
file the message is about, and its cdr is the number of the line the message
is about. Or its cdr may be nil if that error is not interesting.
Each error descriptor is a cons (or nil). Its car is a marker pointing to
an error message. If its cdr is a marker, it points to the text of the
line the message is about. If its cdr is a cons, that cons's car is a cons
\(DIRECTORY . FILE\), specifying the file the message is about, and its cdr
is the number of the line the message is about. Or its cdr may be nil if
that error is not interesting.
The value may be t instead of a list; this means that the buffer of
error messages should be reparsed the next time the list of errors is wanted.")
......@@ -219,7 +217,9 @@ arg REGEXP-ALIST is the error message regexp alist to use (nil means the
default). Sixth arg NAME-FUNCTION is a function called to name the buffer (nil
means the default). The defaults for these variables are the global values of
\`compilation-parse-errors-function', `compilation-error-regexp-alist', and
\`compilation-buffer-name-function', respectively."
\`compilation-buffer-name-function', respectively.
Returns the compilation buffer created."
(let (outbuf)
(save-excursion
(or name-of-mode
......@@ -303,6 +303,10 @@ means the default). The defaults for these variables are the global values of
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-c" 'compile-goto-error)
(define-key map "\C-c\C-k" 'kill-compilation)
(define-key map " " 'scroll-up)
(define-key map "\^?" 'scroll-down)
(define-key map "\M-n" 'compilation-next-error)
(define-key map "\M-p" 'compilation-previous-error)
map)
"Keymap for compilation log buffers.")
......@@ -374,6 +378,67 @@ Runs `compilation-mode-hook' with `run-hooks' (which see)."
(setq compilation-in-progress (delq proc compilation-in-progress))
))))
(defun compilation-next-error (n)
"Move point to the next error in the compilation buffer.
Does NOT find the source line like \\[next-error]."
(interactive "p")
(or (compilation-buffer-p (current-buffer))
(error "Not in a compilation buffer."))
(setq compilation-last-buffer (current-buffer))
(let ((p (point))
(errors nil)
(first t))
(save-excursion ;save point in case of later error
(while (and (if (< n 0)
(null errors)
(< (length errors) n))
(or first (< compilation-parsing-end (point-max))))
(setq first nil)
(if (< compilation-parsing-end (point-max))
(progn
;; Move forward a bit and parse.
;; Hopefully we will parse enough to find the one we want.
(forward-line n)
(compile-reinitialize-errors nil (point))))
(setq errors compilation-old-error-list)
;; Look for the error containing P (the original point).
(if (< n 0)
(while (and errors
(> p (car (car errors))))
(setq errors (cdr errors)))
(while (and errors
(>= p (car (car errors))))
(setq errors (cdr errors))))
(ignore))
;; Move to the error after the one containing point.
(setq p (car (if (< n 0)
(let ((i 0)
(e compilation-old-error-list))
;; See how many cdrs away ERRORS is from the start.
(while (not (eq e errors))
(setq i (1+ i)
e (cdr e)))
(if (> (- n) i)
(error "Moved back past first error")
(nth (+ i n) compilation-old-error-list)))
(if errors
(nth (1- n) errors)
(error "Moved past last error"))))))
(goto-char p)))
(defun compilation-previous-error (n)
"Move point to the previous error in the compilation buffer.
Does NOT find the source line like \\[next-error]."
(interactive "p")
(compilation-next-error (- n)))
(defun kill-compilation ()
"Kill the process made by the \\[compile] command."
(interactive)
......@@ -394,7 +459,7 @@ Runs `compilation-mode-hook' with `run-hooks' (which see)."
(consp argp))
(progn (compilation-forget-errors)
(setq compilation-parsing-end 1)))
(if compilation-error-list
(if (and compilation-error-list (not limit-search))
;; Since compilation-error-list is non-nil, it points to a specific
;; error the user wanted. So don't move it around.
nil
......@@ -419,24 +484,23 @@ other kinds of prefix arguments are ignored."
(error "Not in a compilation buffer."))
(setq compilation-last-buffer (current-buffer))
(compile-reinitialize-errors argp (point))
(save-excursion
(beginning-of-line)
;; Move compilation-error-list to the elt of
;; compilation-old-error-list whose car is the error we want.
(setq compilation-error-list
(memq (let (elt)
(while (not (or (setq elt (assoc (point-marker)
compilation-old-error-list))
(eobp)))
;; This line doesn't contain an error.
;; Move forward a line and look again.
(forward-line 1))
elt)
compilation-old-error-list)))
;; Move compilation-error-list to the elt of compilation-old-error-list
;; whose cadr is the error we want.
(setq compilation-error-list compilation-old-error-list)
(while (and (cdr compilation-error-list)
(> (point) (car (car (cdr compilation-error-list)))))
(setq compilation-error-list (cdr compilation-error-list)))
;; Move to another window, so that next-error's window changes
;; result in the desired setup.
(or (one-window-p)
(other-window -1))
(progn
(other-window -1)
;; other-window changed the selected buffer,
;; but we didn't want to do that.
(set-buffer compilation-last-buffer)))
(next-error 1))
(defun compilation-buffer-p (buffer)
......@@ -505,11 +569,10 @@ See variables `compilation-parse-errors-function' and
(let (next-errors next-error)
(save-excursion
(set-buffer compilation-last-buffer)
(setq next-errors (nthcdr (+ (- (length compilation-old-error-list)
(length compilation-error-list)
1)
(prefix-numeric-value argp))
compilation-old-error-list)
;; This code used to do something bizarre and incomprehensible.
;; Was there a reason I wrote it like that? --roland
(setq next-errors (nthcdr (prefix-numeric-value argp)
compilation-error-list)
next-error (car next-errors))
(while
(progn
......@@ -650,7 +713,7 @@ See variables `compilation-parse-errors-function' and
;; Set compilation-error-list to nil, and unchain the markers that point to the
;; error messages and their text, so that they no longer slow down gap motion.
;; This would happen anyway at the next garbage collection, but it is better to
;; do it the right away.
;; do it right away.
(defun compilation-forget-errors ()
(while compilation-old-error-list
(let ((next-error (car compilation-old-error-list)))
......@@ -741,11 +804,11 @@ See variable `compilation-parse-errors-function' for the interface it uses."
(setq subexpr (+ subexpr 1 (count-regexp-groupings (car (car alist)))))
(setq alist (cdr alist)))
(while (and (re-search-forward regexp nil t)
(while (and (not found-desired)
;; We don't just pass LIMIT-SEARCH to re-search-forward
;; because we want to find matches containing LIMIT-SEARCH
;; but which extend past it.
(not found-desired))
(re-search-forward regexp nil t))
;; Figure out which constituent regexp matched.
(cond ((match-beginning enter-group)
;; The match was the enter-directory regexp.
......@@ -813,16 +876,13 @@ See variable `compilation-parse-errors-function' for the interface it uses."
(cons filename linenum))
compilation-error-list)))))
(t
(error "Impossible regexp match!")))
(error "compilation-parse-errors: impossible regexp match!")))
(and limit-search (>= (point) limit-search)
;; The user wanted a specific error, and we're past it.
(setq found-desired t)))
(if desired-found
(progn
(setq compilation-parsing-end (point))
(message "Desired error message found."))
;; Set to point-max, not point, so we don't perpetually
;; parse the last bit of text when it isn't an error message.
(if found-desired
(setq compilation-parsing-end (point))
;; We have searched the whole buffer.
(setq compilation-parsing-end (point-max))
(message "Parsing error messages...done")))
(setq compilation-error-list (nreverse compilation-error-list)))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment