X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/4c0078d4ab7fa8c234fa8098ec3a9686199edbff..ea5f4192b9954301c0c65804586ed7daf3a98c16:/lisp/info.el diff --git a/lisp/info.el b/lisp/info.el index ea23cf3556..96c22e1511 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -1,6 +1,6 @@ ;; info.el --- info package for Emacs -;; Copyright (C) 1985-1986, 1992-2011 Free Software Foundation, Inc. +;; Copyright (C) 1985-1986, 1992-2013 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 @@ -231,6 +306,12 @@ want to set `Info-refill-paragraphs'." (const :tag "Replace tag and hide reference" t) (const :tag "Hide tag and reference" hide) (other :tag "Only replace tag" tag)) + :set (lambda (sym val) + (set sym val) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (eq major-mode 'Info-mode) + (revert-buffer t t))))) :group 'info) (defcustom Info-refill-paragraphs nil @@ -261,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 aroung 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." @@ -336,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) ...) @@ -528,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. @@ -612,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) @@ -649,7 +776,8 @@ See a list of available Info commands in `Info-mode'." (read-file-name "Info file name: " nil nil t)) (if (numberp current-prefix-arg) (format "*info*<%s>" current-prefix-arg)))) - (info-setup file-or-node (switch-to-buffer (or buffer "*info*")))) + (info-setup file-or-node + (pop-to-buffer-same-window (or buffer "*info*")))) (defun info-setup (file-or-node buffer) "Display Info node FILE-OR-NODE in BUFFER." @@ -675,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. @@ -810,10 +944,6 @@ otherwise, that defaults to `Top'." (concat default-directory (buffer-name)))) (Info-find-node-2 nil nodename)) -;; It's perhaps a bit nasty to kill the *info* buffer to force a re-read, -;; but at least it keeps this routine (which is for makeinfo-buffer and -;; Info-revert-buffer-function) out of the way of normal operations. -;; (defun Info-revert-find-node (filename nodename) "Go to an Info node FILENAME and NODENAME, re-reading disk contents. When *info* is already displaying FILENAME and NODENAME, the window position @@ -821,27 +951,23 @@ is preserved, if possible." (or (eq major-mode 'Info-mode) (switch-to-buffer "*info*")) (let ((old-filename Info-current-file) (old-nodename Info-current-node) - (old-buffer-name (buffer-name)) + (window-selected (eq (selected-window) (get-buffer-window))) (pcolumn (current-column)) (pline (count-lines (point-min) (line-beginning-position))) (wline (count-lines (point-min) (window-start))) - (old-history-forward Info-history-forward) - (old-history Info-history) (new-history (and Info-current-file (list Info-current-file Info-current-node (point))))) - (kill-buffer (current-buffer)) - (switch-to-buffer (or old-buffer-name "*info*")) - (Info-mode) + ;; When `Info-current-file' is nil, `Info-find-node-2' rereads the file. + (setq Info-current-file nil) (Info-find-node filename nodename) - (setq Info-history-forward old-history-forward) - (setq Info-history old-history) (if (and (equal old-filename Info-current-file) (equal old-nodename Info-current-node)) (progn ;; note goto-line is no good, we want to measure from point-min - (goto-char (point-min)) - (forward-line wline) - (set-window-start (selected-window) (point)) + (when window-selected + (goto-char (point-min)) + (forward-line wline) + (set-window-start (selected-window) (point))) (goto-char (point-min)) (forward-line pline) (move-to-column pcolumn)) @@ -1072,7 +1198,7 @@ a case-insensitive match is tried." (throw 'foo t)) ;; No such anchor in tag table or node in tag table or file - (error "No such node or anchor: %s" nodename)) + (user-error "No such node or anchor: %s" nodename)) (Info-select-node) (goto-char (point-min)) @@ -1086,7 +1212,7 @@ a case-insensitive match is tried." ;; Add anchors to the history too (setq Info-history-list (cons new-history - (delete new-history Info-history-list)))) + (remove new-history Info-history-list)))) (goto-char anchorpos)) ((numberp Info-point-loc) (forward-line (- Info-point-loc 2)) @@ -1166,6 +1292,12 @@ a case-insensitive match is tried." (progn (setq file (expand-file-name "dir.info" truename)) (file-attributes file)) (progn (setq file (expand-file-name "DIR.INFO" truename)) + (file-attributes file)) + ;; Shouldn't really happen, but sometimes does, + ;; eg on Debian systems with buggy packages; + ;; so may as well try it. + ;; http://lists.gnu.org/archive/html/emacs-devel/2012-03/msg00005.html + (progn (setq file (expand-file-name "dir.gz" truename)) (file-attributes file))))) (setq dirs-done (cons truename @@ -1513,7 +1645,7 @@ escaped (\\\",\\\\)." ;; Add a new unique history item to full history list (let ((new-history (list Info-current-file Info-current-node))) (setq Info-history-list - (cons new-history (delete new-history Info-history-list))) + (cons new-history (remove new-history Info-history-list))) (setq Info-history-forward nil)) (if (not (eq Info-fontify-maximum-menu-size nil)) (Info-fontify-node)) @@ -1750,9 +1882,7 @@ If DIRECTION is `backward', search in the reverse direction." (while (and (not give-up) (or (null found) (not (funcall isearch-filter-predicate beg-found found)))) - (let ((search-spaces-regexp - (if (or (not isearch-mode) isearch-regexp) - Info-search-whitespace-regexp))) + (let ((search-spaces-regexp Info-search-whitespace-regexp)) (if (if backward (re-search-backward regexp bound t) (re-search-forward regexp bound t)) @@ -1765,17 +1895,17 @@ 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 (if (null Info-current-subfile) - (let ((search-spaces-regexp - (if (or (not isearch-mode) isearch-regexp) - Info-search-whitespace-regexp))) - (if backward - (re-search-backward regexp) - (re-search-forward regexp))) + (if isearch-mode + (signal 'search-failed (list regexp "end of manual")) + (let ((search-spaces-regexp Info-search-whitespace-regexp)) + (if backward + (re-search-backward regexp) + (re-search-forward regexp)))) (setq found nil))) (if (and bound (not found)) @@ -1830,9 +1960,7 @@ If DIRECTION is `backward', search in the reverse direction." (while (and (not give-up) (or (null found) (not (funcall isearch-filter-predicate beg-found found)))) - (let ((search-spaces-regexp - (if (or (not isearch-mode) isearch-regexp) - Info-search-whitespace-regexp))) + (let ((search-spaces-regexp Info-search-whitespace-regexp)) (if (if backward (re-search-backward regexp nil t) (re-search-forward regexp nil t)) @@ -1845,7 +1973,9 @@ If DIRECTION is `backward', search in the reverse direction." (setq list nil))) (if found (message "") - (signal 'search-failed (list regexp)))) + (signal 'search-failed (if isearch-mode + (list regexp "end of manual") + (list regexp))))) (if (not found) (progn (Info-read-subfile osubfile) (goto-char opoint) @@ -1898,26 +2028,28 @@ 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))) + (let ((Info-search-whitespace-regexp + (if (if isearch-regexp + isearch-regexp-lax-whitespace + isearch-lax-whitespace) + search-whitespace-regexp))) + (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 @@ -2003,8 +2135,8 @@ if ERRORNAME is nil, just return nil." (concat name ":" (Info-following-node-name-re)) bound t) (match-string-no-properties 1)) ((not (eq errorname t)) - (error "Node has no %s" - (capitalize (or errorname name))))))))) + (user-error "Node has no %s" + (capitalize (or errorname name))))))))) (defun Info-following-node-name-re (&optional allowedchars) "Return a regexp matching a node name. @@ -2014,7 +2146,7 @@ Submatch 1 is the complete node name. Submatch 2 if non-nil is the parenthesized file name part of the node name. Submatch 3 is the local part of the node name. End of submatch 0, 1, and 3 are the same, so you can safely concat." - (concat "[ \t]*" ;Skip leading space. + (concat "[ \t\n]*" ;Skip leading space. "\\(\\(([^)]+)\\)?" ;Node name can start with a file name. "\\([" (or allowedchars "^,\t\n") "]*" ;Any number of allowed chars. "[" (or allowedchars "^,\t\n") " ]" ;The last char can't be a space. @@ -2073,7 +2205,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." "Go back in the history to the last node visited." (interactive) (or Info-history - (error "This is the first Info node you looked at")) + (user-error "This is the first Info node you looked at")) (let ((history-forward (cons (list Info-current-file Info-current-node (point)) Info-history-forward)) @@ -2093,7 +2225,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." "Go forward in the history of visited nodes." (interactive) (or Info-history-forward - (error "This is the last Info node you looked at")) + (user-error "This is the last Info node you looked at")) (let ((history-forward (cdr Info-history-forward)) filename nodename opoint) (setq filename (car (car Info-history-forward))) @@ -2152,7 +2284,7 @@ If SAME-FILE is non-nil, do not move to a different Info file." (insert "Recently Visited Nodes\n") (insert "**********************\n\n") (insert "* Menu:\n\n") - (let ((hl (delete '("*History*" "Top") Info-history-list))) + (let ((hl (remove '("*History*" "Top") Info-history-list))) (while hl (let ((file (nth 0 (car hl))) (node (nth 1 (car hl)))) @@ -2248,7 +2380,7 @@ Table of contents is created from the tree structure of menus." (match-string-no-properties 1))) (section "Top") menu-items) - (when (string-match "(" upnode) (setq upnode nil)) + (when (and upnode (string-match "(" upnode)) (setq upnode nil)) (when (and (not (Info-index-node nodename file)) (re-search-forward "^\\* Menu:" bound t)) (forward-line 1) @@ -2295,13 +2427,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 @@ -2379,7 +2504,7 @@ new buffer." completions nil t))) (list (if (equal input "") default input) current-prefix-arg)) - (error "No cross-references in this node")))) + (user-error "No cross-references in this node")))) (unless footnotename (error "No reference was specified")) @@ -2410,7 +2535,8 @@ new buffer." (abs (- prev-ref (point)))) next-ref prev-ref)) ((or next-ref prev-ref)) - ((error "No cross-reference named %s" footnotename)))) + ((user-error "No cross-reference named %s" + footnotename)))) (setq target (Info-extract-menu-node-name t)))) (while (setq i (string-match "[ \t\n]+" target i)) (setq target (concat (substring target 0 i) " " @@ -2520,6 +2646,7 @@ Because of ambiguities, this should be concatenated with something like (while (re-search-forward pattern nil t) (push (match-string-no-properties 1) completions)) + (setq completions (delete-dups completions)) ;; Check subsequent nodes if applicable. (or (and Info-complete-next-re (setq nextnode (Info-extract-pointer "next" t)) @@ -2555,7 +2682,7 @@ new buffer." (save-excursion (goto-char (point-min)) (if (not (search-forward "\n* menu:" nil t)) - (error "No menu in this node")) + (user-error "No menu in this node")) (setq beg (point)) (and (< (point) p) (save-excursion @@ -2585,7 +2712,9 @@ new buffer." (list item current-prefix-arg)))) ;; there is a problem here in that if several menu items have the same ;; name you can only go to the node of the first with this command. - (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item))) + (Info-goto-node (Info-extract-menu-item menu-item) + (and fork + (if (stringp fork) fork menu-item)))) (defun Info-extract-menu-item (menu-item) (setq menu-item (regexp-quote menu-item)) @@ -2594,10 +2723,10 @@ new buffer." (let ((case-fold-search t)) (goto-char (point-min)) (or (search-forward "\n* menu:" nil t) - (error "No menu in this node")) + (user-error "No menu in this node")) (or (re-search-forward (concat "\n\\* +" menu-item ":") nil t) (re-search-forward (concat "\n\\* +" menu-item) nil t) - (error "No such item in menu")) + (user-error "No such item in menu")) (beginning-of-line) (forward-char 2) (Info-extract-menu-node-name nil (Info-index-node)))))) @@ -2613,7 +2742,7 @@ new buffer." (match-beginning 0)))) (goto-char (point-min)) (or (search-forward "\n* menu:" bound t) - (error "No menu in this node")) + (user-error "No menu in this node")) (if count (or (search-forward "\n* " bound t count) (error "Too few items in menu")) @@ -2685,7 +2814,7 @@ N is the digit argument used to invoke this command." (if Info-history-skip-intermediate-nodes (setq Info-history old-history))))) (no-error nil) - (t (error "No pointer forward from this node"))))) + (t (user-error "No pointer forward from this node"))))) (defun Info-backward-node () "Go backward one node, considering all nodes as forming one sequence." @@ -2694,7 +2823,7 @@ N is the digit argument used to invoke this command." (upnode (Info-extract-pointer "up" t)) (case-fold-search t)) (cond ((and upnode (string-match "(" upnode)) - (error "First node in file")) + (user-error "First node in file")) ((and upnode (or (null prevnode) ;; Use string-equal, not equal, ;; to ignore text properties. @@ -2712,7 +2841,7 @@ N is the digit argument used to invoke this command." (if Info-history-skip-intermediate-nodes (setq Info-history old-history)))) (t - (error "No pointer backward from this node"))))) + (user-error "No pointer backward from this node"))))) (defun Info-exit () "Exit Info by selecting some other buffer." @@ -2733,7 +2862,7 @@ N is the digit argument used to invoke this command." (and (search-forward "\n* " nil t) (Info-extract-menu-node-name))))) (if node (Info-goto-node node) - (error "No more items in menu")))) + (user-error "No more items in menu")))) (defun Info-last-menu-item () "Go to the node of the previous menu item." @@ -2746,13 +2875,13 @@ N is the digit argument used to invoke this command." (and (search-backward "\n* menu:" nil t) (point))))) (or (and beg (search-backward "\n* " beg t)) - (error "No previous items in menu"))) + (user-error "No previous items in menu"))) (Info-goto-node (save-excursion (goto-char (match-end 0)) (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." @@ -2771,7 +2900,7 @@ N is the digit argument used to invoke this command." (if Info-history-skip-intermediate-nodes (setq Info-history old-history)))) (t - (error "No more nodes")))) + (user-error "No more nodes")))) (defun Info-last-preorder () "Go to the last node, popping up a level if there is none." @@ -2811,7 +2940,7 @@ N is the digit argument used to invoke this command." (let ((case-fold-search t)) (or (search-forward "\n* Menu:" nil t) (goto-char (point-max))))) - (t (error "No previous nodes")))) + (t (user-error "No previous nodes")))) (defun Info-scroll-up () "Scroll one screenful forward in Info, considering all nodes as one sequence. @@ -2900,11 +3029,11 @@ See `Info-scroll-down'." (or (re-search-forward pat nil t) (progn (goto-char old-pt) - (error "No cross references in this node"))))) + (user-error "No cross references in this node"))))) (goto-char (or (match-beginning 1) (match-beginning 0))) (if (looking-at "\\* Menu:") (if recur - (error "No cross references in this node") + (user-error "No cross references in this node") (Info-next-reference t)) (if (looking-at "^\\* ") (forward-char 2))))) @@ -2921,19 +3050,15 @@ See `Info-scroll-down'." (or (re-search-backward pat nil t) (progn (goto-char old-pt) - (error "No cross references in this node"))))) + (user-error "No cross references in this node"))))) (goto-char (or (match-beginning 1) (match-beginning 0))) (if (looking-at "\\* Menu:") (if recur - (error "No cross references in this node") + (user-error "No cross references in this node") (Info-prev-reference t)) (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. @@ -3096,7 +3221,7 @@ Give an empty topic name to go to the Index node itself." (or matches (progn (Info-goto-node orignode) - (error "No `%s' in index" topic))) + (user-error "No `%s' in index" topic))) ;; Here it is a feature that assoc is case-sensitive. (while (setq found (assoc topic matches)) (setq exact (cons found exact) @@ -3109,7 +3234,7 @@ Give an empty topic name to go to the Index node itself." "Go to the next matching index item from the last \\\\[Info-index] command." (interactive "p") (or Info-index-alternatives - (error "No previous `i' command")) + (user-error "No previous `i' command")) (while (< num 0) (setq num (+ num (length Info-index-alternatives)))) (while (> num 0) @@ -3239,7 +3364,7 @@ search results." (Info-index topic) (push (cons (cons Info-current-file topic) Info-index-alternatives) Info-virtual-index-nodes) - ;; Clean up unneccessary side-effects of `Info-index'. + ;; Clean up unnecessary side-effects of `Info-index'. (setq Info-history-list ohist-list) (Info-goto-node orignode) (message ""))) @@ -3327,7 +3452,7 @@ Return a list of matches where each element is in the format (Info-directory) ;; current-node and current-file are nil when they invoke info-apropos ;; as the first Info command, i.e. info-apropos loads info.el. In that - ;; case, we use (DIR)Top instead, to avoid signalling an error after + ;; case, we use (DIR)Top instead, to avoid signaling an error after ;; the search is complete. (when (null current-node) (setq current-file Info-current-file) @@ -3402,7 +3527,7 @@ Build a menu of the possible matches." (declare-function finder-unknown-keywords "finder" ()) (declare-function lm-commentary "lisp-mnt" (&optional file)) (defvar finder-keywords-hash) -(defvar package-alist) ; finder requires package +(defvar package--builtins) ; finder requires package (defun Info-finder-find-node (_filename nodename &optional _no-going-back) "Finder-specific implementation of `Info-find-node-2'." @@ -3416,14 +3541,14 @@ Build a menu of the possible matches." (insert "***************\n\n") (insert "* Menu:\n\n") (dolist (assoc (append '((all . "All package info") - (unknown . "unknown keywords")) + (unknown . "Unknown keywords")) finder-known-keywords)) (let ((keyword (car assoc))) (insert (format "* %s %s.\n" (concat (symbol-name keyword) ": " - "kw:" (symbol-name keyword) ".") + "Keyword " (symbol-name keyword) ".") (cdr assoc)))))) - ((equal nodename "unknown") + ((equal nodename "Keyword unknown") ;; Display unknown keywords (insert (format "\n\^_\nFile: %s, Node: %s, Up: Top\n\n" Info-finder-file nodename)) @@ -3433,24 +3558,29 @@ Build a menu of the possible matches." (mapc (lambda (assoc) (insert (format "* %-14s %s.\n" - (concat (symbol-name (car assoc)) "::") + (concat (symbol-name (car assoc)) ": " + "Keyword " (symbol-name (car assoc)) ".") (cdr assoc)))) (finder-unknown-keywords))) - ((equal nodename "all") + ((equal nodename "Keyword all") ;; Display all package info. (insert (format "\n\^_\nFile: %s, Node: %s, Up: Top\n\n" Info-finder-file nodename)) (insert "Finder Package Info\n") (insert "*******************\n\n") - (dolist (package package-alist) - (insert (format "%s - %s\n" - (format "*Note %s::" (nth 0 package)) - (nth 1 package))))) - ((string-match "\\`kw:" nodename) + (insert "* Menu:\n\n") + (let (desc) + (dolist (package package--builtins) + (setq desc (cdr-safe package)) + (when (vectorp desc) + (insert (format "* %-16s %s.\n" + (concat (symbol-name (car package)) "::") + (aref desc 2))))))) + ((string-match "\\`Keyword " nodename) (setq nodename (substring nodename (match-end 0))) ;; Display packages that match the keyword ;; or the list of keywords separated by comma. - (insert (format "\n\^_\nFile: %s, Node: kw:%s, Up: Top\n\n" + (insert (format "\n\^_\nFile: %s, Node: Keyword %s, Up: Top\n\n" Info-finder-file nodename)) (insert "Finder Packages\n") (insert "***************\n\n") @@ -3462,11 +3592,11 @@ Build a menu of the possible matches." (split-string nodename ",[ \t\n]*" t) (list nodename)))) hits desc) - (dolist (kw keywords) - (push (copy-tree (gethash kw finder-keywords-hash)) hits)) + (dolist (keyword keywords) + (push (copy-tree (gethash keyword finder-keywords-hash)) hits)) (setq hits (delete-dups (apply 'append hits))) (dolist (package hits) - (setq desc (cdr-safe (assq package package-alist))) + (setq desc (cdr-safe (assq package package--builtins))) (when (vectorp desc) (insert (format "* %-16s %s.\n" (concat (symbol-name package) "::") @@ -3624,7 +3754,7 @@ If FORK is a string, it is the name to use for the new buffer." ;; Don't raise an error when mouse-1 is bound to this - it's ;; often used to simply select the window or frame. (eq 'mouse-1 (event-basic-type last-input-event))) - (error "Point neither on reference nor in menu item description"))) + (user-error "Point neither on reference nor in menu item description"))) ;; Common subroutine. (defun Info-try-follow-nearest-node (&optional fork) @@ -3660,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 @@ -3698,7 +3835,7 @@ If FORK is non-nil, it is passed to `Info-goto-node'." (define-key map "b" 'beginning-of-buffer) (put 'beginning-of-buffer :advertised-binding "b") (define-key map "d" 'Info-directory) - (define-key map "e" 'Info-edit) + (define-key map "e" 'end-of-buffer) (define-key map "f" 'Info-follow-reference) (define-key map "g" 'Info-goto-node) (define-key map "h" 'Info-help) @@ -3726,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.") @@ -3891,7 +4030,7 @@ The name of the Info file is prepended to the node name in parentheses. With a zero prefix arg, put the name inside a function call to `info'." (interactive "P") (unless Info-current-node - (error "No current Info node")) + (user-error "No current Info node")) (let ((node (if (stringp Info-current-file) (concat "(" (file-name-nondirectory Info-current-file) ") " Info-current-node)))) @@ -4018,8 +4157,6 @@ Advanced commands: 'Info-isearch-push-state) (set (make-local-variable 'isearch-filter-predicate) 'Info-isearch-filter) - (set (make-local-variable 'search-whitespace-regexp) - Info-search-whitespace-regexp) (set (make-local-variable 'revert-buffer-function) 'Info-revert-buffer-function) (Info-set-mode-line) @@ -4241,45 +4378,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)) @@ -4368,15 +4477,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))) @@ -4417,7 +4525,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)) @@ -4705,6 +4823,12 @@ the variable `Info-file-list-for-emacs'." mouse-face highlight help-echo "mouse-2: go to this URL")))) + ;; Hide empty lines at the end of the node. + (goto-char (point-max)) + (skip-chars-backward "\n") + (when (< (point) (1- (point-max))) + (put-text-property (point) (1- (point-max)) 'invisible t)) + (set-buffer-modified-p nil)))) ;;; Speedbar support: @@ -4883,25 +5007,8 @@ BUFFER is the buffer speedbar is requesting buttons for." (erase-buffer)) (Info-speedbar-hierarchy-buttons nil 0)) -(dolist (mess '("^First node in file$" - "^No `.*' in index$" - "^No cross-reference named" - "^No cross.references in this node$" - "^No current Info node$" - "^No menu in this node$" - "^No more items in menu$" - "^No more nodes$" - "^No pointer \\(?:forward\\|backward\\) from this node$" - "^No previous `i' command$" - "^No previous items in menu$" - "^No previous nodes$" - "^No such item in menu$" - "^No such node or anchor" - "^Node has no" - "^Point neither on reference nor in menu item description$" - "^This is the \\(?:first\\|last\\) Info node you looked at$" - search-failed)) - (add-to-list 'debug-ignored-errors mess)) +;; FIXME: Really? Why here? +(add-to-list 'debug-ignored-errors 'search-failed) ;;;; Desktop support @@ -4950,11 +5057,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)