;; info.el --- info package for Emacs
-;; Copyright (C) 1985-1986, 1992-2011 Free Software Foundation, Inc.
+;; Copyright (C) 1985-1986, 1992-2012 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: help
"List of all Info nodes user has visited.
Each element of the list is a list (FILENAME NODENAME).")
+(defcustom Info-history-skip-intermediate-nodes t
+ "Non-nil means don't record intermediate Info nodes to the history.
+Intermediate Info nodes are nodes visited by Info internally in the process of
+searching the node to display. Intermediate nodes are not presented
+to the user."
+ :type 'boolean
+ :group 'info
+ :version "24.1")
+
(defcustom Info-enable-edit nil
"Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
This is convenient if you want to write Info files by hand.
(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
it fails once with the error message [initial 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
+the rest of the manual, it wraps around the whole manual and
restarts the search from the top/final node depending on
search direction.
(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."
(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.
(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
(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))
(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))
;; 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))
(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
;; 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))
;; 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
+ (if (or (not isearch-mode) isearch-regexp)
+ Info-search-whitespace-regexp)))
+ (if backward
+ (re-search-backward regexp)
+ (re-search-forward regexp))))
(setq found nil)))
(if (and bound (not found))
(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)
(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.
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.
"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))
"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)))
(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))))
(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)
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"))
(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) " "
)
(replace-regexp-in-string
"[ \n]+" " "
- (or (match-string-no-properties 2)
+ (or (and (not (equal (match-string-no-properties 2) ""))
+ (match-string-no-properties 2))
;; If the node name is the menu entry name (using `entry::').
(buffer-substring-no-properties
(match-beginning 0) (1- (match-beginning 1)))))))
(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
(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))
(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))))))
(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"))
"top")))
(let ((old-node Info-current-node))
(Info-up)
- (let (Info-history success)
+ (let ((old-history Info-history)
+ success)
(unwind-protect
(setq success (Info-forward-node t nil no-error))
- (or success (Info-goto-node old-node))))))
+ (or success (Info-goto-node old-node)))
+ (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."
(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.
;; If we move back at the same level,
;; go down to find the last subnode*.
(Info-prev)
- (let (Info-history)
+ (let ((old-history Info-history))
(while (and (not (Info-index-node))
(save-excursion (search-forward "\n* Menu:" nil t)))
- (Info-goto-node (Info-extract-menu-counting nil)))))
+ (Info-goto-node (Info-extract-menu-counting nil)))
+ (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."
(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."
(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)))))
;; Since logically we are done with the node with that menu,
;; move on from it. But don't add intermediate nodes
;; to the history on recursive calls.
- (let (Info-history)
- (Info-next-preorder)))
+ (let ((old-history Info-history))
+ (Info-next-preorder)
+ (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."
(interactive)
- (cond ((Info-no-error
- (Info-last-menu-item)
- ;; If we go down a menu item, go to the end of the node
- ;; so we can scroll back through it.
- (goto-char (point-max)))
+ (cond ((and Info-scroll-prefer-subnodes
+ (Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max))))
;; Keep going down, as long as there are nested menu nodes.
- (while (Info-no-error
- (Info-last-menu-item)
- ;; If we go down a menu item, go to the end of the node
- ;; so we can scroll back through it.
- (goto-char (point-max))))
+ (let ((old-history Info-history))
+ (while (Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max))))
+ (if Info-history-skip-intermediate-nodes
+ (setq Info-history old-history)))
(recenter -1))
((and (Info-no-error (Info-extract-pointer "prev"))
(not (equal (Info-extract-pointer "up")
(Info-extract-pointer "prev"))))
(Info-no-error (Info-prev))
(goto-char (point-max))
- (while (Info-no-error
- (Info-last-menu-item)
- ;; If we go down a menu item, go to the end of the node
- ;; so we can scroll back through it.
- (goto-char (point-max))))
+ (let ((old-history Info-history))
+ (while (Info-no-error
+ (Info-last-menu-item)
+ ;; If we go down a menu item, go to the end of the node
+ ;; so we can scroll back through it.
+ (goto-char (point-max))))
+ (if Info-history-skip-intermediate-nodes
+ (setq Info-history old-history)))
(recenter -1))
((Info-no-error (Info-up t))
(goto-char (point-min))
(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.
(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)))))
(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)))))
(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)
"Go to the next matching index item from the last \\<Info-mode-map>\\[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)
(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 "")))
(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)
(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'."
(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))
(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")
(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) "::")
;; 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)
(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)
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))))
(defvar tool-bar-map)
(defvar bookmark-make-record-function)
+(defvar Info-mode-syntax-table
+ (let ((st (copy-syntax-table text-mode-syntax-table)))
+ ;; Use punctuation syntax for apostrophe because of
+ ;; extensive use of quotes like `this' in Info manuals.
+ (modify-syntax-entry ?' "." st)
+ st)
+ "Syntax table used in `Info-mode'.")
+
;; Autoload cookie needed by desktop.el
;;;###autoload
(define-derived-mode Info-mode nil "Info"
\\[clone-buffer] Select a new cloned Info buffer in another window.
\\[universal-argument] \\[info] Move to new Info file with completion.
\\[universal-argument] N \\[info] Select Info buffer with prefix number in the name *info*<N>."
- :syntax-table text-mode-syntax-table
+ :syntax-table Info-mode-syntax-table
:abbrev-table text-mode-abbrev-table
(setq tab-width 8)
(add-hook 'activate-menubar-hook 'Info-menu-update nil t)
(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