Package: emacs;
Reported by: Kevin Ryde <user42 <at> zip.com.au>
Date: Fri, 10 Dec 2010 22:36:02 UTC
Severity: normal
Found in version 23.2
Done: Glenn Morris <rgm <at> gnu.org>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: Glenn Morris <rgm <at> gnu.org> To: Kevin Ryde <user42 <at> zip.com.au> Cc: 7611 <at> debbugs.gnu.org Subject: bug#7611: 23.2; info-xref.el v.3 Date: Tue, 14 Dec 2010 13:39:13 -0500
Kevin Ryde wrote: >> Can you resend this as a patch > > Alas, the bazaar repo is hugely too big for me to use (both initial > 10-year download and subsequent 10-meg updates). Well, there is http://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/info-xref.el but at that speed, it will take you ~ 10 hours to load the page. :( > * info-xref.el: Use compilation-mode for the errors. > Move explanations from commentary to relevant docstrings. > (info-xref-docstrings): New command. This doesn't adequately describe the diff (below) between your version and the version in the Emacs trunk. Also the new version uses a cl-function (remove-if) at run-time. *** info-xref.el 2010-12-06 13:38:37.000000000 -0800 --- ../info-xref.el 2010-12-13 14:26:59.000000000 -0800 *************** *** 5,10 **** --- 5,11 ---- ;; Author: Kevin Ryde <user42 <at> zip.com.au> ;; Keywords: docs + ;; Version: 3 ;; This file is part of GNU Emacs. *************** *** 23,78 **** ;;; Commentary: ! ;; This file implements some simple checking of external cross references in ! ;; info files, by attempting to visit the nodes specified. ;; ! ;; "makeinfo" checks references internal to a document, but not external ! ;; references, which makes it rather easy for mistakes to creep in or node ! ;; name changes to go unnoticed. `Info-validate' doesn't check external ! ;; references either. ! ;; ! ;; `M-x info-xref-check' checks one file. When invoked from an Info-mode or ! ;; texinfo-mode buffer, the current info file is the default at the prompt. ! ;; ! ;; `M-x info-xref-check-all' looks at everything in the normal info path. ! ;; This might be a lot of files but it's a good way to check the consistency ! ;; of the whole system. ! ;; ! ;; Results are shown in a buffer. The format is a bit rough, but hopefully ! ;; there won't be too many problems normally, and correcting them is a ! ;; manual process anyway, a case of finding the right spot in the original ! ;; .texi and finding what node it ought to point to. ! ;; ! ;; When a target info file doesn't exist there's clearly no way to validate ! ;; node references within it. A message is given for missing target files ! ;; (once per source document), it could be simply that the target hasn't ! ;; been installed, or it could be a mistake in the reference. ! ;; ! ;; Indirect info files are understood, just pass the top-level foo.info to ! ;; `info-xref-check' and it traverses all sub-files. Compressed info files ! ;; are accepted too, as usual for `Info-mode'. ! ;; ! ;; `info-xref-check-all' is rather permissive in what it considers an info ! ;; file. It has to be since info files don't necessarily have a ".info" ! ;; suffix (eg. this is usual for the emacs manuals). One consequence of ! ;; this is that if for instance there's a source code directory in ! ;; `Info-directory-list' then a lot of extraneous files might be read, which ! ;; will be time consuming but should be harmless. ! ;; ! ;; `M-x info-xref-check-all-custom' is a related command, it goes through ! ;; all info document references in customizable variables, checking them ! ;; like info file cross references. ;;; Code: (require 'info) ! (defconst info-xref-results-buffer "*info-xref results*" "Name of the buffer for info-xref results.") ;;;###autoload (defun info-xref-check (filename) ! "Check external references in FILENAME, an info document." (interactive (list (let* ((default-filename --- 24,282 ---- ;;; Commentary: ! ;; This is some simple checking of external cross references in info files, ! ;; docstrings and custom-links by attempting to visit the nodes specified. ;; ! ;; `M-x info-xref-check' checks a single info file. See the docstring for ! ;; details. ! ;; ! ;; `M-x info-xref-check-all' checks all info files in Info-directory-list. ! ;; This is a good way to check the consistency of the whole system. ! ;; ! ;; `M-x info-xref-check-all-custom' loads up all defcustom variables and ! ;; checks any info references in them. ! ;; ! ;; `M-x info-xref-docstrings' checks docstring "Info node ..." hyperlinks in ! ;; source files (and other files). ! ! ;;; History: ! ! ;; Version 3 - new M-x info-xref-docstrings, use compilation-mode ;;; Code: (require 'info) + (eval-when-compile + (require 'cl)) ;; for `incf' + + ;;----------------------------------------------------------------------------- + ;; vaguely generic + + (defun info-xref-lock-file-p (filename) + "Return non-nil if FILENAME is an Emacs lock file. + A lock file is \".#foo.txt\" etc per `lock-buffer'." + (string-match "\\(\\`\\|\\/\\)\\.#" filename)) + + (defun info-xref-subfile-p (filename) + "Return t if FILENAME is an info subfile. + If removing the last \"-<NUM>\" from the filename gives a file + which exists, then consider FILENAME a subfile. This is an + imperfect test, probably ought to open up the purported top file + and see what subfiles it says." + (and (string-match "\\`\\(\\([^-]*-\\)*[^-]*\\)-[0-9]+\\(.*\\)\\'" filename) + (file-exists-p (concat (match-string 1 filename) + (match-string 3 filename))))) + + (defmacro info-xref-with-file (filename &rest body) + ;; checkdoc-params: (filename body) + "Evaluate BODY in a buffer containing the contents of FILENAME. + If FILENAME is already in a buffer then that's used, otherwise a + temporary buffer. + + The current implementation uses `insert-file-contents' rather + than `find-file-noselect' so as not to be held up by queries + about local variables or possible weirdness in a major mode. + `lm-with-file' does a similar thing, but it sets + `emacs-lisp-mode' which is not wanted here." + + (declare (debug t) (indent 1)) + `(let* ((info-xref-with-file--filename ,filename) + (info-xref-with-file--body (lambda () ,@body)) + (info-xref-with-file--existing + (find-buffer-visiting info-xref-with-file--filename))) + (if info-xref-with-file--existing + (with-current-buffer info-xref-with-file--existing + (save-excursion + (funcall info-xref-with-file--body))) + (with-temp-buffer + (insert-file-contents ,filename) + (funcall info-xref-with-file--body))))) + ! ;;----------------------------------------------------------------------------- ! ;; output buffer ! ! (defconst info-xref-output-buffer "*info-xref results*" "Name of the buffer for info-xref results.") + (defvar info-xref-good 0 + "Count of good cross references, during info-xref processing.") + (defvar info-xref-bad 0 + "Count of bad cross references, during info-xref processing.") + (defvar info-xref-unavail 0 + "Count of unavailable cross references, during info-xref processing.") + + (defvar info-xref-output-heading "" + "A heading string, during info-xref processing. + This is shown if there's an error, but not if successful.") + + (defvar info-xref-filename nil + "The current buffer's filename, during info-xref processing. + When looking at file contents in a temp buffer there's no + `buffer-file-name', hence this variable.") + + (defvar info-xref-xfile-alist nil + "Info files found or not found, during info-xref processing. + Key is \"(foo)\" etc and value nil or t according to whether info + manual \"(foo)\" exists or not. This is used to suppress + duplicate messages about foo not being available. (Duplicates + within one top-level file that is.)") + + (defvar info-xref-in-progress nil) + (defmacro info-xref-with-output (&rest body) + "Run BODY with an info-xref output buffer. + This is meant to nest, so you can wrap it around a set of + different info-xref checks and have them write to the one output + buffer created by the outermost `info-xref-with-output', with an + overall good/bad count summary inserted at the very end." + + (declare (debug t)) + `(save-excursion + (unless info-xref-in-progress + (display-buffer (get-buffer-create info-xref-output-buffer)) + (set-buffer info-xref-output-buffer) + (setq buffer-read-only nil) + (fundamental-mode) + (erase-buffer) + (insert ";; info-xref output -*- mode: compilation -*-\n\n") + (compilation-mode) + (setq info-xref-good 0 + info-xref-bad 0 + info-xref-unavail 0 + info-xref-xfile-alist nil)) + + (let ((info-xref-in-progress t) + (info-xref-output-heading "")) + ,@body) + + (unless info-xref-in-progress + (info-xref-output "done, %d good, %d bad, %d unavailable" + info-xref-good info-xref-bad info-xref-unavail)))) + + (defun info-xref-output (fmt &rest args) + "Emit a `format'-ed message FMT+ARGS to the `info-xref-output-buffer'." + (with-current-buffer info-xref-output-buffer + (save-excursion + (goto-char (point-max)) + (let ((inhibit-read-only t)) + (insert info-xref-output-heading + (apply 'format fmt args) + "\n"))) + (setq info-xref-output-heading "") + ;; all this info-xref can be pretty slow, display now so the user sees + ;; some progress + (sit-for 0))) + (put 'info-xref-output 'byte-compile-format-like t) + + (defun info-xref-output-error (fmt &rest args) + "Emit a `format'-ed error FMT+ARGS to the `info-xref-output-buffer'. + The error is attributed to `info-xref-filename' and the current + buffer's line and column of point." + (apply 'info-xref-output + (concat "%s:%s:%s: " fmt) + info-xref-filename + (1+ (count-lines (point-min) (line-beginning-position))) + (1+ (current-column)) + args)) + (put 'info-xref-output-error 'byte-compile-format-like t) + + + ;;----------------------------------------------------------------------------- + ;; node checking + + ;; When asking Info-goto-node to fork, *info* needs to be the current + ;; buffer, otherwise it seems to clone the current buffer but then do the + ;; goto-node in plain *info*. + ;; + ;; We only fork if *info* already exists, if it doesn't then can create and + ;; destroy just that instead of a new name. + ;; + ;; If Info-goto-node can't find the file, then no new buffer is created. If + ;; it finds the file but not the node, then a buffer is created. Handle + ;; this difference by checking before killing. + ;; + (defun info-xref-goto-node-p (node) + "Return t if it's possible to go to the given NODE." + (let ((oldbuf (current-buffer))) + (save-excursion + (save-window-excursion + (prog1 + (condition-case err + (progn + (Info-goto-node node + (when (get-buffer "*info*") + (set-buffer "*info*") + "xref - temporary")) + t) + (error nil)) + (unless (equal (current-buffer) oldbuf) + (kill-buffer))))))) + + (defun info-xref-check-node (node) + + ;; Collapse spaces as per info.el and `help-make-xrefs'. + ;; Note defcustom :info-link nodes don't get this whitespace collapsing, + ;; they should be the exact node name ready to visit. + ;; `info-xref-check-all-custom' uses `info-xref-goto-node-p' and so + ;; doesn't come through here. + ;; + ;; Could use "[\t\n ]+" but try to avoid uselessly replacing " " with " ". + (setq node (replace-regexp-in-string "[\t\n][\t\n ]*\\| [\t\n ]+" " " + node t t)) + + (if (not (string-match "\\`([^)]*)" node)) + (info-xref-output-error "no `(file)' part at start of node: %s\n" node) + (let ((file (match-string 0 node))) + + (if (string-equal "()" file) + (info-xref-output-error "empty filename part: %s" node) + + ;; see if the file exists, if haven't looked before + (unless (assoc file info-xref-xfile-alist) + (let ((found (info-xref-goto-node-p file))) + (push (cons file found) info-xref-xfile-alist) + (unless found + (info-xref-output-error "not available to check: %s\n (this reported once per file)" file)))) + + ;; if the file exists, try the node + (cond ((not (cdr (assoc file info-xref-xfile-alist))) + (incf info-xref-unavail)) + ((info-xref-goto-node-p node) + (incf info-xref-good)) + (t + (incf info-xref-bad) + (info-xref-output-error "no such node: %s" node))))))) + + + ;;----------------------------------------------------------------------------- + ;;;###autoload (defun info-xref-check (filename) ! "Check external references in FILENAME, an info document. ! Interactively from an `Info-mode' or `texinfo-mode' buffer the ! current info file is the default. ! ! Results are shown in a `compilation-mode' buffer. The format is ! a bit rough, but there shouldn't be many problems normally. The ! file:line:column: is the info document, but of course normally ! any correction should be made in the original .texi file. ! Finding the right place in the .texi is a manual process. ! ! When a target info file doesn't exist there's obviously no way to ! validate node references within it. A message is given for ! missing target files once per source document. It could be ! simply that you don't have the target installed, or it could be a ! mistake in the reference. ! ! Indirect info files are understood, just pass the top-level ! foo.info to `info-xref-check' and it traverses all sub-files. ! Compressed info files are accepted too as usual for `Info-mode'. ! ! \"makeinfo\" checks references internal to an info document, but ! not external references, which makes it rather easy for mistakes ! to creep in or node name changes to go unnoticed. ! `Info-validate' doesn't check external references either." ! (interactive (list (let* ((default-filename *************** *** 90,187 **** (format "Info file (%s): " default-filename) "Info file: "))) (read-file-name prompt nil default-filename t)))) (info-xref-check-list (list filename))) ;;;###autoload (defun info-xref-check-all () ! "Check external references in all info documents in the usual path. ! The usual path is `Info-directory-list' and `Info-additional-directory-list'." (interactive) (info-xref-check-list (info-xref-all-info-files))) ! ;; An alternative to trying to get only top-level files here would be to ! ;; simply return all files, and have info-xref-check-list not follow ! ;; Indirect:. The current way seems a bit nicer though, because it gets the ! ;; proper top-level filename into the error messages, and suppresses ! ;; duplicate "not available" messages for all subfiles of a single document. (defun info-xref-all-info-files () "Return a list of all available info files. ! Only top-level files are returned, subfiles are excluded. ! Since info files don't have to have a .info suffix, all files in the ! relevant directories are considered, which might mean a lot of extraneous ! things are returned if for instance a source code directory is in the path." (info-initialize) ;; establish Info-directory-list (apply 'nconc (mapcar (lambda (dir) (let ((result nil)) ! (dolist (name (directory-files dir t)) ! (unless (or (file-directory-p name) (info-xref-subfile-p name)) (push name result))) (nreverse result))) (append Info-directory-list Info-additional-directory-list)))) - (defun info-xref-subfile-p (filename) - "Return t if FILENAME is an info subfile. - If removing the last \"-<NUM>\" from the filename gives a file that exists, - then consider FILENAME a subfile. This is an imperfect test, we probably - should open up the purported top file and see what subfiles it says." - (and (string-match "\\`\\(\\([^-]*-\\)*[^-]*\\)-[0-9]+\\(.*\\)\\'" filename) - (file-exists-p (concat (match-string 1 filename) - (match-string 3 filename))))) - - - ;; Some dynamic variables are used to share information with sub-functions - ;; below. - ;; - ;; info-xref-filename-header - a heading message for the current top-level - ;; filename, or "" when it's been printed. - ;; - (defvar info-xref-xfile-alist) - ;; - ;; info-xref-good - count of good cross references. - ;; - (defvar info-xref-good) - ;; - ;; info-xref-bad - count of bad cross references. - ;; - (defvar info-xref-bad) - ;; - ;; info-xref-xfile-alist - indexed by "(foo)" with value nil or t according - ;; to whether "(foo)" exists or not. This is used to suppress duplicate - ;; messages about foo not being available. (Duplicates within one - ;; top-level file that is.) - ;; - (defvar info-xref-filename-heading) - (defun info-xref-check-list (filename-list) "Check external references in info documents in FILENAME-LIST." ! (pop-to-buffer info-xref-results-buffer t) ! (erase-buffer) ! (let ((info-xref-good 0) ! (info-xref-bad 0)) (dolist (info-xref-filename filename-list) ! (let ((info-xref-filename-heading ! (format "In file %s:\n" info-xref-filename)) ! (info-xref-xfile-alist nil)) (with-temp-message (format "Looking at %s" info-xref-filename) (with-temp-buffer (info-insert-file-contents info-xref-filename) (goto-char (point-min)) ! (if (re-search-forward "\^_\nIndirect:\n" nil t) (let ((dir (file-name-directory info-xref-filename))) (while (looking-at "\\(.*\\): [0-9]+\n") ! (let ((subfile (match-string 1))) (with-temp-buffer ! (info-insert-file-contents ! (expand-file-name subfile dir)) (info-xref-check-buffer))) (forward-line))) ! (info-xref-check-buffer)))))) ! (insert (format "done, %d good, %d bad\n" info-xref-good info-xref-bad)))) (defun info-xref-check-buffer () "Check external references in the info file in the current buffer. --- 294,373 ---- (format "Info file (%s): " default-filename) "Info file: "))) (read-file-name prompt nil default-filename t)))) + (info-xref-check-list (list filename))) ;;;###autoload (defun info-xref-check-all () ! "Check external references in all info documents in the info path. ! `Info-directory-list' and `Info-additional-directory-list' are ! the info paths. See `info-xref-check' for how each file is ! checked. ! ! The search for \"all\" info files is rather permissive, since ! info files don't necessarily have a \".info\" extension and in ! particular the Emacs manuals normally don't. If you have a ! source code directory in `Info-directory-list' then a lot of ! extraneous files might be read. This will be time consuming but ! should be harmless." ! (interactive) (info-xref-check-list (info-xref-all-info-files))) ! ;; An alternative for geting only top-level files here would be to simply ! ;; return all files and have info-xref-check-list not follow "Indirect:". ! ;; The current way seems better because it (potentially) gets the proper ! ;; top-level filename into the error messages, and suppresses duplicate "not ! ;; available" messages for all subfiles of a single document. (defun info-xref-all-info-files () "Return a list of all available info files. ! Only top level files are returned, subfiles are excluded. ! Since info files don't have to have a .info suffix, all files in ! the relevant directories are considered, which might mean a lot ! of extraneous things if for instance a source code directory is ! in the path." (info-initialize) ;; establish Info-directory-list (apply 'nconc (mapcar (lambda (dir) (let ((result nil)) ! (dolist (name (directory-files ! dir ! t ;; absolute filenames ! "\\`[^.]")) ;; not dotfiles, nor .# lockfiles ! (when (and (file-exists-p name) ;; ignore broken symlinks ! (not (string-match "\\.te?xi\\'" name)) ;; not .texi ! (not (backup-file-name-p name)) ! (not (file-directory-p name)) ! (not (info-xref-subfile-p name))) (push name result))) (nreverse result))) (append Info-directory-list Info-additional-directory-list)))) (defun info-xref-check-list (filename-list) "Check external references in info documents in FILENAME-LIST." ! (info-xref-with-output (dolist (info-xref-filename filename-list) ! (setq info-xref-xfile-alist nil) ! (let ((info-xref-output-heading ! (format "Info file %s\n" info-xref-filename))) (with-temp-message (format "Looking at %s" info-xref-filename) (with-temp-buffer (info-insert-file-contents info-xref-filename) (goto-char (point-min)) ! (if (search-forward "\^_\nIndirect:\n" nil t) (let ((dir (file-name-directory info-xref-filename))) (while (looking-at "\\(.*\\): [0-9]+\n") ! (let ((info-xref-filename ! (expand-file-name (match-string 1) dir))) (with-temp-buffer ! (info-insert-file-contents info-xref-filename) (info-xref-check-buffer))) (forward-line))) ! (info-xref-check-buffer)))))))) (defun info-xref-check-buffer () "Check external references in the info file in the current buffer. *************** *** 190,301 **** (while (re-search-forward "\\*[Nn]ote[ \n\t]+[^:]*:[ \n\t]+\\(\\(([^)]*)\\)[^.,]+\\)[.,]" nil t) - (let* ((file (match-string 2)) - (node ;; Canonicalize spaces: we could use "[\t\n ]+" but - ;; we try to avoid uselessly replacing " " with " ". - (replace-regexp-in-string "[\t\n][\t\n ]*\\| [\t\n ]+" " " - (match-string 1) t t))) - (if (string-equal "()" file) - (info-xref-output "Empty filename part: %s\n" node) - ;; see if the file exists, if we haven't tried it before - (unless (assoc file info-xref-xfile-alist) - (let ((found (info-xref-goto-node-p file))) - (push (cons file found) info-xref-xfile-alist) - (unless found - (info-xref-output "Not available to check: %s\n" file)))) - ;; if the file exists, try the node - (when (cdr (assoc file info-xref-xfile-alist)) - (if (info-xref-goto-node-p node) - (setq info-xref-good (1+ info-xref-good)) - (setq info-xref-bad (1+ info-xref-bad)) - (info-xref-output "No such node: %s\n" node))))))) - - (defun info-xref-output (str &rest args) - "Emit a `format'-ed message STR+ARGS to the info-xref output buffer." - (with-current-buffer info-xref-results-buffer - (insert info-xref-filename-heading - (apply 'format str args)) - (setq info-xref-filename-heading "") - ;; all this info-xref can be pretty slow, display now so the user can - ;; see some progress - (sit-for 0))) - - ;; When asking Info-goto-node to fork, *info* needs to be the current - ;; buffer, otherwise it seems to clone the current buffer but then do the - ;; goto-node in plain *info*. - ;; - ;; We only fork if *info* already exists, if it doesn't then we can create - ;; and destroy just that instead of a new name. - ;; - ;; If Info-goto-node can't find the file, then no new buffer is created. If - ;; it finds the file but not the node, then a buffer is created. Handle - ;; this difference by checking before killing. - ;; - (defun info-xref-goto-node-p (node) - "Return t if it's possible to go to the given NODE." - (let ((oldbuf (current-buffer))) (save-excursion ! (save-window-excursion ! (prog1 ! (condition-case err ! (progn ! (Info-goto-node node ! (when (get-buffer "*info*") ! (set-buffer "*info*") ! "xref - temporary")) ! t) ! (error nil)) ! (unless (equal (current-buffer) oldbuf) ! (kill-buffer (current-buffer)))))))) ;;;###autoload (defun info-xref-check-all-custom () "Check info references in all customize groups and variables. ! `custom-manual' and `info-link' entries in the `custom-links' list are checked. ! `custom-load' autoloads for all symbols are loaded in order to get all the ! link information. This will be a lot of lisp packages loaded, and can take ! quite a while." (interactive) ! (pop-to-buffer info-xref-results-buffer t) ! (erase-buffer) ! (let ((info-xref-filename-heading "")) ! ;; `custom-load-symbol' is not used, since it quietly ignores errors, ! ;; but we want to show them (since they may mean incomplete checking). ;; ;; Just one pass through mapatoms is made. There shouldn't be any new ;; custom-loads setup by packages loaded. ;; ! (info-xref-output "Loading custom-load autoloads ...\n") (require 'cus-start) (require 'cus-load) ! (let ((viper-mode nil)) ;; tell viper.el not to ask about viperizing (mapatoms (lambda (symbol) (dolist (load (get symbol 'custom-loads)) (cond ((symbolp load) (condition-case cause (require load) (error ! (info-xref-output "Symbol `%s': cannot require '%s: %s\n" symbol load cause)))) ;; skip if previously loaded ((assoc load load-history)) ((assoc (locate-library load) load-history)) (t ! (condition-case cause (load load) (error ! (info-xref-output "Symbol `%s': cannot load \"%s\": %s\n" ! symbol load cause))))))))) ;; Don't bother to check whether the info file exists as opposed to just ! ;; a missing node. If you have the lisp then you should have the ! ;; documentation, so missing node name will be the usual fault. ;; ! (info-xref-output "\nChecking custom-links references ...\n") ! (let ((good 0) ! (bad 0)) (mapatoms (lambda (symbol) (dolist (link (get symbol 'custom-links)) --- 376,445 ---- (while (re-search-forward "\\*[Nn]ote[ \n\t]+[^:]*:[ \n\t]+\\(\\(([^)]*)\\)[^.,]+\\)[.,]" nil t) (save-excursion ! (goto-char (match-beginning 1)) ;; start of nodename as error position ! (info-xref-check-node (match-string 1))))) ! ! (defvar viper-mode) ;; quieten the byte compiler ! (defvar gnus-registry-install) ;;;###autoload (defun info-xref-check-all-custom () "Check info references in all customize groups and variables. ! Info references can be in `custom-manual' or `info-link' entries ! of the `custom-links' for a variable. ! Any `custom-load' autoloads in variables are loaded in order to ! get full link information. This will be a lot of Lisp packages ! and can take a long time." (interactive) ! (info-xref-with-output ! ;; `custom-load-symbol' is not used, since it quietly ignores errors, but ! ;; we want to show them since they mean incomplete checking. ;; ;; Just one pass through mapatoms is made. There shouldn't be any new ;; custom-loads setup by packages loaded. ;; ! (info-xref-output "Loading custom-load autoloads ...") (require 'cus-start) (require 'cus-load) ! ! ;; These are `setq' rather than `let' since a let would unbind the ! ;; variables after viper.el/gnus-registry.el have loaded, defeating the ! ;; defvars in those files. Of course it'd be better if those files ! ;; didn't make interactive queries on loading at all, to allow for ! ;; programmatic loading like here. ! (unless (boundp 'viper-mode) ! (setq viper-mode nil)) ;; avoid viper.el ask about viperizing ! (unless (boundp 'gnus-registry-install) ! (setq gnus-registry-install nil)) ;; avoid gnus-registery.el querying ! (mapatoms (lambda (symbol) (dolist (load (get symbol 'custom-loads)) (cond ((symbolp load) (condition-case cause (require load) (error ! (info-xref-output "Symbol `%s': cannot require '%s: %s" symbol load cause)))) ;; skip if previously loaded ((assoc load load-history)) ((assoc (locate-library load) load-history)) (t ! (condition-case err ! (load load) (error ! (info-xref-output "Symbol `%s': cannot load \"%s\": %s" ! symbol load ! (error-message-string err))))))))) ;; Don't bother to check whether the info file exists as opposed to just ! ;; a missing node. If you have the code then you should have the ! ;; documentation, so a wrong node name will be the usual fault. ;; ! (info-xref-output "\nChecking custom-links references ...") (mapatoms (lambda (symbol) (dolist (link (get symbol 'custom-links)) *************** *** 304,318 **** (if (eq :tag (cadr link)) (setq link (cddr link))) (if (info-xref-goto-node-p (cadr link)) ! (setq good (1+ good)) ! (setq bad (1+ bad)) ;; symbol-file gives nil for preloaded variables, would need ;; to copy what describe-variable does to show the right place ! (info-xref-output "Symbol `%s' (in %s): cannot goto node: %s\n" ! symbol (symbol-file symbol) (cadr link))))))) ! (info-xref-output "%d good, %d bad\n" good bad)))) (provide 'info-xref) - ;; arch-tag: 69d4d528-69ed-4cc2-8eb4-c666a0c1d5ac ;;; info-xref.el ends here --- 448,528 ---- (if (eq :tag (cadr link)) (setq link (cddr link))) (if (info-xref-goto-node-p (cadr link)) ! (incf info-xref-good) ! (incf info-xref-bad) ;; symbol-file gives nil for preloaded variables, would need ;; to copy what describe-variable does to show the right place ! (info-xref-output "Symbol `%s' (file %s): cannot goto node: %s" ! symbol ! (symbol-file symbol 'defvar) ! (cadr link))))))))) ! ! ;;;###autoload ! (defun info-xref-docstrings (filename-list) ! ;; checkdoc-params: (filename-list) ! "Check docstring info node references in source files. ! The given files are searched for docstring hyperlinks like ! ! Info node `(elisp)Documentation Tips' ! ! and those links checked by attempting to visit the target nodes ! as per `info-xref-check' does. ! ! Interactively filenames are read as a wildcard pattern like ! \"foo*.el\", with the current file as a default. Usually this ! will be lisp sources, but anything with such hyperlinks can be ! checked, including the Emacs .c sources (or the etc/DOC file of ! all builtins). ! ! Because info node hyperlinks are found by a simple regexp search ! in the files, the Lisp code checked doesn't have to be loaded, ! and links can be in the file commentary or elsewhere too. Even ! .elc files can usually be checked successfully if you don't have ! the sources handy." ! ! (interactive ! (let* ((default (and buffer-file-name ! (file-relative-name buffer-file-name))) ! (prompt (if default ! (format "Filename with wildcards (%s): " ! default) ! "Filename with wildcards: ")) ! (pattern (read-file-name prompt nil default)) ! (filename-list (file-expand-wildcards pattern ! t))) ;; absolute filenames ! (eval-and-compile ! (require 'cl)) ;; for `remove-if' ! (setq filename-list (remove-if 'info-xref-lock-file-p filename-list)) ! (unless filename-list ! (error "No files: %S" pattern)) ! (list filename-list))) ! ! (eval-and-compile ! (require 'help-mode)) ;; for `help-xref-info-regexp' ! ! (info-xref-with-output ! (dolist (info-xref-filename filename-list) ! (setq info-xref-xfile-alist nil) ;; "not found"s once per file ! ! (info-xref-with-file info-xref-filename ! (goto-char (point-min)) ! (while (re-search-forward help-xref-info-regexp nil t) ! (let ((node (match-string 2))) ! (save-excursion ! (goto-char (match-beginning 2)) ;; start of node as error position ! ! ;; skip nodes with "%" as probably `format' strings such as in ! ;; info-look.el ! (unless (string-match "%" node) ! ! ;; "(emacs)" is the default manual for docstring hyperlinks, ! ;; per `help-make-xrefs' ! (unless (string-match "\\`(" node) ! (setq node (concat "(emacs)" node))) ! ! (info-xref-check-node node))))))))) ! (provide 'info-xref) ;;; info-xref.el ends here
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.