X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/093571c374d671607c822dd2e5bedb2ac877ea91..cb83c00bd13b63c0d0752698e4ad441968bc04ac:/lisp/info.el diff --git a/lisp/info.el b/lisp/info.el index 91305ee025..78e3f2ef70 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -9,10 +9,10 @@ ;; This file is part of GNU Emacs. -;; GNU Emacs is free software; you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,9 +20,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -36,7 +34,7 @@ ;;; Code: -(eval-when-compile (require 'jka-compr)) +(eval-when-compile (require 'jka-compr) (require 'cl)) (defgroup info nil "Info subsystem." @@ -223,7 +221,8 @@ when you hit the end of the current node." "*If non-nil, hide the tag and section reference in *note and * menu items. If value is non-nil but not `hide', also replaces the \"*note\" with \"see\". If value is non-nil but not t or `hide', the reference section is still shown. -`nil' completely disables this feature." +`nil' completely disables this feature. If this is non-nil, you might +want to set `Info-refill-paragraphs'." :version "22.1" :type '(choice (const :tag "No hiding" nil) (const :tag "Replace tag and hide reference" t) @@ -234,11 +233,17 @@ If value is non-nil but not t or `hide', the reference section is still shown. (defcustom Info-refill-paragraphs nil "*If non-nil, attempt to refill paragraphs with hidden references. This refilling may accidentally remove explicit line breaks in the Info -file, so be prepared for a few surprises if you enable this feature." +file, so be prepared for a few surprises if you enable this feature. +This only has an effect if `Info-hide-note-references' is non-nil." :version "22.1" :type 'boolean :group 'info) +(defcustom Info-breadcrumbs-depth 4 + "Depth of breadcrumbs to display. +0 means do not display breadcrumbs." + :type 'integer) + (defcustom Info-search-whitespace-regexp "\\s-+" "*If non-nil, regular expression to match a sequence of whitespace chars. This applies to Info search for regular expressions. @@ -307,6 +312,11 @@ Marker points nowhere if file has no tag table.") (defvar Info-file-supports-index-cookies nil "Non-nil if current Info file supports index cookies.") +(defvar Info-file-supports-index-cookies-list nil + "List of Info files with information about index cookies support. +Each element of the list is a list (FILENAME SUPPORTS-INDEX-COOKIES) +where SUPPORTS-INDEX-COOKIES can be either t or nil.") + (defvar Info-index-alternatives nil "List of possible matches for last `Info-index' command.") @@ -449,7 +459,7 @@ Do the right thing if the file has been compressed or zipped." (if decoder (progn (insert-file-contents-literally fullname visit) - (let ((buffer-read-only nil) + (let ((inhibit-read-only t) (coding-system-for-write 'no-conversion) (default-directory (or (file-name-directory fullname) default-directory))) @@ -458,6 +468,34 @@ 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)))) (insert-file-contents fullname visit)))) + +(defun Info-file-supports-index-cookies (&optional file) + "Return non-nil value if FILE supports Info index cookies. +Info index cookies were first introduced in 4.7, and all later +makeinfo versions output them in index nodes, so we can rely +solely on the makeinfo version. This function caches the information +in `Info-file-supports-index-cookies-list'." + (or file (setq file Info-current-file)) + (or (assoc file Info-file-supports-index-cookies-list) + ;; Skip virtual Info files + (and (or (not (stringp file)) + (member file '("dir" apropos history toc))) + (setq Info-file-supports-index-cookies-list + (cons (cons file nil) Info-file-supports-index-cookies-list))) + (save-excursion + (let ((found nil)) + (goto-char (point-min)) + (condition-case () + (if (and (re-search-forward + "makeinfo[ \n]version[ \n]\\([0-9]+.[0-9]+\\)" + (line-beginning-position 3) t) + (not (version< (match-string 1) "4.7"))) + (setq found t)) + (error nil)) + (setq Info-file-supports-index-cookies-list + (cons (cons file found) Info-file-supports-index-cookies-list))))) + (cdr (assoc file Info-file-supports-index-cookies-list))) + (defun Info-default-dirs () (let ((source (expand-file-name "info/" source-directory)) @@ -545,7 +583,9 @@ appended to the Info buffer name. The search path for Info files is in the variable `Info-directory-list'. The top-level Info directory is made by combining all the files named `dir' -in all the directories in that path." +in all the directories in that path. + +See a list of available Info commands in `Info-mode'." (interactive (list (if (and current-prefix-arg (not (numberp current-prefix-arg))) (read-file-name "Info file name: " nil nil t)) @@ -683,9 +723,8 @@ it says do not attempt further (recursive) error recovery." ;; Record the node we are leaving, if we were in one. (and (not no-going-back) Info-current-file - (setq Info-history - (cons (list Info-current-file Info-current-node (point)) - Info-history))) + (push (list Info-current-file Info-current-node (point)) + Info-history)) (Info-find-node-2 filename nodename no-going-back)) ;;;###autoload @@ -756,8 +795,7 @@ FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position where the match was found, and MODE is `major-mode' of the buffer in which the match was found." (let ((case-fold-search case-fold)) - (save-excursion - (set-buffer (marker-buffer marker)) + (with-current-buffer (marker-buffer marker) (goto-char marker) ;; Search tag table @@ -826,7 +864,7 @@ a case-insensitive match is tried." ;; Switch files if necessary (or (null filename) (equal Info-current-file filename) - (let ((buffer-read-only nil)) + (let ((inhibit-read-only t)) (setq Info-current-file nil Info-current-subfile nil Info-current-file-completions nil @@ -845,18 +883,8 @@ a case-insensitive match is tried." (info-insert-file-contents filename nil) (setq default-directory (file-name-directory filename)))) (set-buffer-modified-p nil) - - ;; Check makeinfo version for index cookie support - (let ((found nil)) - (goto-char (point-min)) - (condition-case () - (if (and (re-search-forward - "makeinfo[ \n]version[ \n]\\([0-9]+.[0-9]+\\)" - (line-beginning-position 3) t) - (not (version< (match-string 1) "4.7"))) - (setq found t)) - (error nil)) - (set (make-local-variable 'Info-file-supports-index-cookies) found)) + (set (make-local-variable 'Info-file-supports-index-cookies) + (Info-file-supports-index-cookies filename)) ;; See whether file has a tag table. Record the location if yes. (goto-char (point-max)) @@ -880,8 +908,7 @@ a case-insensitive match is tried." (or Info-tag-table-buffer (generate-new-buffer " *info tag table*")))) (setq Info-tag-table-buffer tagbuf) - (save-excursion - (set-buffer tagbuf) + (with-current-buffer tagbuf (buffer-disable-undo (current-buffer)) (setq case-fold-search t) (erase-buffer) @@ -967,6 +994,10 @@ a case-insensitive match is tried." (Info-select-node) (goto-char (point-min)) + (forward-line 1) ; skip header line + (when (> Info-breadcrumbs-depth 0) ; skip breadcrumbs line + (forward-line 1)) + (cond (anchorpos (let ((new-history (list Info-current-file (substring-no-properties nodename)))) @@ -1059,10 +1090,9 @@ a case-insensitive match is tried." (cons (directory-file-name truename) dirs-done))) (if attrs - (save-excursion + (with-current-buffer (generate-new-buffer " info dir") (or buffers (message "Composing main Info directory...")) - (set-buffer (generate-new-buffer " info dir")) (condition-case nil (progn (insert-file-contents file) @@ -1215,19 +1245,20 @@ a case-insensitive match is tried." (delete-region (1- (point)) (point)))) ;; Now remove duplicate entries under the same heading. - (let ((seen nil) - (limit (point-marker))) - (goto-char start) - (while (and (> limit (point)) - (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)" - limit 'move)) - ;; Fold case straight away; `member-ignore-case' here wasteful. - (let ((x (downcase (match-string 1)))) - (if (member x seen) - (delete-region (match-beginning 0) - (progn (re-search-forward "^[^ \t]" nil t) - (match-beginning 0))) - (push x seen)))))))))) + (let (seen) + (save-restriction + (narrow-to-region start (point)) + (goto-char (point-min)) + (while (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)" nil 'move) + ;; Fold case straight away; `member-ignore-case' here wasteful. + (let ((x (downcase (match-string 1)))) + (if (member x seen) + (delete-region + (match-beginning 0) + (if (re-search-forward "^[^ \t]" nil 'move) + (goto-char (match-beginning 0)) + (point-max))) + (push x seen))))))))))) ;; Note that on entry to this function the current-buffer must be the ;; *info* buffer; not the info tags buffer. @@ -1237,8 +1268,7 @@ a case-insensitive match is tried." (let (lastfilepos lastfilename) (if (numberp nodepos) - (save-excursion - (set-buffer (marker-buffer Info-tag-table-marker)) + (with-current-buffer (marker-buffer Info-tag-table-marker) (goto-char (point-min)) (or (looking-at "\^_") (search-forward "\n\^_")) @@ -1264,7 +1294,7 @@ a case-insensitive match is tried." ;; Assume previous buffer is in Info-mode. ;; (set-buffer (get-buffer "*info*")) (or (equal Info-current-subfile lastfilename) - (let ((buffer-read-only nil)) + (let ((inhibit-read-only t)) (setq buffer-file-name nil) (widen) (erase-buffer) @@ -1469,17 +1499,15 @@ If FORK is a string, it is the name to use for the new buffer." (defvar Info-read-node-completion-table) -(defun Info-read-node-name-2 (string path-and-suffixes action) +(defun Info-read-node-name-2 (dirs suffixes string pred action) "Virtual completion table for file names input in Info node names. PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)." - (let* ((names nil) - (suffixes (remove "" (cdr path-and-suffixes))) - (suffix (concat (regexp-opt suffixes t) "\\'")) - (string-dir (file-name-directory string)) - (dirs - (if (file-name-absolute-p string) - (list (file-name-directory string)) - (car path-and-suffixes)))) + (setq suffixes (remove "" suffixes)) + (when (file-name-absolute-p string) + (setq dirs (list (file-name-directory string)))) + (let ((names nil) + (suffix (concat (regexp-opt suffixes t) "\\'")) + (string-dir (file-name-directory string))) (dolist (dir dirs) (unless dir (setq dir default-directory)) @@ -1501,10 +1529,7 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)." (when (string-match suffix file) (setq file (substring file 0 (match-beginning 0))) (push (if string-dir (concat string-dir file) file) names))))) - (cond - ((eq action t) (all-completions string names)) - ((null action) (try-completion string names)) - (t (test-completion string names))))) + (complete-with-action action names string pred))) ;; This function is used as the "completion table" while reading a node name. ;; It does completion using the alist in Info-read-node-completion-table @@ -1513,20 +1538,16 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)." (cond ;; First complete embedded file names. ((string-match "\\`([^)]*\\'" string) - (let ((file (substring string 1))) - (cond - ((eq code nil) - (let ((comp (try-completion file 'Info-read-node-name-2 - (cons Info-directory-list - (mapcar 'car Info-suffix-list))))) - (cond - ((eq comp t) (concat string ")")) - (comp (concat "(" comp))))) - ((eq code t) - (all-completions file 'Info-read-node-name-2 - (cons Info-directory-list - (mapcar 'car Info-suffix-list)))) - (t nil)))) + (completion-table-with-context + "(" + (apply-partially 'completion-table-with-terminator ")" + (apply-partially 'Info-read-node-name-2 + Info-directory-list + (mapcar 'car Info-suffix-list))) + (substring string 1) + predicate + code)) + ;; If a file name was given, then any node is fair game. ((string-match "\\`(" string) (cond @@ -1534,29 +1555,18 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)." ((eq code t) nil) (t t))) ;; Otherwise use Info-read-node-completion-table. - ((eq code nil) - (try-completion string Info-read-node-completion-table predicate)) - ((eq code t) - (all-completions string Info-read-node-completion-table predicate)) - (t - (test-completion string Info-read-node-completion-table predicate)))) + (t (complete-with-action + code Info-read-node-completion-table string predicate)))) ;; Arrange to highlight the proper letters in the completion list buffer. -(put 'Info-read-node-name-1 'completion-base-size-function - (lambda () - (if (string-match "\\`([^)]*\\'" - (or completion-common-substring - (minibuffer-completion-contents))) - 1 - 0))) - -(defun Info-read-node-name (prompt &optional default) + + +(defun Info-read-node-name (prompt) (let* ((completion-ignore-case t) (Info-read-node-completion-table (Info-build-node-completions)) (nodename (completing-read prompt 'Info-read-node-name-1 nil t))) (if (equal nodename "") - (or default - (Info-read-node-name prompt)) + (Info-read-node-name prompt) nodename))) (defun Info-build-node-completions () @@ -1649,20 +1659,8 @@ If DIRECTION is `backward', search in the reverse direction." (1- (point))) (point-max))) (while (and (not give-up) - (save-match-data - (or (null found) - (if backward - (isearch-range-invisible found beg-found) - (isearch-range-invisible beg-found found)) - ;; Skip node header line - (and (save-excursion (forward-line -1) - (looking-at "\^_")) - (forward-line (if backward -1 1))) - ;; Skip Tag Table node - (save-excursion - (and (search-backward "\^_" nil t) - (looking-at - "\^_\n\\(Tag Table\\|Local Variables\\)")))))) + (or (null found) + (not (funcall isearch-success-function beg-found found)))) (let ((search-spaces-regexp (if (or (not isearch-mode) isearch-regexp) Info-search-whitespace-regexp))) @@ -1698,8 +1696,7 @@ If DIRECTION is `backward', search in the reverse direction." (unwind-protect ;; Try other subfiles. (let ((list ())) - (save-excursion - (set-buffer (marker-buffer Info-tag-table-marker)) + (with-current-buffer (marker-buffer Info-tag-table-marker) (goto-char (point-min)) (search-forward "\n\^_\nIndirect:") (save-restriction @@ -1742,20 +1739,8 @@ If DIRECTION is `backward', search in the reverse direction." (setq list (cdr list)) (setq give-up nil found nil) (while (and (not give-up) - (save-match-data - (or (null found) - (if backward - (isearch-range-invisible found beg-found) - (isearch-range-invisible beg-found found)) - ;; Skip node header line - (and (save-excursion (forward-line -1) - (looking-at "\^_")) - (forward-line (if backward -1 1))) - ;; Skip Tag Table node - (save-excursion - (and (search-backward "\^_" nil t) - (looking-at - "\^_\n\\(Tag Table\\|Local Variables\\)")))))) + (or (null found) + (not (funcall isearch-success-function beg-found found)))) (let ((search-spaces-regexp (if (or (not isearch-mode) isearch-regexp) Info-search-whitespace-regexp))) @@ -1835,8 +1820,8 @@ If DIRECTION is `backward', search in the reverse direction." (unless isearch-forward 'backward)) (Info-search (if isearch-regexp string (regexp-quote string)) bound noerror count - (unless isearch-forward 'backward)) - (point))) + (unless isearch-forward 'backward))) + (point)) (let ((isearch-search-fun-function nil)) (isearch-search-fun)))) @@ -1861,6 +1846,28 @@ If DIRECTION is `backward', search in the reverse direction." (defun Info-isearch-start () (setq Info-isearch-initial-node nil)) + +(defun Info-search-success-function (beg-found found) + "Skip invisible text, node header line and Tag Table node." + (save-match-data + (let ((backward (< found beg-found))) + (not + (or + (if backward + (or (text-property-not-all found beg-found 'invisible nil) + (text-property-not-all found beg-found 'display nil)) + (or (text-property-not-all beg-found found 'invisible nil) + (text-property-not-all beg-found found 'display nil))) + ;; Skip node header line + (and (save-excursion (forward-line -1) + (looking-at "\^_")) + (forward-line (if backward -1 1))) + ;; Skip Tag Table node + (save-excursion + (and (search-backward "\^_" nil t) + (looking-at + "\^_\n\\(Tag Table\\|Local Variables\\)")))))))) + (defun Info-extract-pointer (name &optional errorname) "Extract the value of the node-pointer named NAME. @@ -2021,14 +2028,14 @@ Table of contents is created from the tree structure of menus." p) (with-current-buffer (get-buffer-create " *info-toc*") (let ((inhibit-read-only t) - (node-list (Info-build-toc curr-file))) + (node-list (Info-toc-nodes curr-file))) (erase-buffer) (goto-char (point-min)) (insert "\n\^_\nFile: toc, Node: Top, Up: (dir)\n\n") (insert "Table of Contents\n*****************\n\n") (insert "*Note Top: (" curr-file ")Top.\n") (Info-insert-toc - (nth 2 (assoc "Top" node-list)) ; get Top nodes + (nth 3 (assoc "Top" node-list)) ; get Top nodes node-list 0 curr-file)) (if (not (bobp)) (let ((Info-hide-note-references 'hide) @@ -2052,11 +2059,11 @@ Table of contents is created from the tree structure of menus." (let ((section "Top")) (while nodes (let ((node (assoc (car nodes) node-list))) - (unless (member (nth 1 node) (list nil section)) - (insert (setq section (nth 1 node)) "\n")) + (unless (member (nth 2 node) (list nil section)) + (insert (setq section (nth 2 node)) "\n")) (insert (make-string level ?\t)) (insert "*Note " (car nodes) ": (" curr-file ")" (car nodes) ".\n") - (Info-insert-toc (nth 2 node) node-list (1+ level) curr-file) + (Info-insert-toc (nth 3 node) node-list (1+ level) curr-file) (setq nodes (cdr nodes)))))) (defun Info-build-toc (file) @@ -2070,17 +2077,22 @@ Table of contents is created from the tree structure of menus." (sections '(("Top" "Top"))) nodes subfiles) (while (or main-file subfiles) - (or main-file (message "Searching subfile %s..." (car subfiles))) + ;; (or main-file (message "Searching subfile %s..." (car subfiles))) (erase-buffer) (info-insert-file-contents (or main-file (car subfiles))) (goto-char (point-min)) (while (and (search-forward "\n\^_\nFile:" nil 'move) (search-forward "Node: " nil 'move)) - (let ((nodename (substring-no-properties (Info-following-node-name))) - (bound (- (or (save-excursion (search-forward "\n\^_" nil t)) - (point-max)) 2)) - (section "Top") - menu-items) + (let* ((nodename (substring-no-properties (Info-following-node-name))) + (bound (- (or (save-excursion (search-forward "\n\^_" nil t)) + (point-max)) 2)) + (upnode (and (re-search-forward + (concat "Up:" (Info-following-node-name-re)) + bound t) + (match-string-no-properties 1))) + (section "Top") + menu-items) + (when (string-match "(" upnode) (setq upnode nil)) (when (and (not (Info-index-node nodename file)) (re-search-forward "^\\* Menu:" bound t)) (forward-line 1) @@ -2108,7 +2120,7 @@ Table of contents is created from the tree structure of menus." (setq section (match-string-no-properties 1)))) (forward-line 1) (beginning-of-line))) - (setq nodes (cons (list nodename + (setq nodes (cons (list nodename upnode (cadr (assoc nodename sections)) (nreverse menu-items)) nodes)) @@ -2126,6 +2138,32 @@ Table of contents is created from the tree structure of menus." (setq subfiles (cdr subfiles)))) (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 (file) + "Return a node list of Info FILE with parent-children information. +This information is cached in the variable `Info-toc-nodes' with the help +of the function `Info-build-toc'." + (or file (setq file Info-current-file)) + (or (assoc file Info-toc-nodes) + ;; Skip virtual Info files + (and (or (not (stringp file)) + (member file '("dir" apropos history toc))) + (push (cons file nil) Info-toc-nodes)) + ;; Scan the entire manual and cache the result in Info-toc-nodes + (let ((nodes (Info-build-toc file))) + (push (cons file nodes) Info-toc-nodes) + nodes) + ;; If there is an error, still add nil to the cache + (push (cons file nil) Info-toc-nodes)) + (cdr (assoc file Info-toc-nodes))) + (defun Info-follow-reference (footnotename &optional fork) "Follow cross reference named FOOTNOTENAME to the node it refers to. @@ -2287,57 +2325,57 @@ Because of ambiguities, this should be concatenated with something like ;; Note that `Info-complete-menu-buffer' could be current already, ;; so we want to save point. - (save-excursion - (set-buffer Info-complete-menu-buffer) - (let ((completion-ignore-case t) - (case-fold-search t) - (orignode Info-current-node) - nextnode) - (goto-char (point-min)) - (search-forward "\n* Menu:") - (if (not (memq action '(nil t))) - (re-search-forward - (concat "\n\\* +" (regexp-quote string) ":") nil t) - (let ((pattern (concat "\n\\* +\\(" - (regexp-quote string) - Info-menu-entry-name-re "\\):" Info-node-spec-re)) - completions - (complete-nodes Info-complete-nodes)) - ;; Check the cache. - (if (and (equal (nth 0 Info-complete-cache) Info-current-file) - (equal (nth 1 Info-complete-cache) Info-current-node) - (equal (nth 2 Info-complete-cache) Info-complete-next-re) - (equal (nth 5 Info-complete-cache) Info-complete-nodes) - (let ((prev (nth 3 Info-complete-cache))) - (eq t (compare-strings string 0 (length prev) - prev 0 nil t)))) - ;; We can reuse the previous list. - (setq completions (nth 4 Info-complete-cache)) - ;; The cache can't be used. - (while - (progn - (while (re-search-forward pattern nil t) - (push (match-string-no-properties 1) - completions)) - ;; Check subsequent nodes if applicable. - (or (and Info-complete-next-re - (setq nextnode (Info-extract-pointer "next" t)) - (string-match Info-complete-next-re nextnode)) - (and complete-nodes - (setq complete-nodes (cdr complete-nodes) - nextnode (car complete-nodes))))) - (Info-goto-node nextnode)) - ;; Go back to the start node (for the next completion). - (unless (equal Info-current-node orignode) - (Info-goto-node orignode)) - ;; Update the cache. - (set (make-local-variable 'Info-complete-cache) - (list Info-current-file Info-current-node - Info-complete-next-re string completions - Info-complete-nodes))) - (if action - (all-completions string completions predicate) - (try-completion string completions predicate))))))) + (with-current-buffer Info-complete-menu-buffer + (save-excursion + (let ((completion-ignore-case t) + (case-fold-search t) + (orignode Info-current-node) + nextnode) + (goto-char (point-min)) + (search-forward "\n* Menu:") + (if (not (memq action '(nil t))) + (re-search-forward + (concat "\n\\* +" (regexp-quote string) ":") nil t) + (let ((pattern (concat "\n\\* +\\(" + (regexp-quote string) + Info-menu-entry-name-re "\\):" Info-node-spec-re)) + completions + (complete-nodes Info-complete-nodes)) + ;; Check the cache. + (if (and (equal (nth 0 Info-complete-cache) Info-current-file) + (equal (nth 1 Info-complete-cache) Info-current-node) + (equal (nth 2 Info-complete-cache) Info-complete-next-re) + (equal (nth 5 Info-complete-cache) Info-complete-nodes) + (let ((prev (nth 3 Info-complete-cache))) + (eq t (compare-strings string 0 (length prev) + prev 0 nil t)))) + ;; We can reuse the previous list. + (setq completions (nth 4 Info-complete-cache)) + ;; The cache can't be used. + (while + (progn + (while (re-search-forward pattern nil t) + (push (match-string-no-properties 1) + completions)) + ;; Check subsequent nodes if applicable. + (or (and Info-complete-next-re + (setq nextnode (Info-extract-pointer "next" t)) + (string-match Info-complete-next-re nextnode)) + (and complete-nodes + (setq complete-nodes (cdr complete-nodes) + nextnode (car complete-nodes))))) + (Info-goto-node nextnode)) + ;; Go back to the start node (for the next completion). + (unless (equal Info-current-node orignode) + (Info-goto-node orignode)) + ;; Update the cache. + (set (make-local-variable 'Info-complete-cache) + (list Info-current-file Info-current-node + Info-complete-next-re string completions + Info-complete-nodes))) + (if action + (all-completions string completions predicate) + (try-completion string completions predicate)))))))) (defun Info-menu (menu-item &optional fork) @@ -2406,17 +2444,21 @@ new buffer." (Info-extract-menu-node-name nil (Info-index-node)))))) ;; If COUNT is nil, use the last item in the menu. -(defun Info-extract-menu-counting (count) +(defun Info-extract-menu-counting (count &optional no-detail) (let ((case-fold-search t)) (save-excursion - (let ((case-fold-search t)) + (let ((case-fold-search t) + (bound (when (and no-detail + (re-search-forward + "^[ \t-]*The Detailed Node Listing" nil t)) + (match-beginning 0)))) (goto-char (point-min)) - (or (search-forward "\n* menu:" nil t) + (or (search-forward "\n* menu:" bound t) (error "No menu in this node")) (if count - (or (search-forward "\n* " nil t count) + (or (search-forward "\n* " bound t count) (error "Too few items in menu")) - (while (search-forward "\n* " nil t) + (while (search-forward "\n* " bound t) nil)) (Info-extract-menu-node-name nil (Info-index-node)))))) @@ -2439,17 +2481,19 @@ N is the digit argument used to invoke this command." (Info-goto-node "Top") (let ((Info-history nil) (case-fold-search t)) - ;; Go to the last node in the menu of Top. - (Info-goto-node (Info-extract-menu-counting nil)) + ;; Go to the last node in the menu of Top. But don't delve into + ;; detailed node listings. + (Info-goto-node (Info-extract-menu-counting nil t)) ;; If the last node in the menu is not last in pointer structure, - ;; move forward until we can't go any farther. - (while (Info-forward-node t t) nil) + ;; move forward (but not down- or upward - see bug#1116) until we + ;; can't go any farther. + (while (Info-forward-node t t t) nil) ;; Then keep moving down to last subnode, unless we reach an index. (while (and (not (Info-index-node)) (save-excursion (search-forward "\n* Menu:" nil t))) (Info-goto-node (Info-extract-menu-counting nil))))) -(defun Info-forward-node (&optional not-down no-error) +(defun Info-forward-node (&optional not-down not-up no-error) "Go forward one node, considering all nodes as forming one sequence." (interactive) (goto-char (point-min)) @@ -2467,7 +2511,8 @@ N is the digit argument used to invoke this command." ((save-excursion (search-backward "next:" nil t)) (Info-next) t) - ((and (save-excursion (search-backward "up:" nil t)) + ((and (not not-up) + (save-excursion (search-backward "up:" nil t)) ;; Use string-equal, not equal, to ignore text props. (not (string-equal (downcase (Info-extract-pointer "up")) "top"))) @@ -2475,7 +2520,7 @@ N is the digit argument used to invoke this command." (Info-up) (let (Info-history success) (unwind-protect - (setq success (Info-forward-node t no-error)) + (setq success (Info-forward-node t nil no-error)) (or success (Info-goto-node old-node)))))) (no-error nil) (t (error "No pointer forward from this node"))))) @@ -2555,8 +2600,10 @@ N is the digit argument used to invoke this command." ;; go up to the end of this node. (goto-char (point-max)) ;; Since logically we are done with the node with that menu, - ;; move on from it. - (Info-next-preorder)) + ;; move on from it. But don't add intermediate nodes + ;; to the history on recursive calls. + (let (Info-history) + (Info-next-preorder))) (t (error "No more nodes")))) @@ -2673,7 +2720,7 @@ See `Info-scroll-down'." (defun Info-next-reference (&optional recur) "Move cursor to the next cross-reference or menu item in the node." (interactive) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tp://") + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") (old-pt (point)) (case-fold-search t)) (or (eobp) (forward-char 1)) @@ -2695,7 +2742,7 @@ See `Info-scroll-down'." (defun Info-prev-reference (&optional recur) "Move cursor to the previous cross-reference or menu item in the node." (interactive) - (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tp://") + (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://") (old-pt (point)) (case-fold-search t)) (or (re-search-backward pat nil t) @@ -2728,10 +2775,10 @@ following nodes whose names also contain the word \"Index\"." (or file (setq file Info-current-file)) (or (assoc file Info-index-nodes) ;; Skip virtual Info files - (and (member file '("dir" apropos history toc)) + (and (or (not (stringp file)) + (member file '("dir" apropos history toc))) (setq Info-index-nodes (cons (cons file nil) Info-index-nodes))) - (not (stringp file)) - (if Info-file-supports-index-cookies + (if (Info-file-supports-index-cookies file) ;; Find nodes with index cookie (let* ((default-directory (or (and (stringp file) (file-name-directory @@ -2768,7 +2815,7 @@ following nodes whose names also contain the word \"Index\"." nodes) ;; Else find nodes with the word "Index" in the node name (let ((case-fold-search t) - Info-history Info-history-list Info-fontify-maximum-menu-size + Info-history Info-history-list Info-fontify-maximum-menu-size Info-point-loc nodes node) (condition-case nil (with-temp-buffer @@ -2796,12 +2843,13 @@ following nodes whose names also contain the word \"Index\"." "Return non-nil value if NODE is an index node. If NODE is nil, check the current Info node. If FILE is nil, check the current Info file." + (or file (setq file Info-current-file)) (if (or (and node (not (equal node Info-current-node))) - (assoc (or file Info-current-file) Info-index-nodes)) + (assoc file Info-index-nodes)) (member (or node Info-current-node) (Info-index-nodes file)) ;; Don't search all index nodes if request is only for the current node ;; and file is not in the cache of index nodes - (if Info-file-supports-index-cookies + (if (Info-file-supports-index-cookies file) (save-excursion (goto-char (+ (or (save-excursion (search-backward "\n\^_" nil t)) @@ -3091,7 +3139,7 @@ Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \ At end of the node's text, moves to the next node, or up if none." (interactive "e") (mouse-set-point click) - (and (not (Info-try-follow-nearest-node)) + (and (not (Info-follow-nearest-node)) (save-excursion (forward-line 1) (eobp)) (Info-next-preorder))) @@ -3115,15 +3163,19 @@ If FORK is a string, it is the name to use for the new buffer." (Info-goto-node (Info-extract-menu-item (match-string-no-properties 1)) fork) t))) + (and (eq this-command 'Info-mouse-follow-nearest-node) + ;; 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"))) ;; Common subroutine. (defun Info-try-follow-nearest-node (&optional fork) "Follow a node reference near point. Return non-nil if successful. -If FORK is non-nil, it i spassed to `Info-goto-node'." +If FORK is non-nil, it is passed to `Info-goto-node'." (let (node) (cond - ((Info-get-token (point) "[hf]t?tp://" "[hf]t?tp://\\([^ \t\n\"`({<>})']+\\)") + ((Info-get-token (point) "[hf]t?tps?://" "[hf]t?tps?://\\([^ \t\n\"`({<>})']+\\)") (setq node t) (browse-url (browse-url-url-at-point))) ((setq node (Info-get-token (point) "\\*note[ \n\t]+" @@ -3148,66 +3200,65 @@ If FORK is non-nil, it i spassed to `Info-goto-node'." (Info-goto-node node fork))) node)) -(defvar Info-mode-map nil +(defvar Info-mode-map + (let ((map (make-keymap))) + (suppress-keymap map) + (define-key map "." 'beginning-of-buffer) + (define-key map " " 'Info-scroll-up) + (define-key map "\C-m" 'Info-follow-nearest-node) + (define-key map "\t" 'Info-next-reference) + (define-key map "\e\t" 'Info-prev-reference) + (define-key map [(shift tab)] 'Info-prev-reference) + (define-key map [backtab] 'Info-prev-reference) + (define-key map "1" 'Info-nth-menu-item) + (define-key map "2" 'Info-nth-menu-item) + (define-key map "3" 'Info-nth-menu-item) + (define-key map "4" 'Info-nth-menu-item) + (define-key map "5" 'Info-nth-menu-item) + (define-key map "6" 'Info-nth-menu-item) + (define-key map "7" 'Info-nth-menu-item) + (define-key map "8" 'Info-nth-menu-item) + (define-key map "9" 'Info-nth-menu-item) + (define-key map "0" 'undefined) + (define-key map "?" 'Info-summary) + (define-key map "]" 'Info-forward-node) + (define-key map "[" 'Info-backward-node) + (define-key map "<" 'Info-top-node) + (define-key map ">" 'Info-final-node) + (define-key map "b" 'beginning-of-buffer) + (define-key map "d" 'Info-directory) + (define-key map "e" 'Info-edit) + (define-key map "f" 'Info-follow-reference) + (define-key map "g" 'Info-goto-node) + (define-key map "h" 'Info-help) + (define-key map "i" 'Info-index) + (define-key map "l" 'Info-history-back) + (define-key map "L" 'Info-history) + (define-key map "m" 'Info-menu) + (define-key map "n" 'Info-next) + (define-key map "p" 'Info-prev) + (define-key map "q" 'Info-exit) + (define-key map "r" 'Info-history-forward) + (define-key map "s" 'Info-search) + (define-key map "S" 'Info-search-case-sensitively) + ;; For consistency with Rmail. + (define-key map "\M-s" 'Info-search) + (define-key map "\M-n" 'clone-buffer) + (define-key map "t" 'Info-top-node) + (define-key map "T" 'Info-toc) + (define-key map "u" 'Info-up) + ;; `w' for consistency with `dired-copy-filename-as-kill'. + (define-key map "w" 'Info-copy-current-node-name) + (define-key map "c" 'Info-copy-current-node-name) + ;; `^' for consistency with `dired-up-directory'. + (define-key map "^" 'Info-up) + (define-key map "," 'Info-index-next) + (define-key map "\177" 'Info-scroll-down) + (define-key map [mouse-2] 'Info-mouse-follow-nearest-node) + (define-key map [follow-link] 'mouse-face) + map) "Keymap containing Info commands.") -(if Info-mode-map - nil - (setq Info-mode-map (make-keymap)) - (suppress-keymap Info-mode-map) - (define-key Info-mode-map "." 'beginning-of-buffer) - (define-key Info-mode-map " " 'Info-scroll-up) - (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node) - (define-key Info-mode-map "\t" 'Info-next-reference) - (define-key Info-mode-map "\e\t" 'Info-prev-reference) - (define-key Info-mode-map [(shift tab)] 'Info-prev-reference) - (define-key Info-mode-map [backtab] 'Info-prev-reference) - (define-key Info-mode-map "1" 'Info-nth-menu-item) - (define-key Info-mode-map "2" 'Info-nth-menu-item) - (define-key Info-mode-map "3" 'Info-nth-menu-item) - (define-key Info-mode-map "4" 'Info-nth-menu-item) - (define-key Info-mode-map "5" 'Info-nth-menu-item) - (define-key Info-mode-map "6" 'Info-nth-menu-item) - (define-key Info-mode-map "7" 'Info-nth-menu-item) - (define-key Info-mode-map "8" 'Info-nth-menu-item) - (define-key Info-mode-map "9" 'Info-nth-menu-item) - (define-key Info-mode-map "0" 'undefined) - (define-key Info-mode-map "?" 'Info-summary) - (define-key Info-mode-map "]" 'Info-forward-node) - (define-key Info-mode-map "[" 'Info-backward-node) - (define-key Info-mode-map "<" 'Info-top-node) - (define-key Info-mode-map ">" 'Info-final-node) - (define-key Info-mode-map "b" 'beginning-of-buffer) - (define-key Info-mode-map "d" 'Info-directory) - (define-key Info-mode-map "e" 'Info-edit) - (define-key Info-mode-map "f" 'Info-follow-reference) - (define-key Info-mode-map "g" 'Info-goto-node) - (define-key Info-mode-map "h" 'Info-help) - (define-key Info-mode-map "i" 'Info-index) - (define-key Info-mode-map "l" 'Info-history-back) - (define-key Info-mode-map "L" 'Info-history) - (define-key Info-mode-map "m" 'Info-menu) - (define-key Info-mode-map "n" 'Info-next) - (define-key Info-mode-map "p" 'Info-prev) - (define-key Info-mode-map "q" 'Info-exit) - (define-key Info-mode-map "r" 'Info-history-forward) - (define-key Info-mode-map "s" 'Info-search) - (define-key Info-mode-map "S" 'Info-search-case-sensitively) - ;; For consistency with Rmail. - (define-key Info-mode-map "\M-s" 'Info-search) - (define-key Info-mode-map "\M-n" 'clone-buffer) - (define-key Info-mode-map "t" 'Info-top-node) - (define-key Info-mode-map "T" 'Info-toc) - (define-key Info-mode-map "u" 'Info-up) - ;; `w' for consistency with `dired-copy-filename-as-kill'. - (define-key Info-mode-map "w" 'Info-copy-current-node-name) - (define-key Info-mode-map "c" 'Info-copy-current-node-name) - ;; `^' for consistency with `dired-up-directory'. - (define-key Info-mode-map "^" 'Info-up) - (define-key Info-mode-map "," 'Info-index-next) - (define-key Info-mode-map "\177" 'Info-scroll-down) - (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node) - (define-key Info-mode-map [follow-link] 'mouse-face) - ) + (defun Info-check-pointer (item) "Non-nil if ITEM is present in this node." @@ -3266,23 +3317,22 @@ If FORK is non-nil, it i spassed to `Info-goto-node'." (defvar info-tool-bar-map - (if (display-graphic-p) - (let ((map (make-sparse-keymap))) - (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map - :rtl "right-arrow") - (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map - :rtl "left-arrow") - (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map - :rtl "next-node") - (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map - :rtl "prev-node") - (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map) - (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map) - (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map) - (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map) - (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map) - (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map) - map))) + (let ((map (make-sparse-keymap))) + (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map + :rtl "right-arrow") + (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map + :rtl "left-arrow") + (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map + :rtl "next-node") + (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map + :rtl "prev-node") + (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map) + (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map) + (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map) + (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map) + (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map) + (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map) + map)) (defvar Info-menu-last-node nil) ;; Last node the menu was created for. @@ -3375,7 +3425,7 @@ With a zero prefix arg, put the name inside a function call to `info'." (put 'Info-mode 'no-clone-indirect t) (defvar tool-bar-map) -(defvar bookmark-make-cell-function) +(defvar bookmark-make-record-function) ;; Autoload cookie needed by desktop.el ;;;###autoload @@ -3430,6 +3480,7 @@ Advanced commands: \\[Info-search-case-sensitively] Search through this Info file for specified regexp case-sensitively. \\[Info-search-next] Search for another occurrence of regexp from a previous \\\\[Info-search] command. +\\[isearch-forward], \\[isearch-forward-regexp] Use Isearch to search through multiple Info nodes. \\[Info-index] Search for a topic in this manual's Index and go to index entry. \\[Info-index-next] (comma) Move to the next match from a previous \\\\[Info-index] command. \\[info-apropos] Look for a string in the indices of all manuals. @@ -3461,10 +3512,9 @@ Advanced commands: (make-local-variable 'Info-history) (make-local-variable 'Info-history-forward) (make-local-variable 'Info-index-alternatives) - (setq header-line-format - (if Info-use-header-line - '(:eval (get-text-property (point-min) 'header-line)) - nil)) ; so the header line isn't displayed + (if Info-use-header-line ; do not override global header lines + (setq header-line-format + '(:eval (get-text-property (point-min) 'header-line)))) (set (make-local-variable 'tool-bar-map) info-tool-bar-map) ;; This is for the sake of the invisible text we use handling titles. (make-local-variable 'line-move-ignore-invisible) @@ -3483,13 +3533,15 @@ Advanced commands: 'Info-isearch-wrap) (set (make-local-variable 'isearch-push-state-function) 'Info-isearch-push-state) + (set (make-local-variable 'isearch-success-function) + 'Info-search-success-function) (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) - (set (make-local-variable 'bookmark-make-cell-function) - 'Info-bookmark-make-cell) + (set (make-local-variable 'bookmark-make-record-function) + 'Info-bookmark-make-record) (run-mode-hooks 'Info-mode-hook)) ;; When an Info buffer is killed, make sure the associated tags buffer @@ -3737,6 +3789,47 @@ the variable `Info-file-list-for-emacs'." keymap) "Keymap to put on the Up link in the text or the header line.") +(defun Info-insert-breadcrumbs () + (let ((nodes (Info-toc-nodes Info-current-file)) + (node Info-current-node) + (crumbs ()) + (depth Info-breadcrumbs-depth)) + + ;; Get ancestors from the cached parent-children node info + (while (and (not (equal "Top" node)) (> depth 0)) + (setq node (nth 1 (assoc node nodes))) + (if node (push node crumbs)) + (setq depth (1- depth))) + + ;; Add bottom node. + (when Info-use-header-line + ;; Let it disappear if crumbs is nil. + (nconc crumbs (list Info-current-node))) + (when (or Info-use-header-line crumbs) + ;; Add top node (and continuation if needed). + (setq crumbs + (cons "Top" (if (member (pop crumbs) '(nil "Top")) + crumbs (cons nil crumbs)))) + ;; Eliminate duplicate. + (forward-line 1) + (dolist (node crumbs) + (let ((text + (if (not (equal node "Top")) node + (format "(%s)Top" + (if (stringp Info-current-file) + (file-name-nondirectory Info-current-file) + ;; Can be `toc', `apropos', or even `history'. + Info-current-file))))) + (insert (if (bolp) "" " > ") + (cond + ((null node) "...") + ((equal node Info-current-node) + ;; No point linking to ourselves. + (propertize text 'font-lock-face 'info-header-node)) + (t + (concat "*Note " text "::")))))) + (insert "\n")))) + (defun Info-fontify-node () "Fontify the node." (save-excursion @@ -3744,7 +3837,7 @@ the variable `Info-file-list-for-emacs'." (case-fold-search t) paragraph-markers (not-fontified-p ; the node hasn't already been fontified - (not (let ((where (next-single-property-change (point-min) + (not (let ((where (next-single-property-change (point-min) 'font-lock-face))) (and where (not (= where (point-max))))))) (fontify-visited-p ; visited nodes need to be re-fontified @@ -3781,6 +3874,11 @@ the variable `Info-file-list-for-emacs'." ((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)))))) + + (when (> Info-breadcrumbs-depth 0) + (Info-insert-breadcrumbs)) + + ;; Treat header line. (when Info-use-header-line (goto-char (point-min)) (let* ((header-end (line-end-position)) @@ -3808,10 +3906,13 @@ the variable `Info-file-list-for-emacs'." (lambda (s) (concat s s)) header)) ;; Hide the part of the first line ;; that is in the header, if it is just part. - (unless (bobp) + (cond + ((> Info-breadcrumbs-depth 0) + (put-text-property (point-min) (1+ header-end) 'invisible t)) + ((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)))))) ;; Fontify titles (goto-char (point-min)) @@ -3835,7 +3936,7 @@ the variable `Info-file-list-for-emacs'." ;; This is a serious problem for trying to handle multiple ;; frame types at once. We want this text to be invisible ;; on frames that can display the font above. - (when (memq (framep (selected-frame)) '(x pc w32 mac)) + (when (memq (framep (selected-frame)) '(x pc w32 ns)) (add-text-properties (1- (match-beginning 2)) (match-end 2) '(invisible t front-sticky nil rear-nonsticky t))))) @@ -3848,7 +3949,8 @@ the variable `Info-file-list-for-emacs'." other-tag) (when not-fontified-p (when Info-hide-note-references - (when (not (eq Info-hide-note-references 'hide)) + (when (and (not (eq Info-hide-note-references 'hide)) + (> (line-number-at-pos) 4)) ; Skip breadcrumbs ;; *Note is often used where *note should have been (goto-char start) (skip-syntax-backward " ") @@ -4095,8 +4197,8 @@ the variable `Info-file-list-for-emacs'." nil t) (add-text-properties (match-beginning 0) (match-end 0) '(font-lock-face info-xref - mouse-face highlight - help-echo "mouse-2: go to this URL")))) + mouse-face highlight + help-echo "mouse-2: go to this URL")))) (set-buffer-modified-p nil)))) @@ -4235,9 +4337,8 @@ INDENT is the current indentation depth." (defun Info-speedbar-fetch-file-nodes (nodespec) "Fetch the subnodes from the info NODESPEC. NODESPEC is a string of the form: (file)node." - (save-excursion - ;; Set up a buffer we can use to fake-out Info. - (set-buffer (get-buffer-create " *info-browse-tmp*")) + ;; Set up a buffer we can use to fake-out Info. + (with-current-buffer (get-buffer-create " *info-browse-tmp*") (if (not (equal major-mode 'Info-mode)) (Info-mode)) ;; Get the node into this buffer @@ -4321,95 +4422,30 @@ BUFFER is the buffer speedbar is requesting buttons for." '(Info-mode . Info-restore-desktop-buffer)) ;;;; Bookmark support - -(defvar bookmark-search-size) - -;; This is only called from bookmark.el. -(declare-function bookmark-buffer-file-name "bookmark" ()) - -(defun Info-bookmark-make-cell (annotation &optional info-node) - (let ((the-record - `((filename . ,(bookmark-buffer-file-name)) - (front-context-string - . ,(if (>= (- (point-max) (point)) bookmark-search-size) - (buffer-substring-no-properties - (point) - (+ (point) bookmark-search-size)) - nil)) - (rear-context-string - . ,(if (>= (- (point) (point-min)) bookmark-search-size) - (buffer-substring-no-properties - (point) - (- (point) bookmark-search-size)) - nil)) - (position . ,(point)) - (info-node . ,info-node) - (handler . Info-bookmark-jump)))) - - ;; Now fill in the optional parts: - - ;; Take no chances with text properties - (set-text-properties 0 (length annotation) nil annotation) - - (if annotation - (nconc the-record (list (cons 'annotation annotation)))) - - ;; Finally, return the completed record. - the-record)) - -(defvar bookmark-current-bookmark) -(declare-function bookmark-get-filename "bookmark" (bookmark)) -(declare-function bookmark-get-front-context-string "bookmark" (bookmark)) -(declare-function bookmark-get-rear-context-string "bookmark" (bookmark)) -(declare-function bookmark-get-position "bookmark" (bookmark)) -(declare-function bookmark-get-info-node "bookmark" (bookmark)) -(declare-function bookmark-file-or-variation-thereof "bookmark" (file)) -(declare-function bookmark-jump-noselect "bookmark" (str)) +(declare-function bookmark-make-record-default "bookmark" (&optional pos-only)) +(declare-function bookmark-prop-get "bookmark" (bookmark prop)) +(declare-function bookmark-default-handler "bookmark" (bmk)) +(declare-function bookmark-get-bookmark-record "bookmark" (bmk)) + +(defun Info-bookmark-make-record () + `(,Info-current-node + ,@(bookmark-make-record-default 'point-only) + (filename . ,Info-current-file) + (info-node . ,Info-current-node) + (handler . Info-bookmark-jump))) ;;;###autoload (defun Info-bookmark-jump (bmk) ;; This implements the `handler' function interface for record type returned - ;; by `Info-make-cell-function', which see. - (let* ((file (expand-file-name (bookmark-get-filename bmk))) - (forward-str (bookmark-get-front-context-string bmk)) - (behind-str (bookmark-get-rear-context-string bmk)) - (place (bookmark-get-position bmk)) - (info-node (bookmark-get-info-node bmk)) - (orig-file file)) - (if (setq file (bookmark-file-or-variation-thereof file)) - (save-excursion - (save-window-excursion - (with-no-warnings - (Info-find-node file info-node)) - ;; Go searching forward first. Then, if forward-str exists and was - ;; found in the file, we can search backward for behind-str. - ;; Rationale is that if text was inserted between the two in the - ;; file, it's better to be put before it so you can read it, rather - ;; than after and remain perhaps unaware of the changes. - (if forward-str - (if (search-forward forward-str (point-max) t) - (goto-char (match-beginning 0)))) - (if behind-str - (if (search-backward behind-str (point-min) t) - (goto-char (match-end 0)))) - ;; added by db - (setq bookmark-current-bookmark bmk) - `((buffer ,(current-buffer)) (position ,(point))))) - - ;; Else unable to find the marked file, so ask if user wants to - ;; relocate the bookmark, else remind them to consider deletion. - (ding) - (if (y-or-n-p (concat (file-name-nondirectory orig-file) - " nonexistent. Relocate \"" - bmk - "\"? ")) - (progn - (bookmark-relocate bmk) - ;; gasp! It's a recursive function call in Emacs Lisp! - (bookmark-jump-noselect bmk)) - (message - "Bookmark not relocated; consider removing it \(%s\)." bmk) - nil)))) + ;; by `Info-bookmark-make-record', which see. + (let* ((file (bookmark-prop-get bmk 'filename)) + (info-node (bookmark-prop-get bmk 'info-node)) + (buf (save-window-excursion ;FIXME: doesn't work with frames! + (Info-find-node file info-node) (current-buffer)))) + ;; Use bookmark-default-handler to move to the appropriate location + ;; within the node. + (bookmark-default-handler + (list* "" `(buffer . ,buf) (bookmark-get-bookmark-record bmk))))) (provide 'info)