X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/e18afed7d695edac870ddf55aabc85c0a95a4b5f..34f10d412a96624716e83cb5b007dec17c80ee89:/lisp/info.el diff --git a/lisp/info.el b/lisp/info.el index 1e3b14632e..7365b0efcf 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -1,6 +1,6 @@ ;; info.el --- info package for Emacs -;; Copyright (C) 1985-1986, 1992-2012 Free Software Foundation, Inc. +;; Copyright (C) 1985-1986, 1992-2012 Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: help @@ -32,8 +32,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) - (defgroup info nil "Info subsystem." :group 'help @@ -169,6 +167,83 @@ A header-line does not scroll with the rest of the buffer." "Face for Info nodes in a node header." :group 'info) +;; This is a defcustom largely so that we can get the benefit +;; of custom-initialize-delay. Perhaps it would work to make it a +;; defvar and explicitly give it a standard-value property, and +;; call custom-initialize-delay on it. +;; The progn forces the autoloader to include the whole thing, not +;; just an abbreviated version. +;;;###autoload +(progn +(defcustom Info-default-directory-list + (let* ((config-dir + (file-name-as-directory + ;; Self-contained NS build with info/ in the app-bundle. + (or (and (featurep 'ns) + (let ((dir (expand-file-name "../info" data-directory))) + (if (file-directory-p dir) dir))) + configure-info-directory))) + (prefixes + ;; Directory trees in which to look for info subdirectories + (prune-directory-list '("/usr/local/" "/usr/" "/opt/" "/"))) + (suffixes + ;; Subdirectories in each directory tree that may contain info + ;; directories. Most of these are rather outdated. + ;; It ought to be fine to stop checking the "emacs" ones now, + ;; since this is Emacs and we have not installed info files + ;; into such directories for a looong time... + '("share/" "" "gnu/" "gnu/lib/" "gnu/lib/emacs/" + "emacs/" "lib/" "lib/emacs/")) + (standard-info-dirs + (apply #'nconc + (mapcar (lambda (pfx) + (let ((dirs + (mapcar (lambda (sfx) + (concat pfx sfx "info/")) + suffixes))) + (prune-directory-list dirs))) + prefixes))) + ;; If $(prefix)/share/info is not one of the standard info + ;; directories, they are probably installing an experimental + ;; version of Emacs, so make sure that experimental version's Info + ;; files override the ones in standard directories. + (dirs + (if (member config-dir standard-info-dirs) + ;; FIXME? What is the point of adding it again at the end + ;; when it is already present earlier in the list? + (nconc standard-info-dirs (list config-dir)) + (cons config-dir standard-info-dirs)))) + (if (not (eq system-type 'windows-nt)) + dirs + ;; Include the info directory near where Emacs executable was installed. + (let* ((instdir (file-name-directory invocation-directory)) + (dir1 (expand-file-name "../info/" instdir)) + (dir2 (expand-file-name "../../../info/" instdir))) + (cond ((file-exists-p dir1) (append dirs (list dir1))) + ((file-exists-p dir2) (append dirs (list dir2))) + (t dirs))))) + + "Default list of directories to search for Info documentation files. +They are searched in the order they are given in the list. +Therefore, the directory of Info files that come with Emacs +normally should come last (so that local files override standard ones), +unless Emacs is installed into a non-standard directory. In the latter +case, the directory of Info files that come with Emacs should be +first in this list. + +Once Info is started, the list of directories to search +comes from the variable `Info-directory-list'. +This variable `Info-default-directory-list' is used as the default +for initializing `Info-directory-list' when Info is started, unless +the environment variable INFOPATH is set. + +Although this is a customizable variable, that is mainly for technical +reasons. Normally, you should either set INFOPATH or customize +`Info-additional-directory-list', rather than changing this variable." + :initialize 'custom-initialize-delay + :type '(repeat directory) + :group 'info)) + (defvar Info-directory-list nil "List of directories to search for Info documentation files. If nil, meaning not yet initialized, Info uses the environment @@ -267,12 +342,12 @@ a tab, a carriage return (control-M), a newline, and `]+'." (defcustom Info-isearch-search t "If non-nil, isearch in Info searches through multiple nodes. Before leaving the initial Info node, where isearch was started, -it fails once with the error message [initial node], and with +it fails once with the error message [end of node], and with subsequent C-s/C-r continues through other nodes without failing with this error message in other nodes. When isearch fails for -the rest of the manual, it wraps around the whole manual and -restarts the search from the top/final node depending on -search direction. +the rest of the manual, it displays the error message [end of manual], +wraps around the whole manual and restarts the search from the top/final +node depending on search direction. Setting this option to nil restores the default isearch behavior with wrapping around the current Info node." @@ -342,6 +417,21 @@ If number, the point is moved to the corresponding line.") (defvar Info-standalone nil "Non-nil if Emacs was started solely as an Info browser.") +(defvar Info-file-attributes nil + "Alist of file attributes of visited Info files. +Each element is a list (FILE-NAME FILE-ATTRIBUTES...).") + +(defvar Info-toc-nodes nil + "Alist of cached parent-children node information in visited Info files. +Each element is (FILE (NODE-NAME PARENT SECTION CHILDREN) ...) +where PARENT is the parent node extracted from the Up pointer, +SECTION is the section name in the Top node where this node is placed, +CHILDREN is a list of child nodes extracted from the node menu.") + +(defvar Info-index-nodes nil + "Alist of cached index node names of visited Info files. +Each element has the form (INFO-FILE INDEX-NODE-NAMES-LIST).") + (defvar Info-virtual-files nil "List of definitions of virtual Info files. Each element of the list has the format (FILENAME (OPERATION . HANDLER) ...) @@ -534,7 +624,26 @@ Do the right thing if the file has been compressed or zipped." (apply 'call-process-region (point-min) (point-max) (car decoder) t t nil (cdr decoder)))) (let ((inhibit-null-byte-detection t)) ; Index nodes include null bytes - (insert-file-contents fullname visit))))) + (insert-file-contents fullname visit))) + + ;; Clear the caches of modified Info files. + (let* ((attribs-old (cdr (assoc fullname Info-file-attributes))) + (modtime-old (and attribs-old (nth 5 attribs-old))) + (attribs-new (and (stringp fullname) (file-attributes fullname))) + (modtime-new (and attribs-new (nth 5 attribs-new)))) + (when (and modtime-old modtime-new + (> (float-time modtime-new) (float-time modtime-old))) + (setq Info-index-nodes (remove (assoc (or Info-current-file filename) + Info-index-nodes) + Info-index-nodes)) + (setq Info-toc-nodes (remove (assoc (or Info-current-file filename) + Info-toc-nodes) + Info-toc-nodes))) + ;; Add new modtime to `Info-file-attributes'. + (setq Info-file-attributes + (cons (cons fullname attribs-new) + (remove (assoc fullname Info-file-attributes) + Info-file-attributes)))))) (defun Info-file-supports-index-cookies (&optional file) "Return non-nil value if FILE supports Info index cookies. @@ -618,7 +727,19 @@ in `Info-file-supports-index-cookies-list'." (append (split-string (substring path 0 -1) sep) (Info-default-dirs)) (split-string path sep)) - (Info-default-dirs))))))) + (Info-default-dirs)))) + ;; For a self-contained (ie relocatable) NS build, AFAICS we + ;; always want the included info directory to be at the head of + ;; the search path, unless it's already in INFOPATH somewhere. + ;; It's at the head of Info-default-directory-list, + ;; but there's no way to get it at the head of Info-directory-list + ;; except by doing it here. + (and path + (featurep 'ns) + (let ((dir (expand-file-name "../info" data-directory))) + (and (file-directory-p dir) + (not (member dir (split-string path ":" t))) + (push dir Info-directory-list))))))) ;;;###autoload (defun info-other-window (&optional file-or-node) @@ -682,6 +803,12 @@ See a list of available Info commands in `Info-mode'." (interactive) (info "emacs")) +;;;###autoload +(defun info-emacs-bug () + "Display the \"Reporting Bugs\" section of the Emacs manual in Info mode." + (interactive) + (info "(emacs)Bugs")) + ;;;###autoload (defun info-standalone () "Run Emacs as a standalone Info reader. @@ -1770,7 +1897,7 @@ If DIRECTION is `backward', search in the reverse direction." (not bound) (or give-up (and found (not (and (> found opoint-min) (< found opoint-max)))))) - (signal 'search-failed (list regexp "initial node"))) + (signal 'search-failed (list regexp "end of node"))) ;; If no subfiles, give error now. (if give-up @@ -1907,26 +2034,23 @@ If DIRECTION is `backward', search in the reverse direction." (defun Info-isearch-search () (if Info-isearch-search (lambda (string &optional bound noerror count) - (if isearch-word - (Info-search (concat "\\b" (replace-regexp-in-string - "\\W+" "\\W+" - (replace-regexp-in-string - "^\\W+\\|\\W+$" "" string) - nil t) - ;; Lax version of word search - (if (or isearch-nonincremental - (eq (length string) - (length (isearch-string-state - (car isearch-cmds))))) - "\\b")) - bound noerror count - (unless isearch-forward 'backward)) - (Info-search (if isearch-regexp string (regexp-quote string)) - bound noerror count - (unless isearch-forward 'backward))) + (Info-search + (cond + (isearch-word + ;; Lax version of word search + (let ((lax (not (or isearch-nonincremental + (eq (length string) + (length (isearch--state-string + (car isearch-cmds)))))))) + (if (functionp isearch-word) + (funcall isearch-word string lax) + (word-search-regexp string lax)))) + (isearch-regexp string) + (t (regexp-quote string))) + bound noerror count + (unless isearch-forward 'backward)) (point)) - (let ((isearch-search-fun-function nil)) - (isearch-search-fun)))) + (isearch-search-fun-default))) (defun Info-isearch-wrap () (if Info-isearch-search @@ -2304,13 +2428,6 @@ Table of contents is created from the tree structure of menus." (message "") (nreverse nodes)))) -(defvar Info-toc-nodes nil - "Alist of cached parent-children node information in visited Info files. -Each element is (FILE (NODE-NAME PARENT SECTION CHILDREN) ...) -where PARENT is the parent node extracted from the Up pointer, -SECTION is the section name in the Top node where this node is placed, -CHILDREN is a list of child nodes extracted from the node menu.") - (defun Info-toc-nodes (filename) "Return a node list of Info FILENAME with parent-children information. This information is cached in the variable `Info-toc-nodes' with the help @@ -2764,7 +2881,7 @@ N is the digit argument used to invoke this command." (Info-extract-menu-node-name))))) (defmacro Info-no-error (&rest body) - (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil))) + `(condition-case nil (progn ,@body t) (error nil))) (defun Info-next-preorder () "Go to the next subnode or the next node, or go up a level." @@ -2942,10 +3059,6 @@ See `Info-scroll-down'." (if (looking-at "^\\* ") (forward-char 2))))) -(defvar Info-index-nodes nil - "Alist of cached index node names of visited Info files. -Each element has the form (INFO-FILE INDEX-NODE-NAMES-LIST).") - (defun Info-index-nodes (&optional file) "Return a list of names of all index nodes in Info FILE. If FILE is omitted, it defaults to the current Info file. @@ -3677,15 +3790,22 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (defun Info-mouse-follow-link (click) "Follow a link where you click." - (interactive "e") + (interactive "@e") (let* ((position (event-start click)) (posn-string (and position (posn-string position))) - (string (car-safe posn-string)) - (string-pos (cdr-safe posn-string)) - (link-args (and string string-pos - (get-text-property string-pos 'link-args string)))) - (when link-args - (Info-goto-node link-args)))) + (link-args (if posn-string + (get-text-property (cdr posn-string) + 'link-args + (car posn-string)) + (get-char-property (posn-point position) + 'link-args)))) + (cond ((stringp link-args) + (Info-goto-node link-args)) + ;; These special values of the `link-args' property are used + ;; for navigation; see `Info-fontify-node'. + ((eq link-args 'prev) (Info-prev)) + ((eq link-args 'next) (Info-next)) + ((eq link-args 'up) (Info-up))))) (defvar Info-mode-map @@ -3743,6 +3863,8 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (define-key map "\177" 'Info-scroll-down) (define-key map [mouse-2] 'Info-mouse-follow-nearest-node) (define-key map [follow-link] 'mouse-face) + (define-key map [XF86Back] 'Info-history-back) + (define-key map [XF86Forward] 'Info-history-forward) map) "Keymap containing Info commands.") @@ -4258,45 +4380,17 @@ the variable `Info-file-list-for-emacs'." (t (Info-goto-emacs-command-node command))))) -(defvar Info-next-link-keymap - (let ((keymap (make-sparse-keymap))) - (define-key keymap [header-line mouse-1] 'Info-next) - (define-key keymap [header-line mouse-2] 'Info-next) - (define-key keymap [header-line down-mouse-1] 'ignore) - (define-key keymap [mouse-2] 'Info-next) - (define-key keymap [follow-link] 'mouse-face) - keymap) - "Keymap to put on the Next link in the text or the header line.") - -(defvar Info-prev-link-keymap - (let ((keymap (make-sparse-keymap))) - (define-key keymap [header-line mouse-1] 'Info-prev) - (define-key keymap [header-line mouse-2] 'Info-prev) - (define-key keymap [header-line down-mouse-1] 'ignore) - (define-key keymap [mouse-2] 'Info-prev) - (define-key keymap [follow-link] 'mouse-face) - keymap) - "Keymap to put on the Prev link in the text or the header line.") - -(defvar Info-up-link-keymap - (let ((keymap (make-sparse-keymap))) - (define-key keymap [header-line mouse-1] 'Info-up) - (define-key keymap [header-line mouse-2] 'Info-up) - (define-key keymap [header-line down-mouse-1] 'ignore) - (define-key keymap [mouse-2] 'Info-up) - (define-key keymap [follow-link] 'mouse-face) - keymap) - "Keymap to put on the Up link in the text or the header line.") - (defvar Info-link-keymap (let ((keymap (make-sparse-keymap))) - (define-key keymap [header-line mouse-1] 'Info-mouse-follow-link) + (define-key keymap [header-line down-mouse-1] 'mouse-drag-header-line) + (define-key keymap [header-line mouse-1] 'mouse-select-window) (define-key keymap [header-line mouse-2] 'Info-mouse-follow-link) - (define-key keymap [header-line down-mouse-1] 'ignore) (define-key keymap [mouse-2] 'Info-mouse-follow-link) (define-key keymap [follow-link] 'mouse-face) keymap) - "Keymap to put on the link in the text or the header line.") + "Keymap to put on Info links. +This is used for the \"Next\", \"Prev\", and \"Up\" links in the +first line or header line, and for breadcrumb links.") (defun Info-breadcrumbs () (let ((nodes (Info-toc-nodes Info-current-file)) @@ -4385,15 +4479,14 @@ the variable `Info-file-list-for-emacs'." 'help-echo (concat "mouse-2: Go to node " (buffer-substring nbeg nend))) - ;; Always set up the text property keymap. - ;; It will either be used in the buffer - ;; or copied in the header line. - (put-text-property - tbeg nend 'keymap - (cond - ((string-equal (downcase tag) "prev") Info-prev-link-keymap) - ((string-equal (downcase tag) "next") Info-next-link-keymap) - ((string-equal (downcase tag) "up" ) Info-up-link-keymap)))))) + ;; Set up the text property keymap. Depending on + ;; `Info-use-header-line', it is either used in the + ;; buffer, or copied to the header line. A symbol value + ;; of the `link-args' property is handled specially by + ;; `Info-mouse-follow-link'. + (put-text-property tbeg nend 'keymap Info-link-keymap) + (put-text-property tbeg nend 'link-args + (intern (downcase tag)))))) ;; (when (> Info-breadcrumbs-depth 0) ;; (insert (Info-breadcrumbs))) @@ -4434,7 +4527,17 @@ the variable `Info-file-list-for-emacs'." ((not (bobp)) ;; Hide the punctuation at the end, too. (skip-chars-backward " \t,") - (put-text-property (point) header-end 'invisible t)))))) + (put-text-property (point) header-end 'invisible t) + ;; Hide the suffix of the Info file name. + (beginning-of-line) + (if (re-search-forward + (format "File: %s\\([^,\n\t]+\\)," + (if (stringp Info-current-file) + (file-name-nondirectory Info-current-file) + Info-current-file)) + header-end t) + (put-text-property (match-beginning 1) (match-end 1) + 'invisible t))))))) ;; Fontify titles (goto-char (point-min)) @@ -4950,11 +5053,18 @@ BUFFER is the buffer speedbar is requesting buttons for." (defun Info-bookmark-make-record () "This implements the `bookmark-make-record-function' type (which see) for Info nodes." - `(,Info-current-node - ,@(bookmark-make-record-default 'no-file) - (filename . ,Info-current-file) - (info-node . ,Info-current-node) - (handler . Info-bookmark-jump))) + (let* ((file (and (stringp Info-current-file) + (file-name-nondirectory Info-current-file))) + (bookmark-name (if file + (concat "(" file ") " Info-current-node) + Info-current-node)) + (defaults (delq nil (list bookmark-name file Info-current-node)))) + `(,bookmark-name + ,@(bookmark-make-record-default 'no-file) + (filename . ,Info-current-file) + (info-node . ,Info-current-node) + (handler . Info-bookmark-jump) + (defaults . ,defaults)))) ;;;###autoload (defun Info-bookmark-jump (bmk)