merging Emacs.app (NeXTstep port)
[bpt/emacs.git] / lisp / info.el
CommitLineData
55535639 1;;; info.el --- info package for Emacs
e5167999 2
83040cb0 3;; Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
e1af69b4
GM
4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
5;; Free Software Foundation, Inc.
3a801d0c 6
e5167999 7;; Maintainer: FSF
fd7fa35a 8;; Keywords: help
e5167999 9
a384cab3
JB
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
a384cab3 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
a384cab3
JB
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
a384cab3 24
e5167999
ER
25;;; Commentary:
26
185cff95 27;; Note that nowadays we expect Info files to be made using makeinfo.
921e5fe6
TTN
28;; In particular we make these assumptions:
29;; - a menu item MAY contain colons but not colon-space ": "
30;; - a menu item ending with ": " (but not ":: ") is an index entry
31;; - a node name MAY NOT contain a colon
505b68d5
TTN
32;; This distinction is to support indexing of computer programming
33;; language terms that may contain ":" but not ": ".
e5167999
ER
34
35;;; Code:
36
43f8b275 37(eval-when-compile (require 'jka-compr) (require 'cl))
bc6262c8 38
ded3e3d8 39(defgroup info nil
fc039bdc 40 "Info subsystem."
ded3e3d8
RS
41 :group 'help
42 :group 'docs)
43
44
a384cab3 45(defvar Info-history nil
185cff95 46 "Stack of Info nodes user has visited.
db90d225 47Each element of the stack is a list (FILENAME NODENAME BUFFERPOS).")
a384cab3 48
424b9d3e 49(defvar Info-history-forward nil
185cff95 50 "Stack of Info nodes user has visited with `Info-history-back' command.
db90d225 51Each element of the stack is a list (FILENAME NODENAME BUFFERPOS).")
424b9d3e 52
8a7757f6 53(defvar Info-history-list nil
185cff95 54 "List of all Info nodes user has visited.
db90d225 55Each element of the list is a list (FILENAME NODENAME).")
8a7757f6 56
ded3e3d8 57(defcustom Info-enable-edit nil
d5913bf6 58 "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
185cff95 59This is convenient if you want to write Info files by hand.
ada0a60d
RS
60However, we recommend that you not do this.
61It is better to write a Texinfo file and generate the Info file from that,
ded3e3d8
RS
62because that gives you a printed manual as well."
63 :type 'boolean
64 :group 'info)
a384cab3 65
67f445d7 66(defvar Info-enable-active-nodes nil
a384cab3
JB
67 "Non-nil allows Info to execute Lisp code associated with nodes.
68The Lisp code is executed when the node is selected.")
67f445d7 69(put 'Info-enable-active-nodes 'risky-local-variable t)
a384cab3 70
93480d70 71(defface info-node
508bce68
SM
72 '((((class color) (background light)) :foreground "brown" :weight bold :slant italic)
73 (((class color) (background dark)) :foreground "white" :weight bold :slant italic)
74 (t :weight bold :slant italic))
93480d70
RS
75 "Face for Info node names."
76 :group 'info)
77
185cff95
JL
78(defface info-title-1
79 '((((type tty pc) (class color) (background light))
80 :foreground "green" :weight bold)
81 (((type tty pc) (class color) (background dark))
82 :foreground "yellow" :weight bold)
83 (t :height 1.2 :inherit info-title-2))
84 "Face for info titles at level 1."
85 :group 'info)
86;; backward-compatibility alias
87(put 'Info-title-1-face 'face-alias 'info-title-1)
88
89(defface info-title-2
90 '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
91 (t :height 1.2 :inherit info-title-3))
92 "Face for info titles at level 2."
93 :group 'info)
94;; backward-compatibility alias
95(put 'Info-title-2-face 'face-alias 'info-title-2)
96
97(defface info-title-3
98 '((((type tty pc) (class color)) :weight bold)
99 (t :height 1.2 :inherit info-title-4))
100 "Face for info titles at level 3."
101 :group 'info)
102;; backward-compatibility alias
103(put 'Info-title-3-face 'face-alias 'info-title-3)
104
105(defface info-title-4
106 '((((type tty pc) (class color)) :weight bold)
107 (t :weight bold :inherit variable-pitch))
108 "Face for info titles at level 4."
109 :group 'info)
110;; backward-compatibility alias
111(put 'Info-title-4-face 'face-alias 'info-title-4)
112
113(defface info-menu-header
114 '((((type tty pc))
115 :underline t
116 :weight bold)
117 (t
118 :inherit variable-pitch
119 :weight bold))
120 "Face for headers in Info menus."
121 :group 'info)
122
123(defface info-menu-star
508bce68
SM
124 '((((class color)) :foreground "red1")
125 (t :underline t))
ccfb7415 126 "Face for every third `*' in an Info menu."
93480d70 127 :group 'info)
185cff95 128(put 'info-menu-5 'face-alias 'info-menu-star)
93480d70
RS
129
130(defface info-xref
de8ad208
JL
131 '((t :inherit link))
132 "Face for unvisited Info cross-references."
93480d70
RS
133 :group 'info)
134
8a7757f6 135(defface info-xref-visited
fd5b25da 136 '((t :inherit (link-visited info-xref)))
8a7757f6 137 "Face for visited Info cross-references."
de8ad208 138 :version "22.1"
8a7757f6
JL
139 :group 'info)
140
141(defcustom Info-fontify-visited-nodes t
185cff95 142 "*Non-nil to fontify references to visited nodes in `info-xref-visited' face."
bf247b6e 143 :version "22.1"
8a7757f6
JL
144 :type 'boolean
145 :group 'info)
146
7a7821c2 147(defcustom Info-fontify-maximum-menu-size 100000
3d0fc0b7
EZ
148 "*Maximum size of menu to fontify if `font-lock-mode' is non-nil.
149Set to nil to disable node fontification."
ded3e3d8
RS
150 :type 'integer
151 :group 'info)
bdf62a4d 152
50ac70af 153(defcustom Info-use-header-line t
c3717746 154 "*Non-nil means to put the beginning-of-node links in an Emacs header-line.
50ac70af
MB
155A header-line does not scroll with the rest of the buffer."
156 :type 'boolean
157 :group 'info)
158
159(defface info-header-xref
508bce68 160 '((t :inherit info-xref))
50ac70af
MB
161 "Face for Info cross-references in a node header."
162 :group 'info)
163
164(defface info-header-node
508bce68 165 '((t :inherit info-node))
50ac70af
MB
166 "Face for Info nodes in a node header."
167 :group 'info)
168
7d3766a8 169(defvar Info-directory-list nil
a384cab3 170 "List of directories to search for Info documentation files.
fc4d14c5 171If nil, meaning not yet initialized, Info uses the environment
44c327f9 172variable INFOPATH to initialize it, or `Info-default-directory-list'
dde7f979
SM
173if there is no INFOPATH variable in the environment, or the
174concatenation of the two if INFOPATH ends with a colon.
778911b9
EZ
175
176When `Info-directory-list' is initialized from the value of
4dddb0b7
EZ
177`Info-default-directory-list', and Emacs is installed in one of the
178standard directories, the directory of Info files that come with Emacs
179is put last (so that local Info files override standard ones).
180
181When `Info-directory-list' is initialized from the value of
182`Info-default-directory-list', and Emacs is not installed in one
183of the standard directories, the first element of the resulting
778911b9
EZ
184list is the directory where Emacs installs the Info files that
185come with it. This is so that Emacs's own manual, which suits the
4dddb0b7
EZ
186version of Emacs you are using, will always be found first. This
187is useful when you install an experimental version of Emacs without
188removing the standard installation.
189
190If you want to override the order of directories in
191`Info-default-directory-list', set INFOPATH in the environment.
5da0f945
RS
192
193If you run the Emacs executable from the `src' directory in the Emacs
778911b9
EZ
194source tree, and INFOPATH is not defined, the `info' directory in the
195source tree is used as the first element of `Info-directory-list', in
196place of the installation Info directory. This is useful when you run
197a version of Emacs without installing it.")
a384cab3 198
ded3e3d8 199(defcustom Info-additional-directory-list nil
bdf62a4d 200 "List of additional directories to search for Info documentation files.
e9b1d996 201These directories are searched after those in `Info-directory-list'."
ded3e3d8
RS
202 :type '(repeat directory)
203 :group 'info)
bdf62a4d 204
610bc96b 205(defcustom Info-scroll-prefer-subnodes nil
7a53d8c8 206 "*If non-nil, \\<Info-mode-map>\\[Info-scroll-up] in a menu visits subnodes.
185cff95 207
7a53d8c8
EZ
208If this is non-nil, and you scroll far enough in a node that its menu
209appears on the screen, the next \\<Info-mode-map>\\[Info-scroll-up]
210moves to a subnode indicated by the following menu item. This means
211that you visit a subnode before getting to the end of the menu.
212
213Setting this option to nil results in behavior similar to the stand-alone
214Info reader program, which visits the first subnode from the menu only
215when you hit the end of the current node."
bf247b6e 216 :version "22.1"
7a53d8c8
EZ
217 :type 'boolean
218 :group 'info)
219
a9efebd0 220(defcustom Info-hide-note-references t
5f812a3c 221 "*If non-nil, hide the tag and section reference in *note and * menu items.
8a7757f6 222If value is non-nil but not `hide', also replaces the \"*note\" with \"see\".
185cff95
JL
223If value is non-nil but not t or `hide', the reference section is still shown.
224`nil' completely disables this feature."
bf247b6e 225 :version "22.1"
8a7757f6 226 :type '(choice (const :tag "No hiding" nil)
0452a274 227 (const :tag "Replace tag and hide reference" t)
8a7757f6
JL
228 (const :tag "Hide tag and reference" hide)
229 (other :tag "Only replace tag" tag))
a9efebd0
KS
230 :group 'info)
231
bb1e1730
KS
232(defcustom Info-refill-paragraphs nil
233 "*If non-nil, attempt to refill paragraphs with hidden references.
185cff95 234This refilling may accidentally remove explicit line breaks in the Info
bb1e1730 235file, so be prepared for a few surprises if you enable this feature."
bf247b6e 236 :version "22.1"
bb1e1730
KS
237 :type 'boolean
238 :group 'info)
239
180ee7c7 240(defcustom Info-search-whitespace-regexp "\\s-+"
8a7757f6
JL
241 "*If non-nil, regular expression to match a sequence of whitespace chars.
242This applies to Info search for regular expressions.
243You might want to use something like \"[ \\t\\r\\n]+\" instead.
244In the Customization buffer, that is `[' followed by a space,
245a tab, a carriage return (control-M), a newline, and `]+'."
246 :type 'regexp
247 :group 'info)
248
98e77be5 249(defcustom Info-isearch-search t
6270bbe7
JL
250 "*If non-nil, isearch in Info searches through multiple nodes.
251Before leaving the initial Info node, where isearch was started,
252it fails once with the error message [initial node], and with
253subsequent C-s/C-r continues through other nodes without failing
254with this error message in other nodes. When isearch fails for
255the rest of the manual, it wraps aroung the whole manual and
256restarts the search from the top/final node depending on
257search direction.
258
259Setting this option to nil restores the default isearch behavior
260with wrapping around the current Info node."
98e77be5
JL
261 :version "22.1"
262 :type 'boolean
263 :group 'info)
264
d457219d
JL
265(defvar Info-isearch-initial-node nil)
266
dde7f979
SM
267(defcustom Info-mode-hook
268 ;; Try to obey obsolete Info-fontify settings.
269 (unless (and (boundp 'Info-fontify) (null Info-fontify))
270 '(turn-on-font-lock))
8a7757f6
JL
271 "Hooks run when `Info-mode' is called."
272 :type 'hook
273 :group 'info)
274
275(defcustom Info-selection-hook nil
276 "Hooks run when `Info-select-node' is called."
544e5562
CW
277 :type 'hook
278 :group 'info)
279
8a7757f6
JL
280(defvar Info-edit-mode-hook nil
281 "Hooks run when `Info-edit-mode' is called.")
282
a384cab3 283(defvar Info-current-file nil
d6be34f3
RS
284 "Info file that Info is now looking at, or nil.
285This is the name that was specified in Info, not the actual file name.
d590dbd2 286It doesn't contain directory names or file name extensions added by Info.")
a384cab3
JB
287
288(defvar Info-current-subfile nil
fdf4b680 289 "Info subfile that is actually in the *info* buffer now.
ef9644f5 290It is nil if current Info file is not split into subfiles.")
a384cab3
JB
291
292(defvar Info-current-node nil
293 "Name of node that Info is now looking at, or nil.")
294
c5fe2ff1 295(defvar Info-tag-table-marker nil
a384cab3
JB
296 "Marker pointing at beginning of current Info file's tag table.
297Marker points nowhere if file has no tag table.")
298
c5fe2ff1
RS
299(defvar Info-tag-table-buffer nil
300 "Buffer used for indirect tag tables.")
301
552775bd
RS
302(defvar Info-current-file-completions nil
303 "Cached completion list for current Info file.")
304
94b10022
JL
305(defvar Info-file-supports-index-cookies nil
306 "Non-nil if current Info file supports index cookies.")
307
1143a6b0 308(defvar Info-index-alternatives nil
fdf4b680 309 "List of possible matches for last `Info-index' command.")
1143a6b0 310
836bb478
JL
311(defvar Info-point-loc nil
312 "Point location within a selected node.
313If string, the point is moved to the proper occurrence of the
314name of the followed cross reference within a selected node.
315If number, the point is moved to the corresponding line.")
8a7757f6 316
552775bd
RS
317(defvar Info-standalone nil
318 "Non-nil if Emacs was started solely as an Info browser.")
7d3766a8 319\f
bd0c9168 320(defvar Info-suffix-list
3c19e6c1
RS
321 ;; The MS-DOS list should work both when long file names are
322 ;; supported (Windows 9X), and when only 8+3 file names are available.
bd0c9168
RS
323 (if (eq system-type 'ms-dos)
324 '( (".gz" . "gunzip")
325 (".z" . "gunzip")
544e210f 326 (".bz2" . ("bzip2" "-dc"))
3c19e6c1
RS
327 (".inz" . "gunzip")
328 (".igz" . "gunzip")
329 (".info.Z" . "gunzip")
330 (".info.gz" . "gunzip")
331 ("-info.Z" . "gunzip")
332 ("-info.gz" . "gunzip")
333 ("/index.gz". "gunzip")
334 ("/index.z" . "gunzip")
9b842cab 335 (".inf" . nil)
3c19e6c1
RS
336 (".info" . nil)
337 ("-info" . nil)
338 ("/index" . nil)
bd0c9168 339 ("" . nil))
a70dc410
RS
340 '( (".info.Z". "uncompress")
341 (".info.Y". "unyabba")
342 (".info.gz". "gunzip")
343 (".info.z". "gunzip")
544e210f 344 (".info.bz2" . ("bzip2" "-dc"))
a70dc410
RS
345 (".info". nil)
346 ("-info.Z". "uncompress")
347 ("-info.Y". "unyabba")
348 ("-info.gz". "gunzip")
544e210f 349 ("-info.bz2" . ("bzip2" "-dc"))
a70dc410
RS
350 ("-info.z". "gunzip")
351 ("-info". nil)
352 ("/index.Z". "uncompress")
353 ("/index.Y". "unyabba")
354 ("/index.gz". "gunzip")
355 ("/index.z". "gunzip")
544e210f 356 ("/index.bz2". ("bzip2" "-dc"))
a70dc410
RS
357 ("/index". nil)
358 (".Z". "uncompress")
359 (".Y". "unyabba")
360 (".gz". "gunzip")
361 (".z". "gunzip")
544e210f 362 (".bz2" . ("bzip2" "-dc"))
a70dc410 363 ("". nil)))
1143a6b0
ER
364 "List of file name suffixes and associated decoding commands.
365Each entry should be (SUFFIX . STRING); the file is given to
544e210f
KH
366the command as standard input.
367
368STRING may be a list of strings. In that case, the first element is
369the command name, and the rest are arguments to that command.
370
371If STRING is nil, no decoding is done.
97f99202
KH
372Because the SUFFIXes are tried in order, the empty string should
373be last in the list.")
1143a6b0 374
c8e9dd54 375;; Concatenate SUFFIX onto FILENAME. SUFFIX should start with a dot.
e0c1e774
EZ
376;; First, on MS-DOS with no long file names support, delete some of
377;; the extension in FILENAME to make room.
378(defun info-insert-file-contents-1 (filename suffix lfn)
379 (if lfn ; long file names are supported
bd0c9168
RS
380 (concat filename suffix)
381 (let* ((sans-exts (file-name-sans-extension filename))
c8e9dd54
RS
382 ;; How long is the extension in FILENAME (not counting the dot).
383 (ext-len (max 0 (- (length filename) (length sans-exts) 1)))
384 ext-left)
9b842cab 385 ;; SUFFIX starts with a dot. If FILENAME already has one,
1583fe94 386 ;; get rid of the one in SUFFIX (unless suffix is empty).
c8e9dd54 387 (or (and (<= ext-len 0)
9b842cab 388 (not (eq (aref filename (1- (length filename))) ?.)))
1583fe94 389 (= (length suffix) 0)
9b842cab 390 (setq suffix (substring suffix 1)))
c8e9dd54
RS
391 ;; How many chars of that extension should we keep?
392 (setq ext-left (min ext-len (max 0 (- 3 (length suffix)))))
bd0c9168
RS
393 ;; Get rid of the rest of the extension, and add SUFFIX.
394 (concat (substring filename 0 (- (length filename)
395 (- ext-len ext-left)))
396 suffix))))
397
9c7924d5
RS
398(defun info-file-exists-p (filename)
399 (and (file-exists-p filename)
400 (not (file-directory-p filename))))
401
1143a6b0 402(defun info-insert-file-contents (filename &optional visit)
185cff95 403 "Insert the contents of an Info file in the current buffer.
1143a6b0 404Do the right thing if the file has been compressed or zipped."
e0c1e774 405 (let* ((tail Info-suffix-list)
ab5a535c
KS
406 (lfn (if (fboundp 'msdos-long-file-names)
407 (msdos-long-file-names)
408 t))
e0c1e774
EZ
409 (check-short (and (fboundp 'msdos-long-file-names)
410 lfn))
411 fullname decoder done)
b8fa4e87 412 (if (info-file-exists-p filename)
bd0c9168
RS
413 ;; FILENAME exists--see if that name contains a suffix.
414 ;; If so, set DECODE accordingly.
97f99202
KH
415 (progn
416 (while (and tail
417 (not (string-match
418 (concat (regexp-quote (car (car tail))) "$")
419 filename)))
420 (setq tail (cdr tail)))
421 (setq fullname filename
422 decoder (cdr (car tail))))
bd0c9168 423 ;; Try adding suffixes to FILENAME and see if we can find something.
e0c1e774
EZ
424 (while (and tail (not done))
425 (setq fullname (info-insert-file-contents-1 filename
426 (car (car tail)) lfn))
427 (if (info-file-exists-p fullname)
428 (setq done t
429 ;; If we found a file with a suffix, set DECODER
430 ;; according to the suffix.
431 decoder (cdr (car tail)))
432 ;; When the MS-DOS port runs on Windows, we need to check
433 ;; the short variant of a long file name as well.
434 (when check-short
435 (setq fullname (info-insert-file-contents-1 filename
436 (car (car tail)) nil))
437 (if (info-file-exists-p fullname)
438 (setq done t
439 decoder (cdr (car tail))))))
97f99202 440 (setq tail (cdr tail)))
97f99202 441 (or tail
bd0c9168 442 (error "Can't find %s or any compressed version of it" filename)))
e175bda2 443 ;; check for conflict with jka-compr
70746649 444 (if (and (jka-compr-installed-p)
e175bda2
RS
445 (jka-compr-get-compression-info fullname))
446 (setq decoder nil))
97f99202 447 (if decoder
3c19e6c1
RS
448 (progn
449 (insert-file-contents-literally fullname visit)
e8dab975 450 (let ((inhibit-read-only t)
3c19e6c1
RS
451 (coding-system-for-write 'no-conversion)
452 (default-directory (or (file-name-directory fullname)
453 default-directory)))
544e210f
KH
454 (or (consp decoder)
455 (setq decoder (list decoder)))
456 (apply 'call-process-region (point-min) (point-max)
457 (car decoder) t t nil (cdr decoder))))
3c19e6c1 458 (insert-file-contents fullname visit))))
7d3766a8 459\f
3a59b662
SM
460(defun Info-default-dirs ()
461 (let ((source (expand-file-name "info/" source-directory))
462 (sibling (if installation-directory
463 (expand-file-name "info/" installation-directory)
464 (if invocation-directory
465 (let ((infodir (expand-file-name
41a1027e 466 "../share/info/"
3a59b662
SM
467 invocation-directory)))
468 (if (file-exists-p infodir)
469 infodir
470 (setq infodir (expand-file-name
41a1027e 471 "../../../share/info/"
3a59b662
SM
472 invocation-directory))
473 (and (file-exists-p infodir)
474 infodir))))))
475 alternative)
476 (setq alternative
477 (if (and sibling (file-exists-p sibling))
478 ;; Uninstalled, Emacs builddir != srcdir.
479 sibling
480 ;; Uninstalled, builddir == srcdir
481 source))
482 (if (or (member alternative Info-default-directory-list)
483 ;; On DOS/NT, we use movable executables always,
484 ;; and we must always find the Info dir at run time.
485 (if (memq system-type '(ms-dos windows-nt))
486 nil
487 ;; Use invocation-directory for Info
488 ;; only if we used it for exec-directory also.
489 (not (string= exec-directory
490 (expand-file-name "lib-src/"
491 installation-directory))))
492 (not (file-exists-p alternative)))
493 Info-default-directory-list
494 ;; `alternative' contains the Info files that came with this
495 ;; version, so we should look there first. `Info-insert-dir'
496 ;; currently expects to find `alternative' first on the list.
497 (cons alternative
a025fccf
SM
498 ;; Don't drop the last part, it might contain non-Emacs stuff.
499 ;; (reverse (cdr (reverse
500 Info-default-directory-list)))) ;; )))
3a59b662 501
7d3766a8 502(defun info-initialize ()
fdf4b680 503 "Initialize `Info-directory-list', if that hasn't been done yet."
7d3766a8 504 (unless Info-directory-list
3a59b662 505 (let ((path (getenv "INFOPATH")))
7d3766a8 506 (setq Info-directory-list
78e7e8a0
MB
507 (prune-directory-list
508 (if path
3a59b662
SM
509 (if (string-match ":\\'" path)
510 (append (split-string (substring path 0 -1)
511 (regexp-quote path-separator))
512 (Info-default-dirs))
513 (split-string path (regexp-quote path-separator)))
514 (Info-default-dirs)))))))
1143a6b0 515
a6e4564e 516;;;###autoload
94807253 517(defun info-other-window (&optional file-or-node)
a6e4564e
RS
518 "Like `info' but show the Info buffer in another window."
519 (interactive (if current-prefix-arg
520 (list (read-file-name "Info file name: " nil nil t))))
836bb478 521 (let (same-window-buffer-names same-window-regexps)
94807253 522 (info file-or-node)))
399c88ad 523
836bb478 524;;;###autoload (add-hook 'same-window-regexps "\\*info\\*\\(\\|<[0-9]+>\\)")
211d6309 525
cc3f6c32 526;;;###autoload (put 'info 'info-file "emacs")
a384cab3 527;;;###autoload
94807253 528(defun info (&optional file-or-node buffer)
a384cab3 529 "Enter Info, the documentation browser.
94807253 530Optional argument FILE-OR-NODE specifies the file to examine;
a384cab3 531the default is the top-level directory of Info.
94807253 532Called from a program, FILE-OR-NODE may specify an Info node of the form
83d5c772 533`(FILENAME)NODENAME'.
836bb478
JL
534Optional argument BUFFER specifies the Info buffer name;
535the default buffer name is *info*. If BUFFER exists,
536just switch to BUFFER. Otherwise, create a new buffer
537with the top-level Info directory.
a384cab3 538
836bb478
JL
539In interactive use, a non-numeric prefix argument directs
540this command to read a file name from the minibuffer.
bc224645
JL
541A numeric prefix argument selects an Info buffer with the prefix number
542appended to the Info buffer name.
e341dab2
RS
543
544The search path for Info files is in the variable `Info-directory-list'.
399c88ad 545The top-level Info directory is made by combining all the files named `dir'
e341dab2 546in all the directories in that path."
836bb478
JL
547 (interactive (list
548 (if (and current-prefix-arg (not (numberp current-prefix-arg)))
549 (read-file-name "Info file name: " nil nil t))
550 (if (numberp current-prefix-arg)
551 (format "*info*<%s>" current-prefix-arg))))
552 (pop-to-buffer (or buffer "*info*"))
553 (if (and buffer (not (eq major-mode 'Info-mode)))
554 (Info-mode))
94807253 555 (if file-or-node
60705054
SM
556 ;; If argument already contains parentheses, don't add another set
557 ;; since the argument will then be parsed improperly. This also
558 ;; has the added benefit of allowing node names to be included
559 ;; following the parenthesized filename.
83040cb0 560 (Info-goto-node
94807253
JL
561 (if (and (stringp file-or-node) (string-match "(.*)" file-or-node))
562 file-or-node
563 (concat "(" file-or-node ")")))
b0dd5bf5
RS
564 (if (and (zerop (buffer-size))
565 (null Info-history))
566 ;; If we just created the Info buffer, go to the directory.
567 (Info-directory))))
a384cab3 568
c3717746
RS
569;;;###autoload
570(defun info-emacs-manual ()
3a8803c0 571 "Display the Emacs manual in Info mode."
c3717746
RS
572 (interactive)
573 (info "emacs"))
574
552775bd
RS
575;;;###autoload
576(defun info-standalone ()
577 "Run Emacs as a standalone Info reader.
578Usage: emacs -f info-standalone [filename]
579In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
580 (setq Info-standalone t)
581 (if (and command-line-args-left
582 (not (string-match "^-" (car command-line-args-left))))
583 (condition-case err
584 (progn
585 (info (car command-line-args-left))
586 (setq command-line-args-left (cdr command-line-args-left)))
587 (error (send-string-to-terminal
588 (format "%s\n" (if (eq (car-safe err) 'error)
589 (nth 1 err) err)))
590 (save-buffers-kill-emacs)))
591 (info)))
7d3766a8 592\f
983dfbf8 593;; See if the accessible portion of the buffer begins with a node
11638562
EZ
594;; delimiter, and the node header line which follows matches REGEXP.
595;; Typically, this test will be followed by a loop that examines the
596;; rest of the buffer with (search-forward "\n\^_"), and it's a pity
597;; to have the overhead of this special test inside the loop.
598
599;; This function changes match-data, but supposedly the caller might
600;; want to use the results of re-search-backward.
601
602;; The return value is the value of point at the beginning of matching
983dfbf8 603;; REGEXP, if the function succeeds, nil otherwise.
11638562 604(defun Info-node-at-bob-matching (regexp)
760d5cb3
GM
605 (and (bobp) ; are we at beginning of buffer?
606 (looking-at "\^_") ; does it begin with node delimiter?
11638562
EZ
607 (let (beg)
608 (forward-line 1)
609 (setq beg (point))
760d5cb3 610 (forward-line 1) ; does the line after delimiter match REGEXP?
11638562
EZ
611 (re-search-backward regexp beg t))))
612
8a7757f6
JL
613(defun Info-find-file (filename &optional noerror)
614 "Return expanded FILENAME, or t, if FILENAME is \"dir\".
615Optional second argument NOERROR, if t, means if file is not found
616just return nil (no error)."
a384cab3
JB
617 ;; Convert filename to lower case if not found as specified.
618 ;; Expand it.
4fceda3c 619 (if (stringp filename)
a384cab3 620 (let (temp temp-downcase found)
37a3ff5b 621 (setq filename (substitute-in-file-name filename))
89b064a9 622 (cond
ab2f22ad
JH
623 ((string= (downcase filename) "dir")
624 (setq found t))
ab2f22ad
JH
625 (t
626 (let ((dirs (if (string-match "^\\./" filename)
37a3ff5b
RS
627 ;; If specified name starts with `./'
628 ;; then just try current directory.
629 '("./")
630 (if (file-name-absolute-p filename)
631 ;; No point in searching for an
632 ;; absolute file name
633 '(nil)
634 (if Info-additional-directory-list
635 (append Info-directory-list
636 Info-additional-directory-list)
637 Info-directory-list)))))
638 ;; Search the directory list for file FILENAME.
639 (while (and dirs (not found))
640 (setq temp (expand-file-name filename (car dirs)))
641 (setq temp-downcase
642 (expand-file-name (downcase filename) (car dirs)))
643 ;; Try several variants of specified name.
e0c1e774 644 (let ((suffix-list Info-suffix-list)
ab5a535c
KS
645 (lfn (if (fboundp 'msdos-long-file-names)
646 (msdos-long-file-names)
647 t)))
37a3ff5b
RS
648 (while (and suffix-list (not found))
649 (cond ((info-file-exists-p
650 (info-insert-file-contents-1
e0c1e774 651 temp (car (car suffix-list)) lfn))
37a3ff5b
RS
652 (setq found temp))
653 ((info-file-exists-p
654 (info-insert-file-contents-1
e0c1e774
EZ
655 temp-downcase (car (car suffix-list)) lfn))
656 (setq found temp-downcase))
657 ((and (fboundp 'msdos-long-file-names)
658 lfn
659 (info-file-exists-p
660 (info-insert-file-contents-1
661 temp (car (car suffix-list)) nil)))
662 (setq found temp)))
37a3ff5b 663 (setq suffix-list (cdr suffix-list))))
ab2f22ad 664 (setq dirs (cdr dirs))))))
37a3ff5b
RS
665 (if found
666 (setq filename found)
8a7757f6
JL
667 (if noerror
668 (setq filename nil)
669 (error "Info file %s does not exist" filename)))
bec275ed
JL
670 filename)
671 (and (member filename '(apropos history toc)) filename)))
8a7757f6
JL
672
673(defun Info-find-node (filename nodename &optional no-going-back)
185cff95 674 "Go to an Info node specified as separate FILENAME and NODENAME.
8a7757f6
JL
675NO-GOING-BACK is non-nil if recovering from an error in this function;
676it says do not attempt further (recursive) error recovery."
677 (info-initialize)
678 (setq filename (Info-find-file filename))
185cff95 679 ;; Go into Info buffer.
6270bbe7 680 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
b0dd5bf5
RS
681 ;; Record the node we are leaving, if we were in one.
682 (and (not no-going-back)
683 Info-current-file
159dfdde
SM
684 (push (list Info-current-file Info-current-node (point))
685 Info-history))
4fceda3c
SM
686 (Info-find-node-2 filename nodename no-going-back))
687
d590dbd2 688;;;###autoload
4fceda3c 689(defun Info-on-current-buffer (&optional nodename)
70746649
RS
690 "Use Info mode to browse the current Info buffer.
691With a prefix arg, this queries for the node name to visit first;
692otherwise, that defaults to `Top'."
4fceda3c
SM
693 (interactive
694 (list (if current-prefix-arg
695 (completing-read "Node name: " (Info-build-node-completions)
07e91aa3
SM
696 nil t "Top"))))
697 (unless nodename (setq nodename "Top"))
75296efc 698 (info-initialize)
4fceda3c 699 (Info-mode)
d590dbd2
CY
700 (set (make-local-variable 'Info-current-file)
701 (or buffer-file-name
702 ;; If called on a non-file buffer, make a fake file name.
703 (concat default-directory (buffer-name))))
4fceda3c
SM
704 (Info-find-node-2 nil nodename))
705
f584b4c7 706;; It's perhaps a bit nasty to kill the *info* buffer to force a re-read,
e56f84d7
TTN
707;; but at least it keeps this routine (which is for makeinfo-buffer and
708;; Info-revert-buffer-function) out of the way of normal operations.
f584b4c7
RS
709;;
710(defun Info-revert-find-node (filename nodename)
185cff95 711 "Go to an Info node FILENAME and NODENAME, re-reading disk contents.
f584b4c7
RS
712When *info* is already displaying FILENAME and NODENAME, the window position
713is preserved, if possible."
714 (pop-to-buffer "*info*")
715 (let ((old-filename Info-current-file)
716 (old-nodename Info-current-node)
717 (pcolumn (current-column))
718 (pline (count-lines (point-min) (line-beginning-position)))
719 (wline (count-lines (point-min) (window-start)))
720 (old-history Info-history)
96639c89
JL
721 (new-history (and Info-current-file
722 (list Info-current-file Info-current-node (point)))))
f584b4c7
RS
723 (kill-buffer (current-buffer))
724 (Info-find-node filename nodename)
725 (setq Info-history old-history)
726 (if (and (equal old-filename Info-current-file)
727 (equal old-nodename Info-current-node))
728 (progn
729 ;; note goto-line is no good, we want to measure from point-min
cb7de2e7 730 (goto-char (point-min))
f584b4c7
RS
731 (forward-line wline)
732 (set-window-start (selected-window) (point))
cb7de2e7 733 (goto-char (point-min))
f584b4c7
RS
734 (forward-line pline)
735 (move-to-column pcolumn))
736 ;; only add to the history when coming from a different file+node
737 (if new-history
738 (setq Info-history (cons new-history Info-history))))))
739
e56f84d7
TTN
740(defun Info-revert-buffer-function (ignore-auto noconfirm)
741 (when (or noconfirm (y-or-n-p "Revert info buffer? "))
742 (Info-revert-find-node Info-current-file Info-current-node)
743 (message "Reverted %s" Info-current-file)))
744
357d11de
GM
745(defun Info-find-in-tag-table-1 (marker regexp case-fold)
746 "Find a node in a tag table.
747MARKER specifies the buffer and position to start searching at.
748REGEXP is a regular expression matching nodes or references. Its first
749group should match `Node:' or `Ref:'.
750CASE-FOLD t means search for a case-insensitive match.
751If a match was found, value is a list (FOUND-ANCHOR POS MODE), where
752FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position
753where the match was found, and MODE is `major-mode' of the buffer in
754which the match was found."
dde7f979 755 (let ((case-fold-search case-fold))
e8dab975 756 (with-current-buffer (marker-buffer marker)
357d11de 757 (goto-char marker)
921e5fe6 758
357d11de
GM
759 ;; Search tag table
760 (beginning-of-line)
761 (when (re-search-forward regexp nil t)
762 (list (string-equal "Ref:" (match-string 1))
365d2503 763 (+ (point-min) (read (current-buffer)))
357d11de
GM
764 major-mode)))))
765
766(defun Info-find-in-tag-table (marker regexp)
767 "Find a node in a tag table.
768MARKER specifies the buffer and position to start searching at.
769REGEXP is a regular expression matching nodes or references. Its first
770group should match `Node:' or `Ref:'.
771If a match was found, value is a list (FOUND-ANCHOR POS MODE), where
772FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position
773where the match was found, and MODE is `major-mode' of the buffer in
774which the match was found.
775This function tries to find a case-sensitive match first, then a
776case-insensitive match is tried."
777 (let ((result (Info-find-in-tag-table-1 marker regexp nil)))
778 (when (null (car result))
779 (setq result (Info-find-in-tag-table-1 marker regexp t)))
780 result))
781
782(defun Info-find-node-in-buffer-1 (regexp case-fold)
783 "Find a node or anchor in the current buffer.
784REGEXP is a regular expression matching nodes or references. Its first
785group should match `Node:' or `Ref:'.
786CASE-FOLD t means search for a case-insensitive match.
787Value is the position at which a match was found, or nil if not found."
788 (let ((case-fold-search case-fold)
789 found)
790 (save-excursion
791 (when (Info-node-at-bob-matching regexp)
792 (setq found (point)))
793 (while (and (not found)
794 (search-forward "\n\^_" nil t))
795 (forward-line 1)
796 (let ((beg (point)))
797 (forward-line 1)
798 (when (re-search-backward regexp beg t)
799 (beginning-of-line)
800 (setq found (point)))))
801 found)))
921e5fe6 802
357d11de
GM
803(defun Info-find-node-in-buffer (regexp)
804 "Find a node or anchor in the current buffer.
805REGEXP is a regular expression matching nodes or references. Its first
806group should match `Node:' or `Ref:'.
807Value is the position at which a match was found, or nil if not found.
808This function looks for a case-sensitive match first. If none is found,
809a case-insensitive match is tried."
810 (or (Info-find-node-in-buffer-1 regexp nil)
811 (Info-find-node-in-buffer-1 regexp t)))
921e5fe6 812
4fceda3c 813(defun Info-find-node-2 (filename nodename &optional no-going-back)
9e5c2f50 814 (buffer-disable-undo (current-buffer))
a384cab3
JB
815 (or (eq major-mode 'Info-mode)
816 (Info-mode))
817 (widen)
818 (setq Info-current-node nil)
819 (unwind-protect
8bdf4b20
KH
820 (let ((case-fold-search t)
821 anchorpos)
37a3ff5b
RS
822 ;; Switch files if necessary
823 (or (null filename)
824 (equal Info-current-file filename)
e8dab975 825 (let ((inhibit-read-only t))
37a3ff5b
RS
826 (setq Info-current-file nil
827 Info-current-subfile nil
828 Info-current-file-completions nil
829 buffer-file-name nil)
830 (erase-buffer)
ab2f22ad
JH
831 (cond
832 ((eq filename t)
833 (Info-insert-dir))
834 ((eq filename 'apropos)
835 (insert-buffer-substring " *info-apropos*"))
89b064a9
JL
836 ((eq filename 'history)
837 (insert-buffer-substring " *info-history*"))
838 ((eq filename 'toc)
839 (insert-buffer-substring " *info-toc*"))
ab2f22ad 840 (t
f7d5479b 841 (info-insert-file-contents filename nil)
ab2f22ad 842 (setq default-directory (file-name-directory filename))))
37a3ff5b 843 (set-buffer-modified-p nil)
94b10022
JL
844
845 ;; Check makeinfo version for index cookie support
846 (let ((found nil))
847 (goto-char (point-min))
848 (condition-case ()
849 (if (and (re-search-forward
bd82e910 850 "makeinfo[ \n]version[ \n]\\([0-9]+.[0-9]+\\)"
94b10022
JL
851 (line-beginning-position 3) t)
852 (not (version< (match-string 1) "4.7")))
853 (setq found t))
854 (error nil))
855 (set (make-local-variable 'Info-file-supports-index-cookies) found))
856
37a3ff5b
RS
857 ;; See whether file has a tag table. Record the location if yes.
858 (goto-char (point-max))
859 (forward-line -8)
860 ;; Use string-equal, not equal, to ignore text props.
861 (if (not (or (string-equal nodename "*")
862 (not
863 (search-forward "\^_\nEnd tag table\n" nil t))))
864 (let (pos)
865 ;; We have a tag table. Find its beginning.
866 ;; Is this an indirect file?
867 (search-backward "\nTag table:\n")
868 (setq pos (point))
869 (if (save-excursion
870 (forward-line 2)
871 (looking-at "(Indirect)\n"))
872 ;; It is indirect. Copy it to another buffer
873 ;; and record that the tag table is in that buffer.
874 (let ((buf (current-buffer))
875 (tagbuf
876 (or Info-tag-table-buffer
877 (generate-new-buffer " *info tag table*"))))
878 (setq Info-tag-table-buffer tagbuf)
e8dab975 879 (with-current-buffer tagbuf
9e5c2f50 880 (buffer-disable-undo (current-buffer))
37a3ff5b
RS
881 (setq case-fold-search t)
882 (erase-buffer)
883 (insert-buffer-substring buf))
884 (set-marker Info-tag-table-marker
885 (match-end 0) tagbuf))
886 (set-marker Info-tag-table-marker pos)))
887 (set-marker Info-tag-table-marker nil))
888 (setq Info-current-file
ab2f22ad
JH
889 (cond
890 ((eq filename t) "dir")
ab2f22ad
JH
891 (t filename)))
892 ))
37a3ff5b
RS
893 ;; Use string-equal, not equal, to ignore text props.
894 (if (string-equal nodename "*")
895 (progn (setq Info-current-node nodename)
896 (Info-set-mode-line))
897 ;; Possibilities:
898 ;;
899 ;; 1. Anchor found in tag table
900 ;; 2. Anchor *not* in tag table
901 ;;
902 ;; 3. Node found in tag table
903 ;; 4. Node *not* found in tag table, but found in file
904 ;; 5. Node *not* in tag table, and *not* in file
905 ;;
906 ;; *Or* the same, but in an indirect subfile.
907
908 ;; Search file for a suitable node.
8bdf4b20 909 (let ((guesspos (point-min))
357d11de 910 (regexp (concat "\\(Node:\\|Ref:\\) *\\("
4ce5c223 911 (if (stringp nodename)
dd31e4e8
GM
912 (regexp-quote nodename)
913 "")
dde7f979 914 "\\) *[,\t\n\177]")))
37a3ff5b 915
82172606 916 (catch 'foo
921e5fe6 917
82172606 918 ;; First, search a tag table, if any
357d11de
GM
919 (when (marker-position Info-tag-table-marker)
920 (let* ((m Info-tag-table-marker)
921 (found (Info-find-in-tag-table m regexp)))
921e5fe6 922
357d11de
GM
923 (when found
924 ;; FOUND is (ANCHOR POS MODE).
925 (setq guesspos (nth 1 found))
921e5fe6 926
357d11de
GM
927 ;; If this is an indirect file, determine which
928 ;; file really holds this node and read it in.
929 (unless (eq (nth 2 found) 'Info-mode)
930 ;; Note that the current buffer must be the
931 ;; *info* buffer on entry to
932 ;; Info-read-subfile. Thus the hackery above.
933 (setq guesspos (Info-read-subfile guesspos)))
82172606
DL
934
935 ;; Handle anchor
357d11de
GM
936 (when (nth 0 found)
937 (goto-char (setq anchorpos guesspos))
938 (throw 'foo t)))))
82172606
DL
939
940 ;; Else we may have a node, which we search for:
941 (goto-char (max (point-min)
942 (- (byte-to-position guesspos) 1000)))
921e5fe6 943
357d11de
GM
944 ;; Now search from our advised position (or from beg of
945 ;; buffer) to find the actual node. First, check
946 ;; whether the node is right where we are, in case the
947 ;; buffer begins with a node.
948 (let ((pos (Info-find-node-in-buffer regexp)))
949 (when pos
950 (goto-char pos)
141b49f5
JL
951 (throw 'foo t)))
952
953 (when (string-match "\\([^.]+\\)\\." nodename)
954 (let (Info-point-loc)
955 (Info-find-node-2
956 filename (match-string 1 nodename) no-going-back))
957 (widen)
958 (throw 'foo t))
959
960 ;; No such anchor in tag table or node in tag table or file
961 (error "No such node or anchor: %s" nodename))
82172606
DL
962
963 (Info-select-node)
8a7757f6
JL
964 (goto-char (point-min))
965 (cond (anchorpos
966 (let ((new-history (list Info-current-file
967 (substring-no-properties nodename))))
968 ;; Add anchors to the history too
969 (setq Info-history-list
970 (cons new-history
971 (delete new-history Info-history-list))))
972 (goto-char anchorpos))
836bb478
JL
973 ((numberp Info-point-loc)
974 (forward-line (1- Info-point-loc))
975 (setq Info-point-loc nil))
976 ((stringp Info-point-loc)
977 (Info-find-index-name Info-point-loc)
978 (setq Info-point-loc nil))))))
a384cab3
JB
979 ;; If we did not finish finding the specified node,
980 ;; go back to the previous one.
4db579ab 981 (or Info-current-node no-going-back (null Info-history)
37a3ff5b
RS
982 (let ((hist (car Info-history)))
983 (setq Info-history (cdr Info-history))
984 (Info-find-node (nth 0 hist) (nth 1 hist) t)
985 (goto-char (nth 2 hist))))))
a384cab3 986
44c327f9
JB
987;; Cache the contents of the (virtual) dir file, once we have merged
988;; it for the first time, so we can save time subsequently.
7ea13762
RS
989(defvar Info-dir-contents nil)
990
44c327f9
JB
991;; Cache for the directory we decided to use for the default-directory
992;; of the merged dir text.
993(defvar Info-dir-contents-directory nil)
994
c142ab2d
RS
995;; Record the file attributes of all the files from which we
996;; constructed Info-dir-contents.
997(defvar Info-dir-file-attributes nil)
998
4fba3b2c
RS
999(defvar Info-dir-file-name nil)
1000
7ea13762 1001;; Construct the Info directory node by merging the files named `dir'
44c327f9
JB
1002;; from various directories. Set the *info* buffer's
1003;; default-directory to the first directory we actually get any text
1004;; from.
7ea13762 1005(defun Info-insert-dir ()
c142ab2d
RS
1006 (if (and Info-dir-contents Info-dir-file-attributes
1007 ;; Verify that none of the files we used has changed
1008 ;; since we used it.
1009 (eval (cons 'and
b461e32d
SM
1010 (mapcar (lambda (elt)
1011 (let ((curr (file-attributes
1012 ;; Handle symlinks
1013 (file-truename (car elt)))))
1014
1015 ;; Don't compare the access time.
1016 (if curr (setcar (nthcdr 4 curr) 0))
1017 (setcar (nthcdr 4 (cdr elt)) 0)
1018 (equal (cdr elt) curr)))
c142ab2d 1019 Info-dir-file-attributes))))
4fba3b2c
RS
1020 (progn
1021 (insert Info-dir-contents)
1022 (goto-char (point-min)))
dd31e4e8 1023 (let ((dirs (if Info-additional-directory-list
b34dcc82
RS
1024 (append Info-directory-list
1025 Info-additional-directory-list)
1026 Info-directory-list))
1bd19a31 1027 (dir-file-attrs nil)
1bcedb3b
RS
1028 ;; Bind this in case the user sets it to nil.
1029 (case-fold-search t)
4fba3b2c
RS
1030 ;; This is set non-nil if we find a problem in some input files.
1031 problems
f4008b6e 1032 buffers buffer others nodes dirs-done)
44c327f9
JB
1033
1034 ;; Search the directory list for the directory file.
7ea13762 1035 (while dirs
8d1abb42
RS
1036 (let ((truename (file-truename (expand-file-name (car dirs)))))
1037 (or (member truename dirs-done)
1038 (member (directory-file-name truename) dirs-done)
1039 ;; Try several variants of specified name.
1040 ;; Try upcasing, appending `.info', or both.
825d6f08
RS
1041 (let* (file
1042 (attrs
1043 (or
1044 (progn (setq file (expand-file-name "dir" truename))
1045 (file-attributes file))
1046 (progn (setq file (expand-file-name "DIR" truename))
1047 (file-attributes file))
1048 (progn (setq file (expand-file-name "dir.info" truename))
1049 (file-attributes file))
1050 (progn (setq file (expand-file-name "DIR.INFO" truename))
1051 (file-attributes file)))))
8d1abb42
RS
1052 (setq dirs-done
1053 (cons truename
1054 (cons (directory-file-name truename)
1055 dirs-done)))
825d6f08 1056 (if attrs
e8dab975 1057 (with-current-buffer (generate-new-buffer " info dir")
825d6f08
RS
1058 (or buffers
1059 (message "Composing main Info directory..."))
9d499629
RS
1060 (condition-case nil
1061 (progn
1062 (insert-file-contents file)
1bd19a31
MB
1063 (set (make-local-variable 'Info-dir-file-name)
1064 file)
6af7040d 1065 (push (current-buffer) buffers)
1bd19a31 1066 (push (cons file attrs) dir-file-attrs))
9d499629 1067 (error (kill-buffer (current-buffer))))))))
1bd19a31
MB
1068 (unless (cdr dirs)
1069 (set (make-local-variable 'Info-dir-contents-directory)
1070 (file-name-as-directory (car dirs))))
825d6f08 1071 (setq dirs (cdr dirs))))
399c88ad 1072
4db579ab 1073 (or buffers
81e14cb2 1074 (error "Can't find the Info directory node"))
1bd19a31 1075
44c327f9 1076 ;; Distinguish the dir file that comes with Emacs from all the
f4008b6e 1077 ;; others. Yes, that is really what this is supposed to do.
f601cd3e
DL
1078 ;; The definition of `Info-directory-list' puts it first on that
1079 ;; list and so last in `buffers' at this point.
1080 (setq buffer (car (last buffers))
1081 others (delq buffer buffers))
44c327f9 1082
debcea0c
KH
1083 ;; Insert the entire original dir file as a start; note that we've
1084 ;; already saved its default directory to use as the default
1085 ;; directory for the whole concatenation.
69672704 1086 (save-excursion (insert-buffer-substring buffer))
44c327f9 1087
7ea13762 1088 ;; Look at each of the other buffers one by one.
6af7040d
SM
1089 (dolist (other others)
1090 (let (this-buffer-nodes)
7ea13762 1091 ;; In each, find all the menus.
6af7040d 1092 (with-current-buffer other
7ea13762
RS
1093 (goto-char (point-min))
1094 ;; Find each menu, and add an elt to NODES for it.
1095 (while (re-search-forward "^\\* Menu:" nil t)
6af7040d
SM
1096 (while (and (zerop (forward-line 1)) (eolp)))
1097 (let ((beg (point))
1098 nodename end)
1099 (re-search-backward "^\^_")
7ea13762 1100 (search-forward "Node: ")
89b064a9 1101 (setq nodename (Info-following-node-name))
3a6ade8a 1102 (search-forward "\n\^_" nil 'move)
7ea13762
RS
1103 (beginning-of-line)
1104 (setq end (point))
6af7040d 1105 (push (list nodename other beg end) this-buffer-nodes)))
9f4aee27 1106 (if (assoc-string "top" this-buffer-nodes t)
4fba3b2c
RS
1107 (setq nodes (nconc this-buffer-nodes nodes))
1108 (setq problems t)
6af7040d 1109 (message "No `top' node in %s" Info-dir-file-name)))))
fdf4b680 1110 ;; Add to the main menu a menu item for each other node.
6af7040d 1111 (re-search-forward "^\\* Menu:")
7ea13762
RS
1112 (forward-line 1)
1113 (let ((menu-items '("top"))
3a6ade8a 1114 (end (save-excursion (search-forward "\^_" nil t) (point))))
6af7040d
SM
1115 (dolist (node nodes)
1116 (let ((nodename (car node)))
c3a29d70
RS
1117 (save-excursion
1118 (or (member (downcase nodename) menu-items)
b37daea4 1119 (re-search-forward (concat "^\\* +"
c3a29d70
RS
1120 (regexp-quote nodename)
1121 "::")
1122 end t)
1123 (progn
1124 (insert "* " nodename "::" "\n")
6af7040d 1125 (push nodename menu-items)))))))
7ea13762
RS
1126 ;; Now take each node of each of the other buffers
1127 ;; and merge it into the main buffer.
6af7040d 1128 (dolist (node nodes)
760d5cb3 1129 (let ((case-fold-search t)
6af7040d 1130 (nodename (car node)))
7ea13762
RS
1131 (goto-char (point-min))
1132 ;; Find the like-named node in the main buffer.
ba42ce14 1133 (if (re-search-forward (concat "^\^_.*\n.*Node: "
7ea13762
RS
1134 (regexp-quote nodename)
1135 "[,\n\t]")
1136 nil t)
1137 (progn
3a6ade8a 1138 (search-forward "\n\^_" nil 'move)
25869acf
RS
1139 (beginning-of-line)
1140 (insert "\n"))
7ea13762
RS
1141 ;; If none exists, add one.
1142 (goto-char (point-max))
dfbf6104 1143 (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
7ea13762
RS
1144 ;; Merge the text from the other buffer's menu
1145 ;; into the menu in the like-named node in the main buffer.
6af7040d
SM
1146 (apply 'insert-buffer-substring (cdr node))))
1147 (Info-dir-remove-duplicates)
dc9bec22
TTN
1148 ;; Kill all the buffers we just made, including the special one excised.
1149 (mapc 'kill-buffer (cons buffer buffers))
4fba3b2c
RS
1150 (goto-char (point-min))
1151 (if problems
1152 (message "Composing main Info directory...problems encountered, see `*Messages*'")
1bd19a31
MB
1153 (message "Composing main Info directory...done"))
1154 (set (make-local-variable 'Info-dir-contents) (buffer-string))
1155 (set (make-local-variable 'Info-dir-file-attributes) dir-file-attrs)))
44c327f9 1156 (setq default-directory Info-dir-contents-directory))
7ea13762 1157
6af7040d
SM
1158(defvar Info-streamline-headings
1159 '(("Emacs" . "Emacs")
1160 ("Programming" . "Programming")
1161 ("Libraries" . "Libraries")
1162 ("World Wide Web\\|Net Utilities" . "Net Utilities"))
1163 "List of elements (RE . NAME) to merge headings matching RE to NAME.")
1164
1165(defun Info-dir-remove-duplicates ()
1166 (let (limit)
1167 (goto-char (point-min))
1168 ;; Remove duplicate headings in the same menu.
1169 (while (search-forward "\n* Menu:" nil t)
141b49f5 1170 (setq limit (save-excursion (search-forward "\n\^_" nil t)))
6af7040d
SM
1171 ;; Look for the next heading to unify.
1172 (while (re-search-forward "^\\(\\w.*\\)\n\\*" limit t)
1173 (let ((name (match-string 1))
1174 (start (match-beginning 0))
1175 (entries nil) re)
1176 ;; Check whether this heading should be streamlined.
1177 (save-match-data
1178 (dolist (x Info-streamline-headings)
1179 (when (string-match (car x) name)
1180 (setq name (cdr x))
1181 (setq re (car x)))))
1182 (if re (replace-match name t t nil 1))
1183 (goto-char (if (re-search-forward "^[^* \n\t]" limit t)
1184 (match-beginning 0)
1185 (or limit (point-max))))
1186 ;; Look for other headings of the same category and merge them.
1187 (save-excursion
1188 (while (re-search-forward "^\\(\\w.*\\)\n\\*" limit t)
1189 (when (if re (save-match-data (string-match re (match-string 1)))
1190 (equal name (match-string 1)))
1191 (forward-line 0)
1192 ;; Delete redundant heading.
1193 (delete-region (match-beginning 0) (point))
1194 ;; Push the entries onto `text'.
1195 (push
1196 (delete-and-extract-region
1197 (point)
1198 (if (re-search-forward "^[^* \n\t]" nil t)
1199 (match-beginning 0)
2d19a2a4
JL
1200 (or limit (point-max))))
1201 entries)
1202 (forward-line 0))))
6af7040d
SM
1203 ;; Insert the entries just found.
1204 (while (= (line-beginning-position 0) (1- (point)))
1205 (backward-char))
1206 (dolist (entry (nreverse entries))
1207 (insert entry)
1208 (while (= (line-beginning-position 0) (1- (point)))
1209 (delete-region (1- (point)) (point))))
921e5fe6 1210
6af7040d
SM
1211 ;; Now remove duplicate entries under the same heading.
1212 (let ((seen nil)
2d19a2a4 1213 (limit (point-marker)))
6af7040d 1214 (goto-char start)
2d19a2a4
JL
1215 (while (and (> limit (point))
1216 (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)"
1217 limit 'move))
b499789c
TTN
1218 ;; Fold case straight away; `member-ignore-case' here wasteful.
1219 (let ((x (downcase (match-string 1))))
1220 (if (member x seen)
6af7040d
SM
1221 (delete-region (match-beginning 0)
1222 (progn (re-search-forward "^[^ \t]" nil t)
25046503 1223 (match-beginning 0)))
6af7040d
SM
1224 (push x seen))))))))))
1225
c5fe2ff1
RS
1226;; Note that on entry to this function the current-buffer must be the
1227;; *info* buffer; not the info tags buffer.
a384cab3 1228(defun Info-read-subfile (nodepos)
a361deba
RS
1229 ;; NODEPOS is either a position (in the Info file as a whole,
1230 ;; not relative to a subfile) or the name of a subfile.
a384cab3
JB
1231 (let (lastfilepos
1232 lastfilename)
a361deba 1233 (if (numberp nodepos)
e8dab975 1234 (with-current-buffer (marker-buffer Info-tag-table-marker)
a361deba 1235 (goto-char (point-min))
ba42ce14
EZ
1236 (or (looking-at "\^_")
1237 (search-forward "\n\^_"))
a361deba
RS
1238 (forward-line 2)
1239 (catch 'foo
1240 (while (not (looking-at "\^_"))
1241 (if (not (eolp))
1242 (let ((beg (point))
1243 thisfilepos thisfilename)
1244 (search-forward ": ")
1245 (setq thisfilename (buffer-substring beg (- (point) 2)))
365d2503 1246 (setq thisfilepos (+ (point-min) (read (current-buffer))))
a361deba
RS
1247 ;; read in version 19 stops at the end of number.
1248 ;; Advance to the next line.
1249 (forward-line 1)
1250 (if (> thisfilepos nodepos)
1251 (throw 'foo t))
1252 (setq lastfilename thisfilename)
1253 (setq lastfilepos thisfilepos))
1254 (forward-line 1)))))
1255 (setq lastfilename nodepos)
1256 (setq lastfilepos 0))
c5fe2ff1
RS
1257 ;; Assume previous buffer is in Info-mode.
1258 ;; (set-buffer (get-buffer "*info*"))
a384cab3 1259 (or (equal Info-current-subfile lastfilename)
e8dab975 1260 (let ((inhibit-read-only t))
a384cab3
JB
1261 (setq buffer-file-name nil)
1262 (widen)
1263 (erase-buffer)
1143a6b0 1264 (info-insert-file-contents lastfilename)
a384cab3
JB
1265 (set-buffer-modified-p nil)
1266 (setq Info-current-subfile lastfilename)))
b0e52598
RS
1267 ;; Widen in case we are in the same subfile as before.
1268 (widen)
a384cab3 1269 (goto-char (point-min))
ba42ce14
EZ
1270 (if (looking-at "\^_")
1271 (forward-char 1)
1272 (search-forward "\n\^_"))
a361deba
RS
1273 (if (numberp nodepos)
1274 (+ (- nodepos lastfilepos) (point)))))
a384cab3 1275
ec9b1372 1276(defun Info-unescape-quotes (value)
1ed6e0c3 1277 "Unescape double quotes and backslashes in VALUE."
ec9b1372
KB
1278 (let ((start 0)
1279 (unquote value))
1280 (while (string-match "[^\\\"]*\\(\\\\\\)[\\\\\"]" unquote start)
1281 (setq unquote (replace-match "" t t unquote 1))
1282 (setq start (- (match-end 0) 1)))
1283 unquote))
1284
1285;; As of Texinfo 4.6, makeinfo writes constructs like
1286;; \0\h[image param=value ...\h\0]
1287;; into the Info file for handling images.
1288(defun Info-split-parameter-string (parameter-string)
1289 "Return alist of (\"KEY\" . \"VALUE\") from PARAMETER-STRING; a
1ed6e0c3
JPW
1290whitespace separated list of KEY=VALUE pairs. If VALUE contains
1291whitespace or double quotes, it must be quoted in double quotes and
1292any double quotes or backslashes must be escaped (\\\",\\\\)."
ec9b1372
KB
1293 (let ((start 0)
1294 (parameter-alist))
1295 (while (string-match
1296 "\\s *\\([^=]+\\)=\\(?:\\([^\\s \"]+\\)\\|\\(?:\"\\(\\(?:[^\\\"]\\|\\\\[\\\\\"]\\)*\\)\"\\)\\)"
1297 parameter-string start)
1298 (setq start (match-end 0))
1299 (push (cons (match-string 1 parameter-string)
1300 (or (match-string 2 parameter-string)
1301 (Info-unescape-quotes
1302 (match-string 3 parameter-string))))
1303 parameter-alist))
1304 parameter-alist))
1305
1306(defun Info-display-images-node ()
1307 "Display images in current node."
1308 (save-excursion
1309 (let ((inhibit-read-only t)
dde7f979 1310 (case-fold-search t))
ec9b1372
KB
1311 (goto-char (point-min))
1312 (while (re-search-forward
1313 "\\(\0\b[[]image\\(\\(?:[^\b]\\|[^\0]+\b\\)*\\)\0\b[]]\\)"
1314 nil t)
1315 (let* ((start (match-beginning 1))
1316 (parameter-alist (Info-split-parameter-string (match-string 2)))
d3425b35
NR
1317 (src (cdr (assoc-string "src" parameter-alist))))
1318 (if (display-images-p)
1319 (let* ((image-file (if src (if (file-name-absolute-p src) src
1320 (concat default-directory src))
1321 ""))
1322 (image (if (file-exists-p image-file)
1323 (create-image image-file)
1324 "[broken image]")))
1325 (if (not (get-text-property start 'display))
1326 (add-text-properties
1327 start (point) `(display ,image rear-nonsticky (display)))))
1328 ;; text-only display, show alternative text if provided, or
1329 ;; otherwise a clue that there's meant to be a picture
1330 (delete-region start (point))
1331 (insert (or (cdr (assoc-string "text" parameter-alist))
1332 (cdr (assoc-string "alt" parameter-alist))
1333 (and src
1334 (concat "[image:" src "]"))
1335 "[image]"))))))
ec9b1372
KB
1336 (set-buffer-modified-p nil)))
1337
0c1b7af5
JL
1338;; Texinfo 4.7 adds cookies of the form ^@^H[NAME CONTENTS ^@^H].
1339;; Hide any construct of the general form ^@[^@-^_][ ... ^@[^@-^_]],
1340;; including one optional trailing newline.
1341(defun Info-hide-cookies-node ()
e37c1854 1342 "Hide unrecognized cookies in current node."
0c1b7af5
JL
1343 (save-excursion
1344 (let ((inhibit-read-only t)
1345 (case-fold-search t))
1346 (goto-char (point-min))
1347 (while (re-search-forward
1348 "\\(\0[\0-\37][[][^\0]*\0[\0-\37][]]\n?\\)"
1349 nil t)
1350 (let* ((start (match-beginning 1)))
1351 (if (not (get-text-property start 'invisible))
1352 (put-text-property start (point) 'invisible t)))))
1353 (set-buffer-modified-p nil)))
1354
a384cab3 1355(defun Info-select-node ()
185cff95 1356 "Select the Info node that point is in."
b038485e 1357 ;; Bind this in case the user sets it to nil.
1bcedb3b
RS
1358 (let ((case-fold-search t))
1359 (save-excursion
760d5cb3
GM
1360 ;; Find beginning of node.
1361 (if (search-backward "\n\^_" nil 'move)
1362 (forward-line 2)
1363 (if (looking-at "\^_")
1364 (forward-line 1)
1365 (signal 'search-failed (list "\n\^_"))))
1366 ;; Get nodename spelled as it is in the node.
1367 (re-search-forward "Node:[ \t]*")
1368 (setq Info-current-node
1369 (buffer-substring-no-properties (point)
1370 (progn
1371 (skip-chars-forward "^,\t\n")
1372 (point))))
1373 (Info-set-mode-line)
1374 ;; Find the end of it, and narrow.
1375 (beginning-of-line)
1376 (let (active-expression)
50ac70af 1377 ;; Narrow to the node contents
760d5cb3
GM
1378 (narrow-to-region (point)
1379 (if (re-search-forward "\n[\^_\f]" nil t)
1380 (prog1
1381 (1- (point))
1382 (if (looking-at "[\n\^_\f]*execute: ")
1383 (progn
1384 (goto-char (match-end 0))
1385 (setq active-expression
1386 (read (current-buffer))))))
1387 (point-max)))
1388 (if Info-enable-active-nodes (eval active-expression))
8a7757f6
JL
1389 ;; Add a new unique history item to full history list
1390 (let ((new-history (list Info-current-file Info-current-node)))
1391 (setq Info-history-list
424b9d3e
JL
1392 (cons new-history (delete new-history Info-history-list)))
1393 (setq Info-history-forward nil))
141b49f5
JL
1394 (if (not (eq Info-fontify-maximum-menu-size nil))
1395 (Info-fontify-node))
ec9b1372 1396 (Info-display-images-node)
0c1b7af5 1397 (Info-hide-cookies-node)
760d5cb3 1398 (run-hooks 'Info-selection-hook)))))
a384cab3 1399
8eafa165
JL
1400(defvar Info-mode-line-node-keymap
1401 (let ((map (make-sparse-keymap)))
8aab8422
CY
1402 (define-key map [mode-line mouse-1] 'Info-mouse-scroll-up)
1403 (define-key map [mode-line mouse-3] 'Info-mouse-scroll-down)
8eafa165
JL
1404 map)
1405 "Keymap to put on the Info node name in the mode line.")
1406
a384cab3
JB
1407(defun Info-set-mode-line ()
1408 (setq mode-line-buffer-identification
313fe58a 1409 (nconc (propertized-buffer-identification "%b")
918e364a 1410 (list
8eafa165
JL
1411 (concat
1412 " ("
1413 (if (stringp Info-current-file)
fd5b25da
JL
1414 (replace-regexp-in-string
1415 "%" "%%" (file-name-nondirectory Info-current-file))
bec275ed 1416 (format "*%S*" Info-current-file))
8eafa165
JL
1417 ") "
1418 (if Info-current-node
fd5b25da
JL
1419 (propertize (replace-regexp-in-string
1420 "%" "%%" Info-current-node)
8eafa165
JL
1421 'face 'mode-line-buffer-id
1422 'help-echo
1423 "mouse-1: scroll forward, mouse-3: scroll back"
1424 'mouse-face 'mode-line-highlight
1425 'local-map Info-mode-line-node-keymap)
1426 ""))))))
a384cab3 1427\f
185cff95 1428;; Go to an Info node specified with a filename-and-nodename string
a384cab3
JB
1429;; of the sort that is found in pointers in nodes.
1430
83040cb0
SM
1431;; Don't autoload this function: the correct entry point for other packages
1432;; to use is `info'. --Stef
1433;; ;;;###autoload
4fceda3c 1434(defun Info-goto-node (nodename &optional fork)
185cff95 1435 "Go to Info node named NODENAME. Give just NODENAME or (FILENAME)NODENAME.
dc414be1
EZ
1436If NODENAME is of the form (FILENAME)NODENAME, the node is in the Info file
1437FILENAME; otherwise, NODENAME should be in the current Info file (or one of
1438its sub-files).
1439Completion is available, but only for node names in the current Info file.
7210b6f5 1440If FORK is non-nil (interactively with a prefix arg), show the node in
185cff95 1441a new Info buffer.
4fceda3c 1442If FORK is a string, it is the name to use for the new buffer."
d1268e52 1443 (interactive (list (Info-read-node-name "Go to node: ") current-prefix-arg))
d0e41cba 1444 (info-initialize)
4fceda3c 1445 (if fork
760d5cb3
GM
1446 (set-buffer
1447 (clone-buffer (concat "*info-" (if (stringp fork) fork nodename) "*") t)))
a384cab3
JB
1448 (let (filename)
1449 (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
1450 nodename)
1451 (setq filename (if (= (match-beginning 1) (match-end 1))
1452 ""
c60c12be
SM
1453 (match-string 2 nodename))
1454 nodename (match-string 3 nodename))
836bb478 1455 (let ((trim (string-match "\\s +\\'" filename)))
a384cab3 1456 (if trim (setq filename (substring filename 0 trim))))
836bb478 1457 (let ((trim (string-match "\\s +\\'" nodename)))
a384cab3 1458 (if trim (setq nodename (substring nodename 0 trim))))
e579232c 1459 (if transient-mark-mode (deactivate-mark))
a384cab3
JB
1460 (Info-find-node (if (equal filename "") nil filename)
1461 (if (equal nodename "") "Top" nodename))))
552775bd 1462
ddf89211
RS
1463(defvar Info-read-node-completion-table)
1464
e8dab975 1465(defun Info-read-node-name-2 (dirs suffixes string pred action)
a89da416
RS
1466 "Virtual completion table for file names input in Info node names.
1467PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
e8dab975
SM
1468 (setq suffixes (remove "" suffixes))
1469 (when (file-name-absolute-p string)
1470 (setq dirs (list (file-name-directory string))))
1471 (let ((names nil)
1472 (suffix (concat (regexp-opt suffixes t) "\\'"))
1473 (string-dir (file-name-directory string)))
a89da416
RS
1474 (dolist (dir dirs)
1475 (unless dir
1476 (setq dir default-directory))
1477 (if string-dir (setq dir (expand-file-name string-dir dir)))
1478 (when (file-directory-p dir)
1479 (dolist (file (file-name-all-completions
1480 (file-name-nondirectory string) dir))
1481 ;; If the file name has no suffix or a standard suffix,
1482 ;; include it.
1483 (and (or (null (file-name-extension file))
1484 (string-match suffix file))
185cff95 1485 ;; But exclude subfiles of split Info files.
a89da416
RS
1486 (not (string-match "-[0-9]+\\'" file))
1487 ;; And exclude backup files.
1488 (not (string-match "~\\'" file))
1489 (push (if string-dir (concat string-dir file) file) names))
1490 ;; If the file name ends in a standard suffix,
1491 ;; add the unsuffixed name as a completion option.
1492 (when (string-match suffix file)
1493 (setq file (substring file 0 (match-beginning 0)))
1494 (push (if string-dir (concat string-dir file) file) names)))))
e8dab975 1495 (complete-with-action action names string pred)))
a89da416 1496
03524be6 1497;; This function is used as the "completion table" while reading a node name.
ddf89211 1498;; It does completion using the alist in Info-read-node-completion-table
03524be6
RS
1499;; unless STRING starts with an open-paren.
1500(defun Info-read-node-name-1 (string predicate code)
365d2503
SM
1501 (cond
1502 ;; First complete embedded file names.
1503 ((string-match "\\`([^)]*\\'" string)
e2947429
SM
1504 (completion-table-with-context
1505 "("
e8dab975
SM
1506 (apply-partially 'completion-table-with-terminator ")"
1507 (apply-partially 'Info-read-node-name-2
1508 Info-directory-list
1509 (mapcar 'car Info-suffix-list)))
e2947429 1510 (substring string 1)
e8dab975 1511 predicate
e2947429
SM
1512 code))
1513
365d2503
SM
1514 ;; If a file name was given, then any node is fair game.
1515 ((string-match "\\`(" string)
1516 (cond
1517 ((eq code nil) string)
1518 ((eq code t) nil)
1519 (t t)))
1520 ;; Otherwise use Info-read-node-completion-table.
e2947429
SM
1521 (t (complete-with-action
1522 code Info-read-node-completion-table string predicate))))
03524be6 1523
a89da416 1524;; Arrange to highlight the proper letters in the completion list buffer.
e2947429 1525
a89da416 1526
f75d05eb 1527(defun Info-read-node-name (prompt)
552775bd 1528 (let* ((completion-ignore-case t)
ddf89211 1529 (Info-read-node-completion-table (Info-build-node-completions))
b6d61ffa 1530 (nodename (completing-read prompt 'Info-read-node-name-1 nil t)))
552775bd 1531 (if (equal nodename "")
f75d05eb 1532 (Info-read-node-name prompt)
552775bd
RS
1533 nodename)))
1534
1535(defun Info-build-node-completions ()
1536 (or Info-current-file-completions
1bcedb3b
RS
1537 (let ((compl nil)
1538 ;; Bind this in case the user sets it to nil.
11638562
EZ
1539 (case-fold-search t)
1540 (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]"))
552775bd
RS
1541 (save-excursion
1542 (save-restriction
746c9f14
CY
1543 (or Info-tag-table-marker
1544 (error "No Info tags found"))
552775bd 1545 (if (marker-buffer Info-tag-table-marker)
c5fe2ff1
RS
1546 (let ((marker Info-tag-table-marker))
1547 (set-buffer (marker-buffer marker))
cedb118c 1548 (widen)
c5fe2ff1 1549 (goto-char marker)
fdf4b680 1550 (while (re-search-forward "\n\\(Node\\|Ref\\): \\(.*\\)\177" nil t)
552775bd 1551 (setq compl
fdf4b680 1552 (cons (list (match-string-no-properties 2))
552775bd
RS
1553 compl))))
1554 (widen)
1555 (goto-char (point-min))
11638562
EZ
1556 ;; If the buffer begins with a node header, process that first.
1557 (if (Info-node-at-bob-matching node-regexp)
110d4e80 1558 (setq compl (list (match-string-no-properties 1))))
11638562 1559 ;; Now for the rest of the nodes.
552775bd
RS
1560 (while (search-forward "\n\^_" nil t)
1561 (forward-line 1)
1562 (let ((beg (point)))
1563 (forward-line 1)
11638562 1564 (if (re-search-backward node-regexp beg t)
399c88ad 1565 (setq compl
110d4e80 1566 (cons (list (match-string-no-properties 1))
552775bd 1567 compl))))))))
87d2b5a2 1568 (setq compl (cons '("*") compl))
07b1b912 1569 (set (make-local-variable 'Info-current-file-completions) compl))))
a384cab3 1570\f
aea2a8da
JB
1571(defun Info-restore-point (hl)
1572 "If this node has been visited, restore the point value when we left."
cedb118c
RS
1573 (while hl
1574 (if (and (equal (nth 0 (car hl)) Info-current-file)
e90d1271
RS
1575 ;; Use string-equal, not equal, to ignore text props.
1576 (string-equal (nth 1 (car hl)) Info-current-node))
cedb118c 1577 (progn
d96504df
KH
1578 (goto-char (nth 2 (car hl)))
1579 (setq hl nil)) ;terminate the while at next iter
cedb118c 1580 (setq hl (cdr hl)))))
aea2a8da 1581\f
2c80a618
GM
1582(defvar Info-search-history nil
1583 "The history list for `Info-search'.")
a384cab3 1584
8a7757f6
JL
1585(defvar Info-search-case-fold nil
1586 "The value of `case-fold-search' from previous `Info-search' command.")
1587
0b02cda9
JL
1588(defun Info-search (regexp &optional bound noerror count direction)
1589 "Search for REGEXP, starting from point, and select node it's found in.
1590If DIRECTION is `backward', search in the reverse direction."
bbc96600
EZ
1591 (interactive (list (read-string
1592 (if Info-search-history
5b76833f
RF
1593 (format "Regexp search%s (default %s): "
1594 (if case-fold-search "" " case-sensitively")
bbc96600 1595 (car Info-search-history))
8a7757f6 1596 (format "Regexp search%s: "
5b76833f 1597 (if case-fold-search "" " case-sensitively")))
bbc96600 1598 nil 'Info-search-history)))
2c80a618
GM
1599 (when transient-mark-mode
1600 (deactivate-mark))
1601 (when (equal regexp "")
1602 (setq regexp (car Info-search-history)))
c47b5bbe 1603 (when regexp
774f8aee 1604 (let (found beg-found give-up
0b02cda9 1605 (backward (eq direction 'backward))
c47b5bbe
DL
1606 (onode Info-current-node)
1607 (ofile Info-current-file)
1608 (opoint (point))
0b02cda9
JL
1609 (opoint-min (point-min))
1610 (opoint-max (point-max))
c47b5bbe
DL
1611 (ostart (window-start))
1612 (osubfile Info-current-subfile))
8a7757f6 1613 (setq Info-search-case-fold case-fold-search)
c47b5bbe
DL
1614 (save-excursion
1615 (save-restriction
1616 (widen)
ca53db33
JL
1617 (when backward
1618 ;; Hide Info file header for backward search
1619 (narrow-to-region (save-excursion
1620 (goto-char (point-min))
1621 (search-forward "\n\^_")
1622 (1- (point)))
1623 (point-max)))
774f8aee 1624 (while (and (not give-up)
0ec3382b
JL
1625 (or (null found)
1626 (not (funcall isearch-success-function beg-found found))))
9ca9f7f3
JL
1627 (let ((search-spaces-regexp
1628 (if (or (not isearch-mode) isearch-regexp)
1629 Info-search-whitespace-regexp)))
5d4faeda
RS
1630 (if (if backward
1631 (re-search-backward regexp bound t)
1632 (re-search-forward regexp bound t))
1633 (setq found (point) beg-found (if backward (match-end 0)
1634 (match-beginning 0)))
1635 (setq give-up t))))))
d457219d
JL
1636
1637 (when (and isearch-mode Info-isearch-search
1638 (not Info-isearch-initial-node)
1639 (not bound)
1640 (or give-up (and found (not (and (> found opoint-min)
1641 (< found opoint-max))))))
1642 (signal 'search-failed (list regexp "initial node")))
1643
774f8aee
RS
1644 ;; If no subfiles, give error now.
1645 (if give-up
c47b5bbe 1646 (if (null Info-current-subfile)
9ca9f7f3
JL
1647 (let ((search-spaces-regexp
1648 (if (or (not isearch-mode) isearch-regexp)
1649 Info-search-whitespace-regexp)))
180ee7c7
JL
1650 (if backward
1651 (re-search-backward regexp)
1652 (re-search-forward regexp)))
774f8aee
RS
1653 (setq found nil)))
1654
d457219d
JL
1655 (if (and bound (not found))
1656 (signal 'search-failed (list regexp)))
1657
0b02cda9 1658 (unless (or found bound)
774f8aee
RS
1659 (unwind-protect
1660 ;; Try other subfiles.
1661 (let ((list ()))
e8dab975 1662 (with-current-buffer (marker-buffer Info-tag-table-marker)
774f8aee
RS
1663 (goto-char (point-min))
1664 (search-forward "\n\^_\nIndirect:")
1665 (save-restriction
1666 (narrow-to-region (point)
1667 (progn (search-forward "\n\^_")
1668 (1- (point))))
c5fe2ff1 1669 (goto-char (point-min))
774f8aee
RS
1670 ;; Find the subfile we just searched.
1671 (search-forward (concat "\n" osubfile ": "))
1672 ;; Skip that one.
0b02cda9 1673 (forward-line (if backward 0 1))
866920e3 1674 (if backward (forward-char -1))
774f8aee
RS
1675 ;; Make a list of all following subfiles.
1676 ;; Each elt has the form (VIRT-POSITION . SUBFILENAME).
0b02cda9
JL
1677 (while (not (if backward (bobp) (eobp)))
1678 (if backward
1679 (re-search-backward "\\(^.*\\): [0-9]+$")
1680 (re-search-forward "\\(^.*\\): [0-9]+$"))
774f8aee
RS
1681 (goto-char (+ (match-end 1) 2))
1682 (setq list (cons (cons (+ (point-min)
1683 (read (current-buffer)))
1684 (match-string-no-properties 1))
1685 list))
0b02cda9
JL
1686 (goto-char (if backward
1687 (1- (match-beginning 0))
1688 (1+ (match-end 0)))))
774f8aee
RS
1689 ;; Put in forward order
1690 (setq list (nreverse list))))
1691 (while list
1692 (message "Searching subfile %s..." (cdr (car list)))
1693 (Info-read-subfile (car (car list)))
ca53db33
JL
1694 (when backward
1695 ;; Hide Info file header for backward search
1696 (narrow-to-region (save-excursion
1697 (goto-char (point-min))
1698 (search-forward "\n\^_")
1699 (1- (point)))
1700 (point-max))
1701 (goto-char (point-max)))
774f8aee
RS
1702 (setq list (cdr list))
1703 (setq give-up nil found nil)
1704 (while (and (not give-up)
0ec3382b
JL
1705 (or (null found)
1706 (not (funcall isearch-success-function beg-found found))))
9ca9f7f3
JL
1707 (let ((search-spaces-regexp
1708 (if (or (not isearch-mode) isearch-regexp)
1709 Info-search-whitespace-regexp)))
180ee7c7
JL
1710 (if (if backward
1711 (re-search-backward regexp nil t)
1712 (re-search-forward regexp nil t))
1713 (setq found (point) beg-found (if backward (match-end 0)
1714 (match-beginning 0)))
1715 (setq give-up t))))
774f8aee
RS
1716 (if give-up
1717 (setq found nil))
c47b5bbe 1718 (if found
774f8aee
RS
1719 (setq list nil)))
1720 (if found
1721 (message "")
1722 (signal 'search-failed (list regexp))))
1723 (if (not found)
1724 (progn (Info-read-subfile osubfile)
1725 (goto-char opoint)
1726 (Info-select-node)
1727 (set-window-start (selected-window) ostart)))))
0b02cda9
JL
1728
1729 (if (and (string= osubfile Info-current-subfile)
1730 (> found opoint-min)
1731 (< found opoint-max))
1732 ;; Search landed in the same node
1733 (goto-char found)
1734 (widen)
1735 (goto-char found)
1736 (save-match-data (Info-select-node)))
1737
760d5cb3
GM
1738 ;; Use string-equal, not equal, to ignore text props.
1739 (or (and (string-equal onode Info-current-node)
1740 (equal ofile Info-current-file))
866920e3
JL
1741 (and isearch-mode isearch-wrapped
1742 (eq opoint (if isearch-forward opoint-min opoint-max)))
760d5cb3
GM
1743 (setq Info-history (cons (list ofile onode opoint)
1744 Info-history))))))
8a7757f6
JL
1745
1746(defun Info-search-case-sensitively ()
1747 "Search for a regexp case-sensitively."
1748 (interactive)
1749 (let ((case-fold-search nil))
1750 (call-interactively 'Info-search)))
1751
1752(defun Info-search-next ()
1753 "Search for next regexp from a previous `Info-search' command."
1754 (interactive)
1755 (let ((case-fold-search Info-search-case-fold))
1756 (if Info-search-history
1757 (Info-search (car Info-search-history))
1758 (call-interactively 'Info-search))))
0b02cda9
JL
1759
1760(defun Info-search-backward (regexp &optional bound noerror count)
1761 "Search for REGEXP in the reverse direction."
1762 (interactive (list (read-string
1763 (if Info-search-history
5b76833f
RF
1764 (format "Regexp search%s backward (default %s): "
1765 (if case-fold-search "" " case-sensitively")
0b02cda9
JL
1766 (car Info-search-history))
1767 (format "Regexp search%s backward: "
5b76833f 1768 (if case-fold-search "" " case-sensitively")))
0b02cda9
JL
1769 nil 'Info-search-history)))
1770 (Info-search regexp bound noerror count 'backward))
1771
1772(defun Info-isearch-search ()
9553cf63 1773 (if Info-isearch-search
98e77be5 1774 (lambda (string &optional bound noerror count)
d457219d
JL
1775 (if isearch-word
1776 (Info-search (concat "\\b" (replace-regexp-in-string
69672704 1777 "\\W+" "\\W+"
d457219d 1778 (replace-regexp-in-string
69672704
JL
1779 "^\\W+\\|\\W+$" "" string)
1780 nil t)
1781 "\\b")
d457219d
JL
1782 bound noerror count
1783 (unless isearch-forward 'backward))
1784 (Info-search (if isearch-regexp string (regexp-quote string))
1785 bound noerror count
98db7a38
JB
1786 (unless isearch-forward 'backward)))
1787 (point))
98e77be5
JL
1788 (let ((isearch-search-fun-function nil))
1789 (isearch-search-fun))))
0b02cda9
JL
1790
1791(defun Info-isearch-wrap ()
d457219d
JL
1792 (if Info-isearch-search
1793 (if Info-isearch-initial-node
1794 (progn
1795 (if isearch-forward (Info-top-node) (Info-final-node))
1796 (goto-char (if isearch-forward (point-min) (point-max))))
1797 (setq Info-isearch-initial-node Info-current-node)
1798 (setq isearch-wrapped nil))
0b02cda9
JL
1799 (goto-char (if isearch-forward (point-min) (point-max)))))
1800
1801(defun Info-isearch-push-state ()
1802 `(lambda (cmd)
bec275ed 1803 (Info-isearch-pop-state cmd ',Info-current-file ',Info-current-node)))
0b02cda9
JL
1804
1805(defun Info-isearch-pop-state (cmd file node)
bec275ed
JL
1806 (or (and (equal Info-current-file file)
1807 (equal Info-current-node node))
0b02cda9
JL
1808 (progn (Info-find-node file node) (sit-for 0))))
1809
d457219d
JL
1810(defun Info-isearch-start ()
1811 (setq Info-isearch-initial-node nil))
0ec3382b
JL
1812
1813(defun Info-search-success-function (beg-found found)
1814 "Skip invisible text, node header line and Tag Table node."
1815 (save-match-data
1816 (let ((backward (< found beg-found)))
1817 (not
1818 (or
1819 (if backward
1820 (or (text-property-not-all found beg-found 'invisible nil)
1821 (text-property-not-all found beg-found 'display nil))
1822 (or (text-property-not-all beg-found found 'invisible nil)
1823 (text-property-not-all beg-found found 'display nil)))
1824 ;; Skip node header line
1825 (and (save-excursion (forward-line -1)
1826 (looking-at "\^_"))
1827 (forward-line (if backward -1 1)))
1828 ;; Skip Tag Table node
1829 (save-excursion
1830 (and (search-backward "\^_" nil t)
1831 (looking-at
1832 "\^_\n\\(Tag Table\\|Local Variables\\)"))))))))
1833
a384cab3 1834\f
a384cab3 1835(defun Info-extract-pointer (name &optional errorname)
fdf4b680
DL
1836 "Extract the value of the node-pointer named NAME.
1837If there is none, use ERRORNAME in the error message;
c60c12be
SM
1838if ERRORNAME is nil, just return nil."
1839 ;; Bind this in case the user sets it to nil.
1bcedb3b
RS
1840 (let ((case-fold-search t))
1841 (save-excursion
c60c12be
SM
1842 (goto-char (point-min))
1843 (let ((bound (point)))
1844 (forward-line 1)
1845 (cond ((re-search-backward
1846 (concat name ":" (Info-following-node-name-re)) bound t)
bec275ed 1847 (match-string-no-properties 1))
c60c12be
SM
1848 ((not (eq errorname t))
1849 (error "Node has no %s"
1850 (capitalize (or errorname name)))))))))
1851
1852(defun Info-following-node-name-re (&optional allowedchars)
1853 "Return a regexp matching a node name.
fdf4b680 1854ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
c60c12be
SM
1855saying which chars may appear in the node name.
1856Submatch 1 is the complete node name.
1857Submatch 2 if non-nil is the parenthesized file name part of the node name.
1858Submatch 3 is the local part of the node name.
1859End of submatch 0, 1, and 3 are the same, so you can safely concat."
1860 (concat "[ \t]*" ;Skip leading space.
1861 "\\(\\(([^)]+)\\)?" ;Node name can start with a file name.
1862 "\\([" (or allowedchars "^,\t\n") "]*" ;Any number of allowed chars.
1863 "[" (or allowedchars "^,\t\n") " ]" ;The last char can't be a space.
1864 "\\|\\)\\)")) ;Allow empty node names.
a384cab3 1865
718e6bc7
RS
1866;;; For compatibility; other files have used this name.
1867(defun Info-following-node-name ()
1868 (and (looking-at (Info-following-node-name-re))
bec275ed 1869 (match-string-no-properties 1)))
718e6bc7 1870
a384cab3
JB
1871(defun Info-next ()
1872 "Go to the next node of this node."
1873 (interactive)
222a6c9b
CY
1874 ;; In case another window is currently selected
1875 (save-window-excursion
1876 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
1877 (Info-goto-node (Info-extract-pointer "next"))))
a384cab3
JB
1878
1879(defun Info-prev ()
1880 "Go to the previous node of this node."
1881 (interactive)
222a6c9b
CY
1882 ;; In case another window is currently selected
1883 (save-window-excursion
1884 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
1885 (Info-goto-node (Info-extract-pointer "prev[ious]*" "previous"))))
a384cab3 1886
5dab2fb4
RS
1887(defun Info-up (&optional same-file)
1888 "Go to the superior node of this node.
1889If SAME-FILE is non-nil, do not move to a different Info file."
a384cab3 1890 (interactive)
222a6c9b
CY
1891 ;; In case another window is currently selected
1892 (save-window-excursion
1893 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
1894 (let ((old-node Info-current-node)
1895 (old-file Info-current-file)
1896 (node (Info-extract-pointer "up")) p)
d590dbd2 1897 (and same-file
222a6c9b
CY
1898 (string-match "^(" node)
1899 (error "Up node is in another Info file"))
1900 (Info-goto-node node)
1901 (setq p (point))
1902 (goto-char (point-min))
bec275ed
JL
1903 (if (and (stringp old-file)
1904 (search-forward "\n* Menu:" nil t)
222a6c9b
CY
1905 (re-search-forward
1906 (if (string-equal old-node "Top")
1907 (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")")
1908 (concat "\n\\* +\\(" (regexp-quote old-node)
1909 ":\\|[^:]+: +" (regexp-quote old-node) "\\)"))
1910 nil t))
1911 (progn (beginning-of-line) (if (looking-at "^\\* ") (forward-char 2)))
1912 (goto-char p)
1913 (Info-restore-point Info-history)))))
a384cab3 1914
424b9d3e
JL
1915(defun Info-history-back ()
1916 "Go back in the history to the last node visited."
a384cab3
JB
1917 (interactive)
1918 (or Info-history
1919 (error "This is the first Info node you looked at"))
424b9d3e
JL
1920 (let ((history-forward
1921 (cons (list Info-current-file Info-current-node (point))
1922 Info-history-forward))
1923 filename nodename opoint)
a384cab3
JB
1924 (setq filename (car (car Info-history)))
1925 (setq nodename (car (cdr (car Info-history))))
1926 (setq opoint (car (cdr (cdr (car Info-history)))))
1927 (setq Info-history (cdr Info-history))
1928 (Info-find-node filename nodename)
1929 (setq Info-history (cdr Info-history))
424b9d3e
JL
1930 (setq Info-history-forward history-forward)
1931 (goto-char opoint)))
1932
1933(defalias 'Info-last 'Info-history-back)
1934
1935(defun Info-history-forward ()
1936 "Go forward in the history of visited nodes."
1937 (interactive)
1938 (or Info-history-forward
1939 (error "This is the last Info node you looked at"))
1940 (let ((history-forward (cdr Info-history-forward))
1941 filename nodename opoint)
1942 (setq filename (car (car Info-history-forward)))
1943 (setq nodename (car (cdr (car Info-history-forward))))
1944 (setq opoint (car (cdr (cdr (car Info-history-forward)))))
1945 (Info-find-node filename nodename)
1946 (setq Info-history-forward history-forward)
a384cab3
JB
1947 (goto-char opoint)))
1948
48e5b471 1949;;;###autoload
a384cab3
JB
1950(defun Info-directory ()
1951 "Go to the Info directory node."
1952 (interactive)
1953 (Info-find-node "dir" "top"))
1954\f
8a7757f6 1955(defun Info-history ()
89b064a9 1956 "Go to a node with a menu of visited nodes."
8a7757f6
JL
1957 (interactive)
1958 (let ((curr-file Info-current-file)
1959 (curr-node Info-current-node)
1960 p)
89b064a9
JL
1961 (with-current-buffer (get-buffer-create " *info-history*")
1962 (let ((inhibit-read-only t))
1963 (erase-buffer)
1964 (goto-char (point-min))
ea99d5c8 1965 (insert "\n\^_\nFile: history, Node: Top, Up: (dir)\n\n")
89b064a9
JL
1966 (insert "Recently Visited Nodes\n**********************\n\n")
1967 (insert "* Menu:\n\n")
1968 (let ((hl (delete '("history" "Top") Info-history-list)))
1969 (while hl
1970 (let ((file (nth 0 (car hl)))
1971 (node (nth 1 (car hl))))
bec275ed
JL
1972 (if (and (equal file curr-file)
1973 (equal node curr-node))
89b064a9 1974 (setq p (point)))
bec275ed
JL
1975 (if (stringp file)
1976 (insert "* " node ": ("
1977 (propertize (or (file-name-directory file) "") 'invisible t)
1978 (file-name-nondirectory file)
1979 ")" node ".\n")))
89b064a9 1980 (setq hl (cdr hl))))))
bec275ed 1981 (Info-find-node 'history "Top")
8a7757f6
JL
1982 (goto-char (or p (point-min)))))
1983
8a7757f6 1984(defun Info-toc ()
bc224645
JL
1985 "Go to a node with table of contents of the current Info file.
1986Table of contents is created from the tree structure of menus."
8a7757f6 1987 (interactive)
bec275ed
JL
1988 (if (stringp Info-current-file)
1989 (let ((curr-file (substring-no-properties Info-current-file))
1990 (curr-node (substring-no-properties Info-current-node))
1991 p)
1992 (with-current-buffer (get-buffer-create " *info-toc*")
1993 (let ((inhibit-read-only t)
586f9284 1994 (node-list (Info-toc-nodes curr-file)))
bec275ed
JL
1995 (erase-buffer)
1996 (goto-char (point-min))
1997 (insert "\n\^_\nFile: toc, Node: Top, Up: (dir)\n\n")
1998 (insert "Table of Contents\n*****************\n\n")
1999 (insert "*Note Top: (" curr-file ")Top.\n")
2000 (Info-insert-toc
586f9284 2001 (nth 3 (assoc "Top" node-list)) ; get Top nodes
bec275ed
JL
2002 node-list 0 curr-file))
2003 (if (not (bobp))
2004 (let ((Info-hide-note-references 'hide)
2005 (Info-fontify-visited-nodes nil))
2006 (Info-mode)
2007 (setq Info-current-file 'toc Info-current-node "Top")
2008 (goto-char (point-min))
2009 (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
2010 (point-min))
2011 (point-max))
2012 (Info-fontify-node)
2013 (widen)))
2014 (goto-char (point-min))
2015 (if (setq p (search-forward (concat "*Note " curr-node ":") nil t))
2016 (setq p (- p (length curr-node) 2))))
2017 (Info-find-node 'toc "Top")
2018 (goto-char (or p (point-min))))))
8a7757f6 2019
89b064a9 2020(defun Info-insert-toc (nodes node-list level curr-file)
8a7757f6
JL
2021 "Insert table of contents with references to nodes."
2022 (let ((section "Top"))
2023 (while nodes
2024 (let ((node (assoc (car nodes) node-list)))
586f9284
JL
2025 (unless (member (nth 2 node) (list nil section))
2026 (insert (setq section (nth 2 node)) "\n"))
8a7757f6 2027 (insert (make-string level ?\t))
89b064a9 2028 (insert "*Note " (car nodes) ": (" curr-file ")" (car nodes) ".\n")
586f9284 2029 (Info-insert-toc (nth 3 node) node-list (1+ level) curr-file)
8a7757f6
JL
2030 (setq nodes (cdr nodes))))))
2031
2032(defun Info-build-toc (file)
2033 "Build table of contents from menus of Info FILE and its subfiles."
8a7757f6 2034 (with-temp-buffer
ea99d5c8
JL
2035 (let* ((file (and (stringp file) (Info-find-file file)))
2036 (default-directory (or (and (stringp file)
2037 (file-name-directory file))
141b49f5 2038 default-directory))
ea99d5c8 2039 (main-file (and (stringp file) file))
141b49f5
JL
2040 (sections '(("Top" "Top")))
2041 nodes subfiles)
2042 (while (or main-file subfiles)
586f9284 2043 ;; (or main-file (message "Searching subfile %s..." (car subfiles)))
8a7757f6 2044 (erase-buffer)
141b49f5
JL
2045 (info-insert-file-contents (or main-file (car subfiles)))
2046 (goto-char (point-min))
8a7757f6
JL
2047 (while (and (search-forward "\n\^_\nFile:" nil 'move)
2048 (search-forward "Node: " nil 'move))
586f9284
JL
2049 (let* ((nodename (substring-no-properties (Info-following-node-name)))
2050 (bound (- (or (save-excursion (search-forward "\n\^_" nil t))
2051 (point-max)) 2))
2052 (upnode (and (re-search-forward
2053 (concat "Up:" (Info-following-node-name-re))
2054 bound t)
2055 (match-string-no-properties 1)))
2056 (section "Top")
2057 menu-items)
2058 (when (string-match "(" upnode) (setq upnode nil))
141b49f5 2059 (when (and (not (Info-index-node nodename file))
8a7757f6
JL
2060 (re-search-forward "^\\* Menu:" bound t))
2061 (forward-line 1)
2062 (beginning-of-line)
2063 (setq bound (or (and (equal nodename "Top")
2064 (save-excursion
2065 (re-search-forward
2066 "^[ \t-]*The Detailed Node Listing" nil t)))
2067 bound))
2068 (while (< (point) bound)
2069 (cond
2070 ;; Menu item line
2071 ((looking-at "^\\* +[^:]+:")
2072 (beginning-of-line)
2073 (forward-char 2)
2074 (let ((menu-node-name (substring-no-properties
2075 (Info-extract-menu-node-name))))
2076 (setq menu-items (cons menu-node-name menu-items))
2077 (if (equal nodename "Top")
2078 (setq sections
2079 (cons (list menu-node-name section) sections)))))
2080 ;; Other non-empty strings in the Top node are section names
2081 ((and (equal nodename "Top")
2082 (looking-at "^\\([^ \t\n*=.-][^:\n]*\\)"))
2083 (setq section (match-string-no-properties 1))))
2084 (forward-line 1)
2085 (beginning-of-line)))
586f9284 2086 (setq nodes (cons (list nodename upnode
8a7757f6
JL
2087 (cadr (assoc nodename sections))
2088 (nreverse menu-items))
2089 nodes))
2090 (goto-char bound)))
141b49f5 2091 (if main-file
8a7757f6
JL
2092 (save-excursion
2093 (goto-char (point-min))
2094 (if (search-forward "\n\^_\nIndirect:" nil t)
2095 (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
2096 (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
2097 (setq subfiles (cons (match-string-no-properties 1)
2098 subfiles)))))
2099 (setq subfiles (nreverse subfiles)
141b49f5 2100 main-file nil))
8a7757f6
JL
2101 (setq subfiles (cdr subfiles))))
2102 (message "")
2103 (nreverse nodes))))
586f9284
JL
2104
2105(defvar Info-toc-nodes nil
2106 "Alist of cached parent-children node information in visited Info files.
2107Each element is (FILE (NODE-NAME PARENT SECTION CHILDREN) ...)
2108where PARENT is the parent node extracted from the Up pointer,
2109SECTION is the section name in the Top node where this node is placed,
2110CHILDREN is a list of child nodes extracted from the node menu.")
2111
2112(defun Info-toc-nodes (file)
2113 "Return a node list of Info FILE with parent-children information.
2114This information is cached in the variable `Info-toc-nodes' with the help
2115of the function `Info-build-toc'."
2116 (or file (setq file Info-current-file))
2117 (or (assoc file Info-toc-nodes)
2118 ;; Skip virtual Info files
2119 (and (or (not (stringp file))
2120 (member file '("dir" apropos history toc)))
2121 (push (cons file nil) Info-toc-nodes))
2122 ;; Scan the entire manual and cache the result in Info-toc-nodes
2123 (let ((nodes (Info-build-toc file)))
2124 (push (cons file nodes) Info-toc-nodes)
2125 nodes)
2126 ;; If there is an error, still add nil to the cache
2127 (push (cons file nil) Info-toc-nodes))
2128 (cdr (assoc file Info-toc-nodes)))
2129
8a7757f6
JL
2130\f
2131(defun Info-follow-reference (footnotename &optional fork)
fdf4b680 2132 "Follow cross reference named FOOTNOTENAME to the node it refers to.
8a7757f6
JL
2133FOOTNOTENAME may be an abbreviation of the reference name.
2134If FORK is non-nil (interactively with a prefix arg), show the node in
185cff95 2135a new Info buffer. If FORK is a string, it is the name to use for the
8a7757f6 2136new buffer."
a384cab3
JB
2137 (interactive
2138 (let ((completion-ignore-case t)
1bcedb3b 2139 (case-fold-search t)
67bc89ab 2140 completions default alt-default (start-point (point)) str i bol eol)
a384cab3 2141 (save-excursion
67bc89ab
RS
2142 ;; Store end and beginning of line.
2143 (end-of-line)
2144 (setq eol (point))
2145 (beginning-of-line)
2146 (setq bol (point))
2147
a384cab3 2148 (goto-char (point-min))
8a7757f6 2149 (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
c60c12be 2150 (setq str (match-string-no-properties 1))
a384cab3
JB
2151 ;; See if this one should be the default.
2152 (and (null default)
1f179e27 2153 (<= (match-beginning 0) start-point)
a384cab3
JB
2154 (<= start-point (point))
2155 (setq default t))
67bc89ab
RS
2156 ;; See if this one should be the alternate default.
2157 (and (null alt-default)
2158 (and (<= bol (match-beginning 0))
2159 (<= (point) eol))
2160 (setq alt-default t))
a384cab3
JB
2161 (setq i 0)
2162 (while (setq i (string-match "[ \n\t]+" str i))
2163 (setq str (concat (substring str 0 i) " "
2164 (substring str (match-end 0))))
2165 (setq i (1+ i)))
2166 ;; Record as a completion and perhaps as default.
2167 (if (eq default t) (setq default str))
67bc89ab 2168 (if (eq alt-default t) (setq alt-default str))
ec6d29af 2169 ;; Don't add this string if it's a duplicate.
c60c12be
SM
2170 (or (assoc-string str completions t)
2171 (push str completions))))
67bc89ab
RS
2172 ;; If no good default was found, try an alternate.
2173 (or default
2174 (setq default alt-default))
2175 ;; If only one cross-reference found, then make it default.
2176 (if (eq (length completions) 1)
c60c12be 2177 (setq default (car completions)))
a384cab3 2178 (if completions
b0ebdfe5 2179 (let ((input (completing-read (if default
968b7671 2180 (concat
5b76833f
RF
2181 "Follow reference named (default "
2182 default "): ")
b0ebdfe5
RS
2183 "Follow reference named: ")
2184 completions nil t)))
2185 (list (if (equal input "")
8a7757f6 2186 default input) current-prefix-arg))
a384cab3 2187 (error "No cross-references in this node"))))
ebf8f7e1
RS
2188
2189 (unless footnotename
2190 (error "No reference was specified"))
2191
dde7f979 2192 (let (target i (str (concat "\\*note " (regexp-quote footnotename)))
1bcedb3b 2193 (case-fold-search t))
a384cab3
JB
2194 (while (setq i (string-match " " str i))
2195 (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
2196 (setq i (+ i 6)))
2197 (save-excursion
8a7757f6
JL
2198 ;; Move point to the beginning of reference if point is on reference
2199 (or (looking-at "\\*note[ \n\t]+")
2200 (and (looking-back "\\*note[ \n\t]+")
2201 (goto-char (match-beginning 0)))
2202 (if (and (save-excursion
2203 (goto-char (+ (point) 5)) ; skip a possible *note
2204 (re-search-backward "\\*note[ \n\t]+" nil t)
836bb478 2205 (looking-at str))
8a7757f6
JL
2206 (<= (point) (match-end 0)))
2207 (goto-char (match-beginning 0))))
2208 ;; Go to the reference closest to point
2209 (let ((next-ref (save-excursion (and (re-search-forward str nil t)
2210 (+ (match-beginning 0) 5))))
2211 (prev-ref (save-excursion (and (re-search-backward str nil t)
2212 (+ (match-beginning 0) 5)))))
2213 (goto-char (cond ((and next-ref prev-ref)
2214 (if (< (abs (- next-ref (point)))
2215 (abs (- prev-ref (point))))
2216 next-ref prev-ref))
2217 ((or next-ref prev-ref))
2218 ((error "No cross-reference named %s" footnotename))))
2219 (setq target (Info-extract-menu-node-name t))))
a384cab3
JB
2220 (while (setq i (string-match "[ \t\n]+" target i))
2221 (setq target (concat (substring target 0 i) " "
2222 (substring target (match-end 0))))
2223 (setq i (+ i 1)))
8a7757f6 2224 (Info-goto-node target fork)))
a384cab3 2225
e057da52
SM
2226(defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*"
2227 ;; We allow newline because this is also used in Info-follow-reference,
2228 ;; where the xref name might be wrapped over two lines.
c60c12be
SM
2229 "Regexp that matches a menu entry name upto but not including the colon.
2230Because of ambiguities, this should be concatenated with something like
2231`:' and `Info-following-node-name-re'.")
2232
836bb478 2233(defun Info-extract-menu-node-name (&optional multi-line index-node)
a384cab3 2234 (skip-chars-forward " \t\n")
c60c12be
SM
2235 (when (looking-at (concat Info-menu-entry-name-re ":\\(:\\|"
2236 (Info-following-node-name-re
836bb478
JL
2237 (cond
2238 (index-node "^,\t\n")
2239 (multi-line "^.,\t")
2240 (t "^.,\t\n")))
2241 "\\)"
2242 (if index-node
2243 "\\.\\(?:[ \t\n]+(line +\\([0-9]+\\))\\)?"
2244 "")))
2245 (if index-node
2246 (setq Info-point-loc
2247 (if (match-beginning 5)
2248 (string-to-number (match-string 5))
bec275ed
JL
2249 (buffer-substring-no-properties
2250 (match-beginning 0) (1- (match-beginning 1)))))
02c9f5cd 2251;;; Uncomment next line to use names of cross-references in non-index nodes:
836bb478
JL
2252;;; (setq Info-point-loc
2253;;; (buffer-substring (match-beginning 0) (1- (match-beginning 1))))
2254 )
c60c12be
SM
2255 (replace-regexp-in-string
2256 "[ \n]+" " "
bec275ed 2257 (or (match-string-no-properties 2)
c60c12be 2258 ;; If the node name is the menu entry name (using `entry::').
bec275ed
JL
2259 (buffer-substring-no-properties
2260 (match-beginning 0) (1- (match-beginning 1)))))))
a384cab3 2261
e8adde3f 2262;; No one calls this.
9e5c2f50
RS
2263;;(defun Info-menu-item-sequence (list)
2264;; (while list
e8adde3f 2265;; (Info-menu (car list))
9e5c2f50 2266;; (setq list (cdr list))))
a384cab3 2267
6356e646 2268(defvar Info-complete-menu-buffer)
e4e8ee63 2269(defvar Info-complete-next-re nil)
141b49f5 2270(defvar Info-complete-nodes nil)
e4e8ee63 2271(defvar Info-complete-cache nil)
6356e646 2272
c60c12be
SM
2273(defconst Info-node-spec-re
2274 (concat (Info-following-node-name-re "^.,:") "[,:.]")
a1637357
SM
2275 "Regexp to match the text after a : until the terminating `.'.")
2276
e8adde3f 2277(defun Info-complete-menu-item (string predicate action)
2d12f5c2
SM
2278 ;; This uses two dynamically bound variables:
2279 ;; - `Info-complete-menu-buffer' which contains the buffer in which
2280 ;; is the menu of items we're trying to complete.
2281 ;; - `Info-complete-next-re' which, if non-nil, indicates that we should
2282 ;; also look for menu items in subsequent nodes as long as those
2283 ;; nodes' names match `Info-complete-next-re'. This feature is currently
141b49f5
JL
2284 ;; not used.
2285 ;; - `Info-complete-nodes' which, if non-nil, indicates that we should
2286 ;; also look for menu items in these nodes. This feature is currently
2d12f5c2 2287 ;; only used for completion in Info-index.
0defe758
LK
2288
2289 ;; Note that `Info-complete-menu-buffer' could be current already,
2290 ;; so we want to save point.
e8dab975 2291 (with-current-buffer Info-complete-menu-buffer
494858be
SM
2292 (save-excursion
2293 (let ((completion-ignore-case t)
2294 (case-fold-search t)
2295 (orignode Info-current-node)
2296 nextnode)
2297 (goto-char (point-min))
2298 (search-forward "\n* Menu:")
2299 (if (not (memq action '(nil t)))
2300 (re-search-forward
2301 (concat "\n\\* +" (regexp-quote string) ":") nil t)
2302 (let ((pattern (concat "\n\\* +\\("
2303 (regexp-quote string)
2304 Info-menu-entry-name-re "\\):" Info-node-spec-re))
2305 completions
2306 (complete-nodes Info-complete-nodes))
2307 ;; Check the cache.
2308 (if (and (equal (nth 0 Info-complete-cache) Info-current-file)
2309 (equal (nth 1 Info-complete-cache) Info-current-node)
2310 (equal (nth 2 Info-complete-cache) Info-complete-next-re)
2311 (equal (nth 5 Info-complete-cache) Info-complete-nodes)
2312 (let ((prev (nth 3 Info-complete-cache)))
2313 (eq t (compare-strings string 0 (length prev)
2314 prev 0 nil t))))
2315 ;; We can reuse the previous list.
2316 (setq completions (nth 4 Info-complete-cache))
2317 ;; The cache can't be used.
2318 (while
2319 (progn
2320 (while (re-search-forward pattern nil t)
2321 (push (match-string-no-properties 1)
2322 completions))
2323 ;; Check subsequent nodes if applicable.
2324 (or (and Info-complete-next-re
2325 (setq nextnode (Info-extract-pointer "next" t))
2326 (string-match Info-complete-next-re nextnode))
2327 (and complete-nodes
2328 (setq complete-nodes (cdr complete-nodes)
2329 nextnode (car complete-nodes)))))
2330 (Info-goto-node nextnode))
2331 ;; Go back to the start node (for the next completion).
2332 (unless (equal Info-current-node orignode)
2333 (Info-goto-node orignode))
2334 ;; Update the cache.
2335 (set (make-local-variable 'Info-complete-cache)
d5850019
JB
2336 (list Info-current-file Info-current-node
2337 Info-complete-next-re string completions
2338 Info-complete-nodes)))
2339 (if action
2340 (all-completions string completions predicate)
2341 (try-completion string completions predicate))))))))
e8adde3f
RS
2342
2343
4fceda3c 2344(defun Info-menu (menu-item &optional fork)
dc414be1
EZ
2345 "Go to the node pointed to by the menu item named (or abbreviated) MENU-ITEM.
2346The menu item should one of those listed in the current node's menu.
2347Completion is allowed, and the default menu item is the one point is on.
7210b6f5 2348If FORK is non-nil (interactively with a prefix arg), show the node in
185cff95 2349a new Info buffer. If FORK is a string, it is the name to use for the
7210b6f5 2350new buffer."
a384cab3
JB
2351 (interactive
2352 (let ((completions '())
2353 ;; If point is within a menu item, use that item as the default
2354 (default nil)
2355 (p (point))
211d6309 2356 beg
760d5cb3
GM
2357 (last nil)
2358 (case-fold-search t))
a384cab3
JB
2359 (save-excursion
2360 (goto-char (point-min))
2361 (if (not (search-forward "\n* menu:" nil t))
2362 (error "No menu in this node"))
e8adde3f
RS
2363 (setq beg (point))
2364 (and (< (point) p)
2365 (save-excursion
2366 (goto-char p)
2367 (end-of-line)
00d32b3f
SM
2368 (if (re-search-backward (concat "\n\\* +\\("
2369 Info-menu-entry-name-re
2370 "\\):") beg t)
110d4e80 2371 (setq default (match-string-no-properties 1))))))
a384cab3
JB
2372 (let ((item nil))
2373 (while (null item)
e8adde3f
RS
2374 (setq item (let ((completion-ignore-case t)
2375 (Info-complete-menu-buffer (current-buffer)))
a384cab3
JB
2376 (completing-read (if default
2377 (format "Menu item (default %s): "
2378 default)
760d5cb3 2379 "Menu item: ")
e8adde3f 2380 'Info-complete-menu-item nil t)))
aea2a8da
JB
2381 ;; we rely on the fact that completing-read accepts an input
2382 ;; of "" even when the require-match argument is true and ""
2383 ;; is not a valid possibility
a384cab3
JB
2384 (if (string= item "")
2385 (if default
2386 (setq item default)
760d5cb3
GM
2387 ;; ask again
2388 (setq item nil))))
4fceda3c 2389 (list item current-prefix-arg))))
a384cab3
JB
2390 ;; there is a problem here in that if several menu items have the same
2391 ;; name you can only go to the node of the first with this command.
4fceda3c 2392 (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item)))
399c88ad 2393
a384cab3
JB
2394(defun Info-extract-menu-item (menu-item)
2395 (setq menu-item (regexp-quote menu-item))
1bcedb3b
RS
2396 (let ((case-fold-search t))
2397 (save-excursion
760d5cb3
GM
2398 (let ((case-fold-search t))
2399 (goto-char (point-min))
2400 (or (search-forward "\n* menu:" nil t)
2401 (error "No menu in this node"))
2402 (or (re-search-forward (concat "\n\\* +" menu-item ":") nil t)
2403 (re-search-forward (concat "\n\\* +" menu-item) nil t)
2404 (error "No such item in menu"))
2405 (beginning-of-line)
2406 (forward-char 2)
141b49f5 2407 (Info-extract-menu-node-name nil (Info-index-node))))))
a384cab3
JB
2408
2409;; If COUNT is nil, use the last item in the menu.
2410(defun Info-extract-menu-counting (count)
1bcedb3b
RS
2411 (let ((case-fold-search t))
2412 (save-excursion
760d5cb3
GM
2413 (let ((case-fold-search t))
2414 (goto-char (point-min))
2415 (or (search-forward "\n* menu:" nil t)
2416 (error "No menu in this node"))
2417 (if count
2418 (or (search-forward "\n* " nil t count)
2419 (error "Too few items in menu"))
2420 (while (search-forward "\n* " nil t)
2421 nil))
141b49f5 2422 (Info-extract-menu-node-name nil (Info-index-node))))))
a384cab3 2423
e38e7367
RM
2424(defun Info-nth-menu-item ()
2425 "Go to the node of the Nth menu item.
2426N is the digit argument used to invoke this command."
a384cab3 2427 (interactive)
e38e7367
RM
2428 (Info-goto-node
2429 (Info-extract-menu-counting
2430 (- (aref (this-command-keys) (1- (length (this-command-keys)))) ?0))))
a384cab3
JB
2431
2432(defun Info-top-node ()
2433 "Go to the Top node of this file."
2434 (interactive)
2435 (Info-goto-node "Top"))
2436
2437(defun Info-final-node ()
2438 "Go to the final node in this file."
2439 (interactive)
2440 (Info-goto-node "Top")
760d5cb3
GM
2441 (let ((Info-history nil)
2442 (case-fold-search t))
a384cab3
JB
2443 ;; Go to the last node in the menu of Top.
2444 (Info-goto-node (Info-extract-menu-counting nil))
2445 ;; If the last node in the menu is not last in pointer structure,
399c88ad 2446 ;; move forward until we can't go any farther.
a384cab3
JB
2447 (while (Info-forward-node t t) nil)
2448 ;; Then keep moving down to last subnode, unless we reach an index.
141b49f5 2449 (while (and (not (Info-index-node))
a384cab3
JB
2450 (save-excursion (search-forward "\n* Menu:" nil t)))
2451 (Info-goto-node (Info-extract-menu-counting nil)))))
2452
2453(defun Info-forward-node (&optional not-down no-error)
2454 "Go forward one node, considering all nodes as forming one sequence."
2455 (interactive)
2456 (goto-char (point-min))
2457 (forward-line 1)
760d5cb3
GM
2458 (let ((case-fold-search t))
2459 ;; three possibilities, in order of priority:
2460 ;; 1. next node is in a menu in this node (but not in an index)
2461 ;; 2. next node is next at same level
2462 ;; 3. next node is up and next
2463 (cond ((and (not not-down)
2464 (save-excursion (search-forward "\n* menu:" nil t))
141b49f5 2465 (not (Info-index-node)))
760d5cb3
GM
2466 (Info-goto-node (Info-extract-menu-counting 1))
2467 t)
6af7040d 2468 ((save-excursion (search-backward "next:" nil t))
760d5cb3
GM
2469 (Info-next)
2470 t)
6af7040d 2471 ((and (save-excursion (search-backward "up:" nil t))
760d5cb3
GM
2472 ;; Use string-equal, not equal, to ignore text props.
2473 (not (string-equal (downcase (Info-extract-pointer "up"))
2474 "top")))
2475 (let ((old-node Info-current-node))
2476 (Info-up)
2477 (let (Info-history success)
2478 (unwind-protect
2479 (setq success (Info-forward-node t no-error))
2480 (or success (Info-goto-node old-node))))))
2481 (no-error nil)
2482 (t (error "No pointer forward from this node")))))
a384cab3
JB
2483
2484(defun Info-backward-node ()
2485 "Go backward one node, considering all nodes as forming one sequence."
2486 (interactive)
2487 (let ((prevnode (Info-extract-pointer "prev[ious]*" t))
760d5cb3
GM
2488 (upnode (Info-extract-pointer "up" t))
2489 (case-fold-search t))
a384cab3
JB
2490 (cond ((and upnode (string-match "(" upnode))
2491 (error "First node in file"))
2492 ((and upnode (or (null prevnode)
e90d1271
RS
2493 ;; Use string-equal, not equal,
2494 ;; to ignore text properties.
2495 (string-equal (downcase prevnode)
2496 (downcase upnode))))
a384cab3
JB
2497 (Info-up))
2498 (prevnode
2499 ;; If we move back at the same level,
2500 ;; go down to find the last subnode*.
2501 (Info-prev)
2502 (let (Info-history)
141b49f5 2503 (while (and (not (Info-index-node))
a384cab3
JB
2504 (save-excursion (search-forward "\n* Menu:" nil t)))
2505 (Info-goto-node (Info-extract-menu-counting nil)))))
2506 (t
2507 (error "No pointer backward from this node")))))
2508
2509(defun Info-exit ()
2510 "Exit Info by selecting some other buffer."
2511 (interactive)
552775bd
RS
2512 (if Info-standalone
2513 (save-buffers-kill-emacs)
4643e92f 2514 (quit-window)))
a384cab3 2515
253db917 2516(defun Info-next-menu-item ()
3a8803c0 2517 "Go to the node of the next menu item."
253db917 2518 (interactive)
fdf4b680 2519 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
2520 (let* ((case-fold-search t)
2521 (node
2522 (save-excursion
2523 (forward-line -1)
2524 (search-forward "\n* menu:" nil t)
2525 (and (search-forward "\n* " nil t)
2526 (Info-extract-menu-node-name)))))
fadfb77f
RS
2527 (if node (Info-goto-node node)
2528 (error "No more items in menu"))))
253db917
ER
2529
2530(defun Info-last-menu-item ()
3a8803c0 2531 "Go to the node of the previous menu item."
253db917
ER
2532 (interactive)
2533 (save-excursion
2534 (forward-line 1)
fdf4b680 2535 ;; Bind this in case the user sets it to nil.
760d5cb3
GM
2536 (let* ((case-fold-search t)
2537 (beg (save-excursion
2538 (and (search-backward "\n* menu:" nil t)
2539 (point)))))
0a56332b
RS
2540 (or (and beg (search-backward "\n* " beg t))
2541 (error "No previous items in menu")))
2542 (Info-goto-node (save-excursion
2543 (goto-char (match-end 0))
2544 (Info-extract-menu-node-name)))))
253db917 2545
552775bd 2546(defmacro Info-no-error (&rest body)
253db917
ER
2547 (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
2548
2549(defun Info-next-preorder ()
3c9179ea
RS
2550 "Go to the next subnode or the next node, or go up a level."
2551 (interactive)
2552 (cond ((Info-no-error (Info-next-menu-item)))
2553 ((Info-no-error (Info-next)))
5dab2fb4 2554 ((Info-no-error (Info-up t))
ed690657
KH
2555 ;; Since we have already gone thru all the items in this menu,
2556 ;; go up to the end of this node.
b54d0f0e
RS
2557 (goto-char (point-max))
2558 ;; Since logically we are done with the node with that menu,
fd3d78f2
JL
2559 ;; move on from it. But don't add intermediate nodes
2560 ;; to the history on recursive calls.
2561 (let (Info-history)
2562 (Info-next-preorder)))
3c9179ea
RS
2563 (t
2564 (error "No more nodes"))))
253db917
ER
2565
2566(defun Info-last-preorder ()
2567 "Go to the last node, popping up a level if there is none."
2568 (interactive)
0a56332b
RS
2569 (cond ((Info-no-error
2570 (Info-last-menu-item)
2571 ;; If we go down a menu item, go to the end of the node
2572 ;; so we can scroll back through it.
ed690657 2573 (goto-char (point-max)))
b54d0f0e
RS
2574 ;; Keep going down, as long as there are nested menu nodes.
2575 (while (Info-no-error
2576 (Info-last-menu-item)
2577 ;; If we go down a menu item, go to the end of the node
2578 ;; so we can scroll back through it.
2579 (goto-char (point-max))))
ed690657 2580 (recenter -1))
c2def7a0
MB
2581 ((and (Info-no-error (Info-extract-pointer "prev"))
2582 (not (equal (Info-extract-pointer "up")
5dab2fb4
RS
2583 (Info-extract-pointer "prev"))))
2584 (Info-no-error (Info-prev))
ed690657 2585 (goto-char (point-max))
b54d0f0e
RS
2586 (while (Info-no-error
2587 (Info-last-menu-item)
2588 ;; If we go down a menu item, go to the end of the node
2589 ;; so we can scroll back through it.
2590 (goto-char (point-max))))
ed690657 2591 (recenter -1))
5dab2fb4 2592 ((Info-no-error (Info-up t))
ed690657 2593 (goto-char (point-min))
760d5cb3
GM
2594 (let ((case-fold-search t))
2595 (or (search-forward "\n* Menu:" nil t)
2596 (goto-char (point-max)))))
ed690657 2597 (t (error "No previous nodes"))))
253db917
ER
2598
2599(defun Info-scroll-up ()
3f32dc86 2600 "Scroll one screenful forward in Info, considering all nodes as one sequence.
280d11ed 2601Once you scroll far enough in a node that its menu appears on the screen
7a53d8c8
EZ
2602but after point, the next scroll moves into its first subnode, unless
2603`Info-scroll-prefer-subnodes' is nil.
280d11ed 2604
7a53d8c8
EZ
2605When you scroll past the end of a node, that goes to the next node if
2606`Info-scroll-prefer-subnodes' is non-nil and to the first subnode otherwise;
2607if this node has no successor, it moves to the parent node's successor,
2608and so on. If `Info-scroll-prefer-subnodes' is non-nil and point is inside
2609the menu of a node, it moves to subnode indicated by the following menu
2610item. (That case won't normally result from this command, but can happen
2611in other ways.)"
280d11ed 2612
253db917 2613 (interactive)
0a56332b
RS
2614 (if (or (< (window-start) (point-min))
2615 (> (window-start) (point-max)))
2616 (set-window-start (selected-window) (point)))
760d5cb3
GM
2617 (let* ((case-fold-search t)
2618 (virtual-end (save-excursion
2619 (goto-char (point-min))
7a53d8c8
EZ
2620 (if (and Info-scroll-prefer-subnodes
2621 (search-forward "\n* Menu:" nil t))
760d5cb3
GM
2622 (point)
2623 (point-max)))))
0a56332b
RS
2624 (if (or (< virtual-end (window-start))
2625 (pos-visible-in-window-p virtual-end))
7a53d8c8
EZ
2626 (cond
2627 (Info-scroll-prefer-subnodes (Info-next-preorder))
2628 ((Info-no-error (Info-goto-node (Info-extract-menu-counting 1))))
2629 (t (Info-next-preorder)))
0a56332b 2630 (scroll-up))))
253db917 2631
8aab8422
CY
2632(defun Info-mouse-scroll-up (e)
2633 "Scroll one screenful forward in Info, using the mouse.
2634See `Info-scroll-up'."
2635 (interactive "e")
2636 (save-selected-window
2637 (if (eventp e)
2638 (select-window (posn-window (event-start e))))
2639 (Info-scroll-up)))
2640
253db917 2641(defun Info-scroll-down ()
3f32dc86 2642 "Scroll one screenful back in Info, considering all nodes as one sequence.
c2def7a0
MB
2643If point is within the menu of a node, and `Info-scroll-prefer-subnodes'
2644is non-nil, this goes to its last subnode. When you scroll past the
2645beginning of a node, that goes to the previous node or back up to the
2646parent node."
253db917 2647 (interactive)
0a56332b
RS
2648 (if (or (< (window-start) (point-min))
2649 (> (window-start) (point-max)))
2650 (set-window-start (selected-window) (point)))
760d5cb3
GM
2651 (let* ((case-fold-search t)
2652 (current-point (point))
c2def7a0
MB
2653 (virtual-end
2654 (and Info-scroll-prefer-subnodes
2655 (save-excursion
2656 (beginning-of-line)
2657 (setq current-point (point))
2658 (goto-char (point-min))
2659 (search-forward "\n* Menu:"
2660 current-point
2661 t)))))
3a8803c0 2662 (if (or virtual-end
7afe24e3 2663 (pos-visible-in-window-p (point-min) nil t))
0a56332b
RS
2664 (Info-last-preorder)
2665 (scroll-down))))
253db917 2666
8aab8422
CY
2667(defun Info-mouse-scroll-down (e)
2668 "Scroll one screenful backward in Info, using the mouse.
2669See `Info-scroll-down'."
2670 (interactive "e")
2671 (save-selected-window
2672 (if (eventp e)
2673 (select-window (posn-window (event-start e))))
2674 (Info-scroll-down)))
2675
56cda6f5 2676(defun Info-next-reference (&optional recur)
552775bd
RS
2677 "Move cursor to the next cross-reference or menu item in the node."
2678 (interactive)
4ba89826 2679 (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
1bcedb3b
RS
2680 (old-pt (point))
2681 (case-fold-search t))
552775bd
RS
2682 (or (eobp) (forward-char 1))
2683 (or (re-search-forward pat nil t)
2684 (progn
2685 (goto-char (point-min))
2686 (or (re-search-forward pat nil t)
2687 (progn
2688 (goto-char old-pt)
2689 (error "No cross references in this node")))))
8a7757f6 2690 (goto-char (or (match-beginning 1) (match-beginning 0)))
552775bd 2691 (if (looking-at "\\* Menu:")
56cda6f5
RS
2692 (if recur
2693 (error "No cross references in this node")
185cff95
JL
2694 (Info-next-reference t))
2695 (if (looking-at "^\\* ")
2696 (forward-char 2)))))
552775bd 2697
56cda6f5 2698(defun Info-prev-reference (&optional recur)
552775bd
RS
2699 "Move cursor to the previous cross-reference or menu item in the node."
2700 (interactive)
4ba89826 2701 (let ((pat "\\*note[ \n\t]+\\([^:]+\\):\\|^\\* .*:\\|[hf]t?tps?://")
1bcedb3b
RS
2702 (old-pt (point))
2703 (case-fold-search t))
552775bd
RS
2704 (or (re-search-backward pat nil t)
2705 (progn
2706 (goto-char (point-max))
2707 (or (re-search-backward pat nil t)
2708 (progn
2709 (goto-char old-pt)
2710 (error "No cross references in this node")))))
8a7757f6 2711 (goto-char (or (match-beginning 1) (match-beginning 0)))
552775bd 2712 (if (looking-at "\\* Menu:")
56cda6f5
RS
2713 (if recur
2714 (error "No cross references in this node")
185cff95
JL
2715 (Info-prev-reference t))
2716 (if (looking-at "^\\* ")
2717 (forward-char 2)))))
141b49f5
JL
2718\f
2719(defvar Info-index-nodes nil
2720 "Alist of cached index node names of visited Info files.
2721Each element has the form (INFO-FILE INDEX-NODE-NAMES-LIST).")
2722
2723(defun Info-index-nodes (&optional file)
2724 "Return a list of names of all index nodes in Info FILE.
2725If FILE is omitted, it defaults to the current Info file.
50163b00
JL
2726First look in a list of cached index node names. Then scan Info
2727file and its subfiles for nodes with the index cookie. Then try
2728to find index nodes starting from the first node in the top level
2729menu whose name contains the word \"Index\", plus any immediately
2730following nodes whose names also contain the word \"Index\"."
141b49f5
JL
2731 (or file (setq file Info-current-file))
2732 (or (assoc file Info-index-nodes)
2733 ;; Skip virtual Info files
586f9284
JL
2734 (and (or (not (stringp file))
2735 (member file '("dir" apropos history toc)))
50163b00 2736 (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
94b10022
JL
2737 (if Info-file-supports-index-cookies
2738 ;; Find nodes with index cookie
2739 (let* ((default-directory (or (and (stringp file)
2740 (file-name-directory
2741 (setq file (Info-find-file file))))
2742 default-directory))
2743 Info-history Info-history-list Info-fontify-maximum-menu-size
2744 (main-file file) subfiles nodes node)
2745 (condition-case nil
2746 (with-temp-buffer
2747 (while (or main-file subfiles)
2748 (erase-buffer)
2749 (info-insert-file-contents (or main-file (car subfiles)))
2750 (goto-char (point-min))
2751 (while (search-forward "\0\b[index\0\b]" nil 'move)
2752 (save-excursion
2753 (re-search-backward "^\^_")
2754 (search-forward "Node: ")
2755 (setq nodes (cons (Info-following-node-name) nodes))))
2756 (if main-file
2757 (save-excursion
2758 (goto-char (point-min))
2759 (if (search-forward "\n\^_\nIndirect:" nil t)
2760 (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
2761 (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
2762 (setq subfiles (cons (match-string-no-properties 1)
2763 subfiles)))))
2764 (setq subfiles (nreverse subfiles)
2765 main-file nil))
2766 (setq subfiles (cdr subfiles)))))
2767 (error nil))
2768 (if nodes
2769 (setq nodes (nreverse nodes)
2770 Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
2771 nodes)
2772 ;; Else find nodes with the word "Index" in the node name
2773 (let ((case-fold-search t)
2774 Info-history Info-history-list Info-fontify-maximum-menu-size
2775 nodes node)
2776 (condition-case nil
2777 (with-temp-buffer
2778 (Info-mode)
2779 (Info-find-node file "Top")
2780 (when (and (search-forward "\n* menu:" nil t)
2781 (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
2782 (goto-char (match-beginning 1))
2783 (setq nodes (list (Info-extract-menu-node-name)))
2784 (Info-goto-node (car nodes))
2785 (while (and (setq node (Info-extract-pointer "next" t))
2786 (string-match "\\<Index\\>" node))
2787 (setq nodes (cons node nodes))
2788 (Info-goto-node node))))
2789 (error nil))
2790 (if nodes
2791 (setq nodes (nreverse nodes)
2792 Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
2793 nodes))
50163b00
JL
2794 ;; If file has no index nodes, still add it to the cache
2795 (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
141b49f5
JL
2796 (cdr (assoc file Info-index-nodes)))
2797
2798(defun Info-index-node (&optional node file)
2799 "Return non-nil value if NODE is an index node.
2800If NODE is nil, check the current Info node.
2801If FILE is nil, check the current Info file."
50163b00
JL
2802 (if (or (and node (not (equal node Info-current-node)))
2803 (assoc (or file Info-current-file) Info-index-nodes))
2804 (member (or node Info-current-node) (Info-index-nodes file))
2805 ;; Don't search all index nodes if request is only for the current node
2806 ;; and file is not in the cache of index nodes
94b10022
JL
2807 (if Info-file-supports-index-cookies
2808 (save-excursion
2809 (goto-char (+ (or (save-excursion
2810 (search-backward "\n\^_" nil t))
2811 (point-min)) 2))
2812 (search-forward "\0\b[index\0\b]"
2813 (or (save-excursion
2814 (search-forward "\n\^_" nil t))
2815 (point-max)) t))
2816 (save-match-data
2817 (string-match "\\<Index\\>" (or node Info-current-node ""))))))
552775bd 2818
e4e8ee63 2819(defun Info-goto-index ()
141b49f5
JL
2820 "Go to the first index node."
2821 (let ((node (car (Info-index-nodes))))
2822 (or node (error "No index"))
2823 (Info-goto-node node)))
e4e8ee63 2824
201bc1bd 2825;;;###autoload
1143a6b0 2826(defun Info-index (topic)
4a99f153 2827 "Look up a string TOPIC in the index for this manual and go to that entry.
1143a6b0
ER
2828If there are no exact matches to the specified topic, this chooses
2829the first match which is a case-insensitive substring of a topic.
89b064a9 2830Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches.
4a99f153 2831Give an empty topic name to go to the Index node itself."
e4e8ee63
SM
2832 (interactive
2833 (list
ea71ffe8
CY
2834 (let ((completion-ignore-case t)
2835 (Info-complete-menu-buffer (clone-buffer))
141b49f5
JL
2836 (Info-complete-nodes (Info-index-nodes))
2837 (Info-history-list nil))
aebd1760
RS
2838 (if (equal Info-current-file "dir")
2839 (error "The Info directory node has no index; use m to select a manual"))
e4e8ee63
SM
2840 (unwind-protect
2841 (with-current-buffer Info-complete-menu-buffer
2842 (Info-goto-index)
2843 (completing-read "Index topic: " 'Info-complete-menu-item))
2844 (kill-buffer Info-complete-menu-buffer)))))
aebd1760
RS
2845 (if (equal Info-current-file "dir")
2846 (error "The Info directory node has no index; use m to select a manual"))
03e8d958
KS
2847 ;; Strip leading colon in topic; index format does not allow them.
2848 (if (and (stringp topic)
2849 (> (length topic) 0)
2850 (= (aref topic 0) ?:))
2851 (setq topic (substring topic 1)))
1143a6b0 2852 (let ((orignode Info-current-node)
836bb478 2853 (pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]*\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
1143a6b0 2854 (regexp-quote topic)))
141b49f5
JL
2855 node (nodes (Info-index-nodes))
2856 (ohist-list Info-history-list)
1bcedb3b 2857 (case-fold-search t))
e700ec12 2858 (Info-goto-index)
1143a6b0
ER
2859 (or (equal topic "")
2860 (let ((matches nil)
2861 (exact nil)
e700ec12
SM
2862 ;; We bind Info-history to nil for internal node-switches so
2863 ;; that we don't put junk in the history. In the first
2864 ;; Info-goto-index call, above, we do update the history
2865 ;; because that is what the user's previous node choice into it.
c696ac76 2866 (Info-history nil)
1143a6b0
ER
2867 found)
2868 (while
2869 (progn
2870 (goto-char (point-min))
2871 (while (re-search-forward pattern nil t)
983dfbf8
SM
2872 (push (list (match-string-no-properties 1)
2873 (match-string-no-properties 2)
2874 Info-current-node
3a8803c0
JPW
2875 (string-to-number (concat "0"
2876 (match-string 3))))
983dfbf8 2877 matches))
141b49f5 2878 (setq nodes (cdr nodes) node (car nodes)))
1143a6b0
ER
2879 (Info-goto-node node))
2880 (or matches
2881 (progn
81e14cb2 2882 (Info-goto-node orignode)
920bdaab 2883 (error "No `%s' in index" topic)))
1143a6b0
ER
2884 ;; Here it is a feature that assoc is case-sensitive.
2885 (while (setq found (assoc topic matches))
2886 (setq exact (cons found exact)
2887 matches (delq found matches)))
141b49f5 2888 (setq Info-history-list ohist-list)
1143a6b0
ER
2889 (setq Info-index-alternatives (nconc exact (nreverse matches)))
2890 (Info-index-next 0)))))
2891
2892(defun Info-index-next (num)
89b064a9 2893 "Go to the next matching index item from the last \\<Info-mode-map>\\[Info-index] command."
1143a6b0
ER
2894 (interactive "p")
2895 (or Info-index-alternatives
882e61bf 2896 (error "No previous `i' command"))
1143a6b0
ER
2897 (while (< num 0)
2898 (setq num (+ num (length Info-index-alternatives))))
2899 (while (> num 0)
2900 (setq Info-index-alternatives
2901 (nconc (cdr Info-index-alternatives)
2902 (list (car Info-index-alternatives)))
2903 num (1- num)))
2904 (Info-goto-node (nth 1 (car Info-index-alternatives)))
2905 (if (> (nth 3 (car Info-index-alternatives)) 0)
836bb478 2906 (forward-line (1- (nth 3 (car Info-index-alternatives))))
760d5cb3 2907 (forward-line 3) ; don't search in headers
1143a6b0 2908 (let ((name (car (car Info-index-alternatives))))
920bdaab
RS
2909 (Info-find-index-name name)))
2910 (message "Found `%s' in %s. %s"
1143a6b0
ER
2911 (car (car Info-index-alternatives))
2912 (nth 2 (car Info-index-alternatives))
2913 (if (cdr Info-index-alternatives)
fa62821e
EZ
2914 (format "(%s total; use `%s' for next)"
2915 (length Info-index-alternatives)
2916 (key-description (where-is-internal
2917 'Info-index-next overriding-local-map
2918 t)))
1143a6b0
ER
2919 "(Only match)")))
2920
920bdaab
RS
2921(defun Info-find-index-name (name)
2922 "Move point to the place within the current node where NAME is defined."
1bcedb3b
RS
2923 (let ((case-fold-search t))
2924 (if (or (re-search-forward (format
2925 "[a-zA-Z]+: %s\\( \\|$\\)"
2926 (regexp-quote name)) nil t)
d31c6ecf
RS
2927 ;; Find a function definition with a return type.
2928 (re-search-forward (format
f2fc5b0a 2929 "[a-zA-Z]+: [a-zA-Z0-9_ *&]+ %s\\( \\|$\\)"
d31c6ecf 2930 (regexp-quote name)) nil t)
1bcedb3b
RS
2931 (search-forward (format "`%s'" name) nil t)
2932 (and (string-match "\\`.*\\( (.*)\\)\\'" name)
2933 (search-forward
2934 (format "`%s'" (substring name 0 (match-beginning 1)))
2935 nil t))
bbe054f7
EZ
2936 (search-forward name nil t)
2937 ;; Try again without the " <1>" makeinfo can append
2938 (and (string-match "\\`\\(.*\\) <[0-9]+>\\'" name)
2939 (Info-find-index-name (match-string 1 name))))
2940 (progn (beginning-of-line) t) ;; non-nil for recursive call
1bcedb3b 2941 (goto-char (point-min)))))
920bdaab 2942
ab2f22ad
JH
2943;;;###autoload
2944(defun info-apropos (string)
2945 "Grovel indices of all known Info files on your system for STRING.
2946Build a menu of the possible matches."
2947 (interactive "sIndex apropos: ")
2948 (unless (string= string "")
836bb478 2949 (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
ab2f22ad
JH
2950 (regexp-quote string)))
2951 (ohist Info-history)
37600b6d 2952 (ohist-list Info-history-list)
ab2f22ad
JH
2953 (current-node Info-current-node)
2954 (current-file Info-current-file)
141b49f5
JL
2955 manuals matches node nodes)
2956 (let ((Info-fontify-maximum-menu-size nil))
ab2f22ad 2957 (Info-directory)
59ad6ae5
EZ
2958 ;; current-node and current-file are nil when they invoke info-apropos
2959 ;; as the first Info command, i.e. info-apropos loads info.el. In that
2960 ;; case, we use (DIR)Top instead, to avoid signalling an error after
2961 ;; the search is complete.
2962 (when (null current-node)
2963 (setq current-file Info-current-file)
2964 (setq current-node Info-current-node))
ab2f22ad
JH
2965 (message "Searching indices...")
2966 (goto-char (point-min))
2967 (re-search-forward "\\* Menu: *\n" nil t)
7b30b20c 2968 (while (re-search-forward "\\*.*: *(\\([^)]+\\))" nil t)
9ef5b432
EZ
2969 ;; add-to-list makes sure we don't have duplicates in `manuals',
2970 ;; so that the following dolist loop runs faster.
2971 (add-to-list 'manuals (match-string 1)))
185cff95 2972 (dolist (manual (nreverse manuals))
ab2f22ad 2973 (message "Searching %s" manual)
185cff95
JL
2974 (condition-case err
2975 (if (setq nodes (Info-index-nodes (Info-find-file manual)))
141b49f5
JL
2976 (save-excursion
2977 (Info-find-node manual (car nodes))
2978 (while
2979 (progn
2980 (goto-char (point-min))
2981 (while (re-search-forward pattern nil t)
185cff95
JL
2982 (setq matches
2983 (cons (list manual
2984 (match-string-no-properties 1)
2985 (match-string-no-properties 2)
2986 (match-string-no-properties 3))
2987 matches)))
141b49f5 2988 (setq nodes (cdr nodes) node (car nodes)))
185cff95
JL
2989 (Info-goto-node node))))
2990 (error
2991 (message "%s" (if (eq (car-safe err) 'error)
2992 (nth 1 err) err))
2993 (sit-for 1 t)))))
bec275ed 2994 (Info-find-node current-file current-node)
37600b6d
JH
2995 (setq Info-history ohist
2996 Info-history-list ohist-list)
ab2f22ad
JH
2997 (message "Searching indices...done")
2998 (if (null matches)
2999 (message "No matches found")
3000 (with-current-buffer (get-buffer-create " *info-apropos*")
3001 (erase-buffer)
141b49f5 3002 (insert "\n\^_\nFile: apropos, Node: Index, Up: (dir)\n")
185cff95
JL
3003 (insert "* Menu: \nNodes whose indices contain `" string "':\n\n")
3004 (dolist (entry (nreverse matches))
5242942f
JH
3005 (insert
3006 (format "* %-38s (%s)%s.%s\n"
3007 (concat (nth 1 entry) " [" (nth 0 entry) "]:")
3008 (nth 0 entry)
3009 (nth 2 entry)
3010 (if (nth 3 entry)
3011 (concat " (line " (nth 3 entry) ")")
3012 "")))))
bec275ed 3013 (Info-find-node 'apropos "Index")
82f1aca9 3014 (setq Info-complete-cache nil)))))
ab2f22ad 3015
a384cab3
JB
3016(defun Info-undefined ()
3017 "Make command be undefined in Info."
3018 (interactive)
3019 (ding))
3020
3021(defun Info-help ()
3022 "Enter the Info tutorial."
3023 (interactive)
3024 (delete-other-windows)
3025 (Info-find-node "info"
3026 (if (< (window-height) 23)
3027 "Help-Small-Screen"
3028 "Help")))
3029
3030(defun Info-summary ()
3031 "Display a brief summary of all Info commands."
3032 (interactive)
3033 (save-window-excursion
3034 (switch-to-buffer "*Help*")
881c84c7 3035 (setq buffer-read-only nil)
a384cab3
JB
3036 (erase-buffer)
3037 (insert (documentation 'Info-mode))
9d29f94c 3038 (help-mode)
a384cab3
JB
3039 (goto-char (point-min))
3040 (let (ch flag)
3041 (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
3042 (message (if flag "Type Space to see more"
3043 "Type Space to return to Info"))
fc039bdc 3044 (if (not (eq ?\s (setq ch (read-event))))
dbc4e1c1 3045 (progn (setq unread-command-events (list ch)) nil)
a384cab3 3046 flag))
552775bd
RS
3047 (scroll-up)))
3048 (bury-buffer "*Help*")))
a384cab3
JB
3049\f
3050(defun Info-get-token (pos start all &optional errorstring)
fdf4b680 3051 "Return the token around POS.
a384cab3
JB
3052POS must be somewhere inside the token
3053START is a regular expression which will match the
3054 beginning of the tokens delimited string
3055ALL is a regular expression with a single
90a715f0 3056 parenthesized subpattern which is the token to be
fdf4b680 3057 returned. E.g. '{\(.*\)}' would return any string
a384cab3 3058 enclosed in braces around POS.
fdf4b680 3059ERRORSTRING optional fourth argument, controls action on no match
a384cab3
JB
3060 nil: return nil
3061 t: beep
3062 a string: signal an error, using that string."
1bcedb3b
RS
3063 (let ((case-fold-search t))
3064 (save-excursion
3065 (goto-char pos)
3066 ;; First look for a match for START that goes across POS.
3067 (while (and (not (bobp)) (> (point) (- pos (length start)))
3068 (not (looking-at start)))
3069 (forward-char -1))
3070 ;; If we did not find one, search back for START
3071 ;; (this finds only matches that end at or before POS).
3072 (or (looking-at start)
3073 (progn
3074 (goto-char pos)
3075 (re-search-backward start (max (point-min) (- pos 200)) 'yes)))
3076 (let (found)
3077 (while (and (re-search-forward all (min (point-max) (+ pos 200)) 'yes)
3078 (not (setq found (and (<= (match-beginning 0) pos)
3079 (> (match-end 0) pos))))))
3080 (if (and found (<= (match-beginning 0) pos)
3081 (> (match-end 0) pos))
110d4e80 3082 (match-string-no-properties 1)
1bcedb3b
RS
3083 (cond ((null errorstring)
3084 nil)
3085 ((eq errorstring t)
3086 (beep)
3087 nil)
3088 (t
3089 (error "No %s around position %d" errorstring pos))))))))
a384cab3 3090
981947af 3091(defun Info-mouse-follow-nearest-node (click)
f9969361
RS
3092 "\\<Info-mode-map>Follow a node reference near point.
3093Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
981947af 3094At end of the node's text, moves to the next node, or up if none."
f9969361 3095 (interactive "e")
d1268e52 3096 (mouse-set-point click)
981947af
KH
3097 (and (not (Info-try-follow-nearest-node))
3098 (save-excursion (forward-line 1) (eobp))
ed690657 3099 (Info-next-preorder)))
981947af 3100
8a7757f6 3101(defun Info-follow-nearest-node (&optional fork)
d0b560c6
JB
3102 "Follow a node reference near point.
3103If point is on a reference, follow that reference. Otherwise,
04d6dfe2
R
3104if point is in a menu item description, follow that menu item.
3105
6716bfa4 3106If FORK is non-nil (interactively with a prefix arg), show the node in
04d6dfe2 3107a new Info buffer.
6716bfa4 3108If FORK is a string, it is the name to use for the new buffer."
8a7757f6
JL
3109 (interactive "P")
3110 (or (Info-try-follow-nearest-node fork)
d0b560c6
JB
3111 (when (save-excursion
3112 (search-backward "\n* menu:" nil t))
3113 (save-excursion
3114 (beginning-of-line)
3115 (while (not (or (bobp) (looking-at "[^ \t]\\|[ \t]*$")))
3116 (beginning-of-line 0))
3117 (when (looking-at "\\* +\\([^\t\n]*\\):")
3118 (Info-goto-node
8a7757f6 3119 (Info-extract-menu-item (match-string-no-properties 1)) fork)
d0b560c6
JB
3120 t)))
3121 (error "Point neither on reference nor in menu item description")))
981947af
KH
3122
3123;; Common subroutine.
8a7757f6 3124(defun Info-try-follow-nearest-node (&optional fork)
8aed3f67
RS
3125 "Follow a node reference near point. Return non-nil if successful.
3126If FORK is non-nil, it i spassed to `Info-goto-node'."
6af7040d 3127 (let (node)
a384cab3 3128 (cond
4ba89826 3129 ((Info-get-token (point) "[hf]t?tps?://" "[hf]t?tps?://\\([^ \t\n\"`({<>})']+\\)")
8a7757f6
JL
3130 (setq node t)
3131 (browse-url (browse-url-url-at-point)))
3132 ((setq node (Info-get-token (point) "\\*note[ \n\t]+"
3133 "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))
8a7757f6 3134 (Info-follow-reference node fork))
505b68d5 3135 ;; menu item: node name
b37daea4 3136 ((setq node (Info-get-token (point) "\\* +" "\\* +\\([^:]*\\)::"))
8a7757f6 3137 (Info-goto-node node fork))
836bb478 3138 ;; menu item: node name or index entry
505b68d5 3139 ((Info-get-token (point) "\\* +" "\\* +\\(.*\\): ")
631ba13e
RS
3140 (beginning-of-line)
3141 (forward-char 2)
141b49f5 3142 (setq node (Info-extract-menu-node-name nil (Info-index-node)))
8a7757f6 3143 (Info-goto-node node fork))
bc2ada62 3144 ((setq node (Info-get-token (point) "Up: " "Up: \\([^,\n\t]*\\)"))
8a7757f6 3145 (Info-goto-node node fork))
bc2ada62 3146 ((setq node (Info-get-token (point) "Next: " "Next: \\([^,\n\t]*\\)"))
8a7757f6 3147 (Info-goto-node node fork))
bc2ada62 3148 ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
8a7757f6 3149 (Info-goto-node "Top" fork))
bc2ada62 3150 ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
8a7757f6 3151 (Info-goto-node node fork)))
981947af 3152 node))
a384cab3 3153\f
fdeb777a
SM
3154(defvar Info-mode-map
3155 (let ((map (make-keymap)))
3156 (suppress-keymap map)
3157 (define-key map "." 'beginning-of-buffer)
3158 (define-key map " " 'Info-scroll-up)
3159 (define-key map "\C-m" 'Info-follow-nearest-node)
3160 (define-key map "\t" 'Info-next-reference)
3161 (define-key map "\e\t" 'Info-prev-reference)
3162 (define-key map [(shift tab)] 'Info-prev-reference)
3163 (define-key map [backtab] 'Info-prev-reference)
3164 (define-key map "1" 'Info-nth-menu-item)
3165 (define-key map "2" 'Info-nth-menu-item)
3166 (define-key map "3" 'Info-nth-menu-item)
3167 (define-key map "4" 'Info-nth-menu-item)
3168 (define-key map "5" 'Info-nth-menu-item)
3169 (define-key map "6" 'Info-nth-menu-item)
3170 (define-key map "7" 'Info-nth-menu-item)
3171 (define-key map "8" 'Info-nth-menu-item)
3172 (define-key map "9" 'Info-nth-menu-item)
3173 (define-key map "0" 'undefined)
3174 (define-key map "?" 'Info-summary)
3175 (define-key map "]" 'Info-forward-node)
3176 (define-key map "[" 'Info-backward-node)
3177 (define-key map "<" 'Info-top-node)
3178 (define-key map ">" 'Info-final-node)
3179 (define-key map "b" 'beginning-of-buffer)
3180 (define-key map "d" 'Info-directory)
3181 (define-key map "e" 'Info-edit)
3182 (define-key map "f" 'Info-follow-reference)
3183 (define-key map "g" 'Info-goto-node)
3184 (define-key map "h" 'Info-help)
3185 (define-key map "i" 'Info-index)
3186 (define-key map "l" 'Info-history-back)
3187 (define-key map "L" 'Info-history)
3188 (define-key map "m" 'Info-menu)
3189 (define-key map "n" 'Info-next)
3190 (define-key map "p" 'Info-prev)
3191 (define-key map "q" 'Info-exit)
3192 (define-key map "r" 'Info-history-forward)
3193 (define-key map "s" 'Info-search)
3194 (define-key map "S" 'Info-search-case-sensitively)
3195 ;; For consistency with Rmail.
3196 (define-key map "\M-s" 'Info-search)
3197 (define-key map "\M-n" 'clone-buffer)
3198 (define-key map "t" 'Info-top-node)
3199 (define-key map "T" 'Info-toc)
3200 (define-key map "u" 'Info-up)
3201 ;; `w' for consistency with `dired-copy-filename-as-kill'.
3202 (define-key map "w" 'Info-copy-current-node-name)
3203 (define-key map "c" 'Info-copy-current-node-name)
3204 ;; `^' for consistency with `dired-up-directory'.
3205 (define-key map "^" 'Info-up)
3206 (define-key map "," 'Info-index-next)
3207 (define-key map "\177" 'Info-scroll-down)
3208 (define-key map [mouse-2] 'Info-mouse-follow-nearest-node)
3209 (define-key map [follow-link] 'mouse-face)
3210 map)
a384cab3 3211 "Keymap containing Info commands.")
fdeb777a 3212
75a209d4
RS
3213
3214(defun Info-check-pointer (item)
fdf4b680 3215 "Non-nil if ITEM is present in this node."
75a209d4
RS
3216 (condition-case nil
3217 (Info-extract-pointer item)
3218 (error nil)))
3219
d4af987a
DL
3220(easy-menu-define
3221 Info-mode-menu Info-mode-map
185cff95 3222 "Menu for Info files."
d4af987a
DL
3223 '("Info"
3224 ["Up" Info-up :active (Info-check-pointer "up")
3225 :help "Go up in the Info tree"]
3226 ["Next" Info-next :active (Info-check-pointer "next")
3227 :help "Go to the next node"]
3228 ["Previous" Info-prev :active (Info-check-pointer "prev[ious]*")
3229 :help "Go to the previous node"]
3230 ["Backward" Info-backward-node
3231 :help "Go backward one node, considering all as a sequence"]
3232 ["Forward" Info-forward-node
3233 :help "Go forward one node, considering all as a sequence"]
7210b6f5
DL
3234 ["Beginning" beginning-of-buffer
3235 :help "Go to beginning of this node"]
d4af987a
DL
3236 ["Top" Info-top-node
3237 :help "Go to top node of file"]
3238 ["Final Node" Info-final-node
3239 :help "Go to final node in this file"]
3240 ("Menu Item" ["You should never see this" report-emacs-bug t])
3241 ("Reference" ["You should never see this" report-emacs-bug t])
3242 ["Search..." Info-search
3243 :help "Search for regular expression in this Info file"]
8a7757f6
JL
3244 ["Search Next" Info-search-next
3245 :help "Search for another occurrence of regular expression"]
d1268e52 3246 ["Go to Node..." Info-goto-node
d4af987a 3247 :help "Go to a named node"]
424b9d3e
JL
3248 ["Back in history" Info-history-back :active Info-history
3249 :help "Go back in history to the last node you were at"]
3250 ["Forward in history" Info-history-forward :active Info-history-forward
3251 :help "Go forward in history"]
8a7757f6 3252 ["History" Info-history :active Info-history-list
bc224645 3253 :help "Go to menu of visited nodes"]
8a7757f6 3254 ["Table of Contents" Info-toc
bc224645 3255 :help "Go to table of contents"]
5938202c
JL
3256 ("Index"
3257 ["Lookup a String..." Info-index
d4af987a 3258 :help "Look for a string in the index items"]
5938202c 3259 ["Next Matching Item" Info-index-next :active Info-index-alternatives
ab2f22ad 3260 :help "Look for another occurrence of previous item"]
5938202c 3261 ["Lookup a string in all indices..." info-apropos
ab2f22ad 3262 :help "Look for a string in the indices of all manuals"])
126f5d4d
MB
3263 ["Copy Node Name" Info-copy-current-node-name
3264 :help "Copy the name of the current node into the kill ring"]
8a7757f6
JL
3265 ["Clone Info buffer" clone-buffer
3266 :help "Create a twin copy of the current Info buffer."]
ec03f31e 3267 ["Exit" Info-exit :help "Stop reading Info"]))
75a209d4 3268
7210b6f5
DL
3269
3270(defvar info-tool-bar-map
3271 (if (display-graphic-p)
df6aed99 3272 (let ((map (make-sparse-keymap)))
4bc5cb9a
JD
3273 (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map
3274 :rtl "right-arrow")
3275 (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map
3276 :rtl "left-arrow")
3277 (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map
3278 :rtl "next-node")
3279 (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map
3280 :rtl "prev-node")
db2cf961 3281 (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map)
df6aed99 3282 (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map)
ffda926c 3283 (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map)
94807253 3284 (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map)
df6aed99 3285 (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map)
c2dbc5f2 3286 (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map)
df6aed99 3287 map)))
7210b6f5 3288
75a209d4
RS
3289(defvar Info-menu-last-node nil)
3290;; Last node the menu was created for.
6d2c8e3e 3291;; Value is a list, (FILE-NAME NODE-NAME).
75a209d4
RS
3292
3293(defun Info-menu-update ()
fdf4b680 3294 "Update the Info menu for the current node."
75a209d4
RS
3295 (condition-case nil
3296 (if (or (not (eq major-mode 'Info-mode))
6d2c8e3e
RS
3297 (equal (list Info-current-file Info-current-node)
3298 Info-menu-last-node))
75a209d4
RS
3299 ()
3300 ;; Update menu menu.
3301 (let* ((Info-complete-menu-buffer (current-buffer))
3302 (items (nreverse (condition-case nil
e4e8ee63 3303 (Info-complete-menu-item "" nil t)
75a209d4 3304 (error nil))))
399c88ad 3305 entries current
75a209d4
RS
3306 (number 0))
3307 (while (and items (< number 9))
3308 (setq current (car items)
3309 items (cdr items)
3310 number (1+ number))
399c88ad 3311 (setq entries (cons `[,current
75a209d4
RS
3312 (Info-menu ,current)
3313 :keys ,(format "%d" number)]
3314 entries)))
3315 (if items
3316 (setq entries (cons ["Other..." Info-menu t] entries)))
3317 (or entries
5d5845dc 3318 (setq entries (list ["No menu" nil nil] nil :active)))
d4af987a 3319 (easy-menu-change '("Info") "Menu Item" (nreverse entries)))
75a209d4
RS
3320 ;; Update reference menu. Code stolen from `Info-follow-reference'.
3321 (let ((items nil)
399c88ad 3322 str i entries current
1bcedb3b
RS
3323 (number 0)
3324 (case-fold-search t))
75a209d4
RS
3325 (save-excursion
3326 (goto-char (point-min))
8a7757f6 3327 (while (re-search-forward "\\*note[ \n\t]+\\([^:]*\\):" nil t)
c60c12be 3328 (setq str (match-string 1))
75a209d4
RS
3329 (setq i 0)
3330 (while (setq i (string-match "[ \n\t]+" str i))
3331 (setq str (concat (substring str 0 i) " "
3332 (substring str (match-end 0))))
3333 (setq i (1+ i)))
3334 (setq items
3335 (cons str items))))
3336 (while (and items (< number 9))
3337 (setq current (car items)
3338 items (cdr items)
3339 number (1+ number))
399c88ad 3340 (setq entries (cons `[,current
75a209d4
RS
3341 (Info-follow-reference ,current)
3342 t]
3343 entries)))
3344 (if items
3345 (setq entries (cons ["Other..." Info-follow-reference t]
3346 entries)))
3347 (or entries
5d5845dc 3348 (setq entries (list ["No references" nil nil] nil :active)))
75a209d4
RS
3349 (easy-menu-change '("Info") "Reference" (nreverse entries)))
3350 ;; Update last seen node.
6d2c8e3e 3351 (setq Info-menu-last-node (list Info-current-file Info-current-node)))
75a209d4
RS
3352 ;; Try to avoid entering infinite beep mode in case of errors.
3353 (error (ding))))
3354
a384cab3 3355\f
50163b00 3356(defun Info-copy-current-node-name (&optional arg)
185cff95
JL
3357 "Put the name of the current Info node into the kill ring.
3358The name of the Info file is prepended to the node name in parentheses.
50163b00
JL
3359With a zero prefix arg, put the name inside a function call to `info'."
3360 (interactive "P")
126f5d4d 3361 (unless Info-current-node
185cff95 3362 (error "No current Info node"))
bec275ed 3363 (let ((node (if (stringp Info-current-file)
e56f84d7 3364 (concat "(" (file-name-nondirectory Info-current-file) ") "
bec275ed 3365 Info-current-node))))
50163b00
JL
3366 (if (zerop (prefix-numeric-value arg))
3367 (setq node (concat "(info \"" node "\")")))
bec275ed
JL
3368 (unless (stringp Info-current-file)
3369 (setq node (format "(Info-find-node '%S '%S)"
3370 Info-current-file Info-current-node)))
50163b00
JL
3371 (kill-new node)
3372 (message "%s" node)))
126f5d4d
MB
3373
3374\f
a384cab3 3375;; Info mode is suitable only for specially formatted data.
68bc119c 3376(put 'Info-mode 'mode-class 'special)
2fa8f8a4 3377(put 'Info-mode 'no-clone-indirect t)
a384cab3 3378
f4f2bca3 3379(defvar tool-bar-map)
e0385bf4 3380(defvar bookmark-make-record-function)
f4f2bca3 3381
80123c9e
LH
3382;; Autoload cookie needed by desktop.el
3383;;;###autoload
a384cab3 3384(defun Info-mode ()
fdf4b680 3385 "Info mode provides commands for browsing through the Info documentation tree.
a384cab3
JB
3386Documentation in Info is divided into \"nodes\", each of which discusses
3387one topic and contains references to other nodes which discuss related
3388topics. Info has commands to follow the references and show you other nodes.
3389
fdf4b680 3390\\<Info-mode-map>\
a384cab3 3391\\[Info-help] Invoke the Info tutorial.
6b136ab9 3392\\[Info-exit] Quit Info: reselect previously selected buffer.
a384cab3
JB
3393
3394Selecting other nodes:
21d5959f
RS
3395\\[Info-mouse-follow-nearest-node]
3396 Follow a node reference you click on.
3397 This works with menu items, cross references, and
3398 the \"next\", \"previous\" and \"up\", depending on where you click.
6b136ab9 3399\\[Info-follow-nearest-node] Follow a node reference near point, like \\[Info-mouse-follow-nearest-node].
a384cab3 3400\\[Info-next] Move to the \"next\" node of this node.
bc2ada62 3401\\[Info-prev] Move to the \"previous\" node of this node.
a384cab3
JB
3402\\[Info-up] Move \"up\" from this node.
3403\\[Info-menu] Pick menu item specified by name (or abbreviation).
e8042313 3404 Picking a menu item causes another node to be selected.
aea2a8da 3405\\[Info-directory] Go to the Info directory node.
8300a5b5
JL
3406\\[Info-top-node] Go to the Top node of this file.
3407\\[Info-final-node] Go to the final node in this file.
3408\\[Info-backward-node] Go backward one node, considering all nodes as forming one sequence.
3409\\[Info-forward-node] Go forward one node, considering all nodes as forming one sequence.
3410\\[Info-next-reference] Move cursor to next cross-reference or menu item.
3411\\[Info-prev-reference] Move cursor to previous cross-reference or menu item.
a384cab3 3412\\[Info-follow-reference] Follow a cross reference. Reads name of reference.
424b9d3e
JL
3413\\[Info-history-back] Move back in history to the last node you were at.
3414\\[Info-history-forward] Move forward in history to the node you returned from after using \\[Info-history-back].
bc224645
JL
3415\\[Info-history] Go to menu of visited nodes.
3416\\[Info-toc] Go to table of contents of the current Info file.
a384cab3
JB
3417
3418Moving within a node:
177c3549 3419\\[Info-scroll-up] Normally, scroll forward a full screen.
e8042313
PJ
3420 Once you scroll far enough in a node that its menu appears on the
3421 screen but after point, the next scroll moves into its first
3422 subnode. When after all menu items (or if there is no menu),
3423 move up to the parent node.
177c3549 3424\\[Info-scroll-down] Normally, scroll backward. If the beginning of the buffer is
e8042313
PJ
3425 already visible, try to go to the previous menu entry, or up
3426 if there is none.
399c88ad 3427\\[beginning-of-buffer] Go to beginning of node.
a384cab3 3428
a384cab3 3429Advanced commands:
a384cab3 3430\\[Info-search] Search through this Info file for specified regexp,
e8042313 3431 and select the node in which the next occurrence is found.
bc224645 3432\\[Info-search-case-sensitively] Search through this Info file for specified regexp case-sensitively.
8a7757f6 3433\\[Info-search-next] Search for another occurrence of regexp
89b064a9 3434 from a previous \\<Info-mode-map>\\[Info-search] command.
9012c084 3435\\[Info-index] Search for a topic in this manual's Index and go to index entry.
8300a5b5
JL
3436\\[Info-index-next] (comma) Move to the next match from a previous \\<Info-mode-map>\\[Info-index] command.
3437\\[info-apropos] Look for a string in the indices of all manuals.
3438\\[Info-goto-node] Move to node specified by name.
3439 You may include a filename as well, as (FILENAME)NODENAME.
34401 .. 9 Pick first ... ninth item in node's menu.
3441 Every third `*' is highlighted to help pick the right number.
3442\\[Info-copy-current-node-name] Put name of current Info node in the kill ring.
3443\\[clone-buffer] Select a new cloned Info buffer in another window.
3444\\[universal-argument] \\[info] Move to new Info file with completion.
3445\\[universal-argument] N \\[info] Select Info buffer with prefix number in the name *info*<N>."
a384cab3
JB
3446 (kill-all-local-variables)
3447 (setq major-mode 'Info-mode)
3448 (setq mode-name "Info")
f73299f3 3449 (setq tab-width 8)
a384cab3 3450 (use-local-map Info-mode-map)
75a209d4 3451 (add-hook 'activate-menubar-hook 'Info-menu-update nil t)
a384cab3
JB
3452 (set-syntax-table text-mode-syntax-table)
3453 (setq local-abbrev-table text-mode-abbrev-table)
3454 (setq case-fold-search t)
3455 (setq buffer-read-only t)
a384cab3
JB
3456 (make-local-variable 'Info-current-file)
3457 (make-local-variable 'Info-current-subfile)
3458 (make-local-variable 'Info-current-node)
3459 (make-local-variable 'Info-tag-table-marker)
c5fe2ff1
RS
3460 (setq Info-tag-table-marker (make-marker))
3461 (make-local-variable 'Info-tag-table-buffer)
3462 (setq Info-tag-table-buffer nil)
a384cab3 3463 (make-local-variable 'Info-history)
424b9d3e 3464 (make-local-variable 'Info-history-forward)
1143a6b0 3465 (make-local-variable 'Info-index-alternatives)
dde7f979
SM
3466 (setq header-line-format
3467 (if Info-use-header-line
3536c121 3468 '(:eval (get-text-property (point-min) 'header-line))
dde7f979 3469 nil)) ; so the header line isn't displayed
7210b6f5 3470 (set (make-local-variable 'tool-bar-map) info-tool-bar-map)
93480d70
RS
3471 ;; This is for the sake of the invisible text we use handling titles.
3472 (make-local-variable 'line-move-ignore-invisible)
3473 (setq line-move-ignore-invisible t)
9d9d10ec 3474 (make-local-variable 'desktop-save-buffer)
ab357a65
RS
3475 (make-local-variable 'widen-automatically)
3476 (setq widen-automatically nil)
9d9d10ec 3477 (setq desktop-save-buffer 'Info-desktop-buffer-misc-data)
34d86208 3478 (add-hook 'kill-buffer-hook 'Info-kill-buffer nil t)
274bcfe0 3479 (add-hook 'clone-buffer-hook 'Info-clone-buffer nil t)
3d12b331 3480 (add-hook 'change-major-mode-hook 'font-lock-defontify nil t)
d457219d 3481 (add-hook 'isearch-mode-hook 'Info-isearch-start nil t)
0b02cda9
JL
3482 (set (make-local-variable 'isearch-search-fun-function)
3483 'Info-isearch-search)
3484 (set (make-local-variable 'isearch-wrap-function)
3485 'Info-isearch-wrap)
3486 (set (make-local-variable 'isearch-push-state-function)
3487 'Info-isearch-push-state)
0ec3382b
JL
3488 (set (make-local-variable 'isearch-success-function)
3489 'Info-search-success-function)
0b02cda9
JL
3490 (set (make-local-variable 'search-whitespace-regexp)
3491 Info-search-whitespace-regexp)
e56f84d7
TTN
3492 (set (make-local-variable 'revert-buffer-function)
3493 'Info-revert-buffer-function)
a384cab3 3494 (Info-set-mode-line)
e0385bf4
KF
3495 (set (make-local-variable 'bookmark-make-record-function)
3496 'Info-bookmark-make-record)
d39f149d 3497 (run-mode-hooks 'Info-mode-hook))
a384cab3 3498
34d86208
JL
3499;; When an Info buffer is killed, make sure the associated tags buffer
3500;; is killed too.
3501(defun Info-kill-buffer ()
3502 (and (eq major-mode 'Info-mode)
3503 Info-tag-table-buffer
3504 (kill-buffer Info-tag-table-buffer)))
3505
274bcfe0
RS
3506;; Placed on `clone-buffer-hook'.
3507(defun Info-clone-buffer ()
4fceda3c
SM
3508 (when (bufferp Info-tag-table-buffer)
3509 (setq Info-tag-table-buffer
4ce5c223
SM
3510 (with-current-buffer Info-tag-table-buffer (clone-buffer))))
3511 (let ((m Info-tag-table-marker))
3512 (when (markerp m)
15533ae2 3513 (setq Info-tag-table-marker
4ce5c223 3514 (if (and (marker-position m) (bufferp Info-tag-table-buffer))
15533ae2
SM
3515 (with-current-buffer Info-tag-table-buffer
3516 (copy-marker (marker-position m)))
3517 (make-marker))))))
4fceda3c 3518
7210b6f5
DL
3519(defvar Info-edit-map (let ((map (make-sparse-keymap)))
3520 (set-keymap-parent map text-mode-map)
3521 (define-key map "\C-c\C-c" 'Info-cease-edit)
3522 map)
a384cab3 3523 "Local keymap used within `e' command of Info.")
a384cab3
JB
3524
3525;; Info-edit mode is suitable only for specially formatted data.
68bc119c 3526(put 'Info-edit-mode 'mode-class 'special)
a384cab3
JB
3527
3528(defun Info-edit-mode ()
3529 "Major mode for editing the contents of an Info node.
a426b157 3530Like text mode with the addition of `Info-cease-edit'
a384cab3
JB
3531which returns to Info mode for browsing.
3532\\{Info-edit-map}"
a384cab3
JB
3533 (use-local-map Info-edit-map)
3534 (setq major-mode 'Info-edit-mode)
3535 (setq mode-name "Info Edit")
3536 (kill-local-variable 'mode-line-buffer-identification)
3537 (setq buffer-read-only nil)
2c609f53 3538 (force-mode-line-update)
e82c28f9 3539 (buffer-enable-undo (current-buffer))
d39f149d 3540 (run-mode-hooks 'Info-edit-mode-hook))
e82c28f9
KH
3541
3542(defun Info-edit ()
3543 "Edit the contents of this Info node.
3544Allowed only if variable `Info-enable-edit' is non-nil."
3545 (interactive)
3546 (or Info-enable-edit
185cff95 3547 (error "Editing Info nodes is not enabled"))
e82c28f9 3548 (Info-edit-mode)
8ab3e50b 3549 (message "%s" (substitute-command-keys
760d5cb3 3550 "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
a384cab3
JB
3551
3552(defun Info-cease-edit ()
3553 "Finish editing Info node; switch back to Info proper."
3554 (interactive)
3555 ;; Do this first, so nothing has changed if user C-g's at query.
3556 (and (buffer-modified-p)
3557 (y-or-n-p "Save the file? ")
3558 (save-buffer))
3559 (use-local-map Info-mode-map)
3560 (setq major-mode 'Info-mode)
3561 (setq mode-name "Info")
3562 (Info-set-mode-line)
3563 (setq buffer-read-only t)
2c609f53 3564 (force-mode-line-update)
a384cab3
JB
3565 (and (marker-position Info-tag-table-marker)
3566 (buffer-modified-p)
3567 (message "Tags may have changed. Use Info-tagify if necessary")))
f0a8a3f1 3568\f
f88ab1c9 3569(defvar Info-file-list-for-emacs
02c9f5cd 3570 '("ediff" "eudc" "forms" "gnus" "info" ("Info" . "info") ("mh" . "mh-e")
906b3163
EZ
3571 "sc" "message" ("dired" . "dired-x") "viper" "vip" "idlwave"
3572 ("c" . "ccmode") ("c++" . "ccmode") ("objc" . "ccmode")
3573 ("java" . "ccmode") ("idl" . "ccmode") ("pike" . "ccmode")
af718538
EZ
3574 ("skeleton" . "autotype") ("auto-insert" . "autotype")
3575 ("copyright" . "autotype") ("executable" . "autotype")
3576 ("time-stamp" . "autotype") ("quickurl" . "autotype")
3577 ("tempo" . "autotype") ("hippie-expand" . "autotype")
807378ad
EZ
3578 ("cvs" . "pcl-cvs") ("ada" . "ada-mode") "calc"
3579 ("calcAlg" . "calc") ("calcDigit" . "calc") ("calcVar" . "calc")
906b3163
EZ
3580 "ebrowse" "eshell" "cl" "reftex" "speedbar" "widget" "woman"
3581 ("mail-header" . "emacs-mime") ("mail-content" . "emacs-mime")
3582 ("mail-encode" . "emacs-mime") ("mail-decode" . "emacs-mime")
3583 ("rfc2045" . "emacs-mime")
3584 ("rfc2231" . "emacs-mime") ("rfc2047" . "emacs-mime")
3585 ("rfc2045" . "emacs-mime") ("rfc1843" . "emacs-mime")
3586 ("ietf-drums" . "emacs-mime") ("quoted-printable" . "emacs-mime")
3587 ("binhex" . "emacs-mime") ("uudecode" . "emacs-mime")
3588 ("mailcap" . "emacs-mime") ("mm" . "emacs-mime")
3589 ("mml" . "emacs-mime"))
f88ab1c9
RS
3590 "List of Info files that describe Emacs commands.
3591An element can be a file name, or a list of the form (PREFIX . FILE)
3592where PREFIX is a name prefix and FILE is the file to look in.
3593If the element is just a file name, the file name also serves as the prefix.")
3594
f0a8a3f1 3595(defun Info-find-emacs-command-nodes (command)
f88ab1c9 3596 "Return a list of locations documenting COMMAND.
f57b2cd8
RS
3597The `info-file' property of COMMAND says which Info manual to search.
3598If COMMAND has no property, the variable `Info-file-list-for-emacs'
3599defines heuristics for which Info manual to try.
fdf4b680 3600The locations are of the format used in `Info-history', i.e.
02c9f5cd
JL
3601\(FILENAME NODENAME BUFFERPOS\), where BUFFERPOS is the line number
3602in the first element of the returned list (which is treated specially in
3603`Info-goto-emacs-command-node'), and 0 for the rest elements of a list."
3604 (let ((where '()) line-number
b37daea4 3605 (cmd-desc (concat "^\\* +" (regexp-quote (symbol-name command))
f254e21f 3606 "\\( <[0-9]+>\\)?:\\s *\\(.*\\)\\."
02c9f5cd 3607 "\\(?:[ \t\n]+(line +\\([0-9]+\\))\\)?"))
f88ab1c9 3608 (info-file "emacs")) ;default
185cff95 3609 ;; Determine which Info file this command is documented in.
f88ab1c9
RS
3610 (if (get command 'info-file)
3611 (setq info-file (get command 'info-file))
3612 ;; If it doesn't say explicitly, test its name against
3613 ;; various prefixes that we know.
3614 (let ((file-list Info-file-list-for-emacs))
3615 (while file-list
3616 (let* ((elt (car file-list))
3617 (name (if (consp elt)
3618 (car elt)
3619 elt))
3620 (file (if (consp elt) (cdr elt) elt))
cc3f6c32 3621 (case-fold-search nil)
f57b2cd8 3622 (regexp (concat "\\`" (regexp-quote name)
f88ab1c9
RS
3623 "\\(\\'\\|-\\)")))
3624 (if (string-match regexp (symbol-name command))
3625 (setq info-file file file-list nil))
3626 (setq file-list (cdr file-list))))))
218c2cc7 3627 (Info-find-node info-file "Top")
218c2cc7
EZ
3628 ;; Bind Info-history to nil, to prevent the index nodes from
3629 ;; getting into the node history.
3630 (let ((Info-history nil)
141b49f5
JL
3631 (Info-history-list nil)
3632 node (nodes (Info-index-nodes)))
3633 (Info-goto-node (car nodes))
218c2cc7
EZ
3634 (while
3635 (progn
3636 (goto-char (point-min))
3637 (while (re-search-forward cmd-desc nil t)
3638 (setq where
3639 (cons (list Info-current-file
3640 (match-string-no-properties 2)
760d5cb3 3641 0)
02c9f5cd
JL
3642 where))
3643 (setq line-number (and (match-beginning 3)
3644 (string-to-number (match-string 3)))))
141b49f5 3645 (and (setq nodes (cdr nodes) node (car nodes))))
218c2cc7 3646 (Info-goto-node node)))
02c9f5cd
JL
3647 (if (and line-number where)
3648 (cons (list (nth 0 (car where)) (nth 1 (car where)) line-number)
3649 (cdr where))
3650 where)))
f0a8a3f1 3651
02c9f5cd 3652;;;###autoload (put 'Info-goto-emacs-command-node 'info-file "emacs")
f0a8a3f1
RM
3653;;;###autoload
3654(defun Info-goto-emacs-command-node (command)
e0568e86 3655 "Go to the Info node in the Emacs manual for command COMMAND.
30db89f9 3656The command is found by looking up in Emacs manual's indices
f57b2cd8 3657or in another manual found via COMMAND's `info-file' property or
1ed6e0c3
JPW
3658the variable `Info-file-list-for-emacs'.
3659COMMAND must be a symbol or string."
f0a8a3f1 3660 (interactive "CFind documentation for command: ")
fafb00dc
MY
3661 ;; If command is given as a string, convert it to a symbol.
3662 (if (stringp command)
1ed6e0c3 3663 (setq command (intern command)))
f0a8a3f1
RM
3664 (or (commandp command)
3665 (signal 'wrong-type-argument (list 'commandp command)))
3666 (let ((where (Info-find-emacs-command-nodes command)))
3667 (if where
3668 (let ((num-matches (length where)))
3669 ;; Get Info running, and pop to it in another window.
3670 (save-window-excursion
3671 (info))
89b064a9 3672 (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
218c2cc7
EZ
3673 ;; Bind Info-history to nil, to prevent the last Index node
3674 ;; visited by Info-find-emacs-command-nodes from being
3675 ;; pushed onto the history.
02c9f5cd
JL
3676 (let ((Info-history nil) (Info-history-list nil)
3677 (line-number (nth 2 (car where))))
3678 (Info-find-node (nth 0 (car where)) (nth 1 (car where)))
3679 (if (and (integerp line-number) (> line-number 0))
3680 (forward-line (1- line-number))))
f0a8a3f1
RM
3681 (if (> num-matches 1)
3682 (progn
218c2cc7
EZ
3683 ;; (car where) will be pushed onto Info-history
3684 ;; when/if they go to another node. Put the other
3685 ;; nodes that were found on the history.
f0a8a3f1 3686 (setq Info-history (nconc (cdr where) Info-history))
8ab3e50b 3687 (message "Found %d other entr%s. Use %s to see %s."
cedb118c
RS
3688 (1- num-matches)
3689 (if (> num-matches 2) "ies" "y")
424b9d3e 3690 (substitute-command-keys "\\[Info-history-back]")
cedb118c 3691 (if (> num-matches 2) "them" "it")))))
e9b81433 3692 (error "Couldn't find documentation for %s" command))))
f0a8a3f1 3693
02c9f5cd 3694;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file "emacs")
f0a8a3f1
RM
3695;;;###autoload
3696(defun Info-goto-emacs-key-command-node (key)
30db89f9
EZ
3697 "Go to the node in the Emacs manual which describes the command bound to KEY.
3698KEY is a string.
fdf4b680 3699Interactively, if the binding is `execute-extended-command', a command is read.
30db89f9 3700The command is found by looking up in Emacs manual's indices
f57b2cd8
RS
3701or in another manual found via COMMAND's `info-file' property or
3702the variable `Info-file-list-for-emacs'."
ea33749b 3703 (interactive "kFind documentation for key: ")
f0a8a3f1
RM
3704 (let ((command (key-binding key)))
3705 (cond ((null command)
9e5c2f50 3706 (message "%s is undefined" (key-description key)))
f0a8a3f1
RM
3707 ((and (interactive-p)
3708 (eq command 'execute-extended-command))
3709 (Info-goto-emacs-command-node
3710 (read-command "Find documentation for command: ")))
3711 (t
3712 (Info-goto-emacs-command-node command)))))
552775bd 3713\f
efb21aab
RS
3714(defvar Info-next-link-keymap
3715 (let ((keymap (make-sparse-keymap)))
3716 (define-key keymap [header-line mouse-1] 'Info-next)
3717 (define-key keymap [header-line mouse-2] 'Info-next)
3718 (define-key keymap [header-line down-mouse-1] 'ignore)
3719 (define-key keymap [mouse-2] 'Info-next)
3c1498ac 3720 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3721 keymap)
3722 "Keymap to put on the Next link in the text or the header line.")
3723
3724(defvar Info-prev-link-keymap
3725 (let ((keymap (make-sparse-keymap)))
3726 (define-key keymap [header-line mouse-1] 'Info-prev)
3727 (define-key keymap [header-line mouse-2] 'Info-prev)
3728 (define-key keymap [header-line down-mouse-1] 'ignore)
3729 (define-key keymap [mouse-2] 'Info-prev)
3c1498ac 3730 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3731 keymap)
3732 "Keymap to put on the Prev link in the text or the header line.")
3733
efb21aab
RS
3734(defvar Info-up-link-keymap
3735 (let ((keymap (make-sparse-keymap)))
3736 (define-key keymap [header-line mouse-1] 'Info-up)
3737 (define-key keymap [header-line mouse-2] 'Info-up)
3738 (define-key keymap [header-line down-mouse-1] 'ignore)
3739 (define-key keymap [mouse-2] 'Info-up)
3c1498ac 3740 (define-key keymap [follow-link] 'mouse-face)
efb21aab
RS
3741 keymap)
3742 "Keymap to put on the Up link in the text or the header line.")
3743
586f9284 3744(defcustom Info-breadcrumbs-depth 4
fdeb777a
SM
3745 "Depth of breadcrumbs to display.
37460 means do not display breadcrumbs."
3747 :type 'integer)
3748
3749(defun Info-insert-breadcrumbs ()
586f9284
JL
3750 (let ((nodes (Info-toc-nodes Info-current-file))
3751 (node Info-current-node)
fdeb777a 3752 (crumbs ())
586f9284
JL
3753 (depth Info-breadcrumbs-depth))
3754
3755 ;; Get ancestors from the cached parent-children node info
3756 (while (and (not (equal "Top" node)) (> depth 0))
3757 (setq node (nth 1 (assoc node nodes)))
3758 (if node (push node crumbs))
3759 (setq depth (1- depth)))
3760
3761 ;; Add bottom node.
3762 (when Info-use-header-line
3763 ;; Let it disappear if crumbs is nil.
3764 (nconc crumbs (list Info-current-node)))
3765 (when (or Info-use-header-line crumbs)
3766 ;; Add top node (and continuation if needed).
3767 (setq crumbs
3768 (cons "Top" (if (member (pop crumbs) '(nil "Top"))
3769 crumbs (cons nil crumbs))))
3770 ;; Eliminate duplicate.
3771 (forward-line 1)
3772 (dolist (node crumbs)
3773 (let ((text
3774 (if (not (equal node "Top")) node
3775 (format "(%s)Top"
3776 (if (stringp Info-current-file)
3777 (file-name-nondirectory Info-current-file)
3778 ;; Can be `toc', `apropos', or even `history'.
3779 Info-current-file)))))
3780 (insert (if (bolp) "" " > ")
3781 (cond
3782 ((null node) "...")
3783 ((equal node Info-current-node)
3784 ;; No point linking to ourselves.
3785 (propertize text 'font-lock-face 'info-header-node))
3786 (t
3787 (concat "*Note " text "::"))))))
3788 (insert "\n"))))
fdeb777a 3789
552775bd 3790(defun Info-fontify-node ()
8a7757f6
JL
3791 "Fontify the node."
3792 (save-excursion
3793 (let* ((inhibit-read-only t)
3794 (case-fold-search t)
3795 paragraph-markers
3796 (not-fontified-p ; the node hasn't already been fontified
f9b27c85 3797 (not (let ((where (next-single-property-change (point-min)
499878f8 3798 'font-lock-face)))
8a7757f6
JL
3799 (and where (not (= where (point-max)))))))
3800 (fontify-visited-p ; visited nodes need to be re-fontified
3801 (and Info-fontify-visited-nodes
3802 ;; Don't take time to refontify visited nodes in huge nodes
3d0fc0b7 3803 Info-fontify-maximum-menu-size
e14105f6
TTN
3804 (< (- (point-max) (point-min)) Info-fontify-maximum-menu-size)))
3805 rbeg rend)
8a7757f6
JL
3806
3807 ;; Fontify header line
3808 (goto-char (point-min))
3809 (when (and not-fontified-p (looking-at "^\\(File: [^,: \t]+,?[ \t]+\\)?"))
3810 (goto-char (match-end 0))
3811 (while (looking-at "[ \t]*\\([^:, \t\n]+\\):[ \t]+\\([^:,\t\n]+\\),?")
3812 (goto-char (match-end 0))
3813 (let* ((nbeg (match-beginning 2))
3814 (nend (match-end 2))
3815 (tbeg (match-beginning 1))
3816 (tag (match-string 1)))
99315976 3817 (if (string-equal (downcase tag) "node")
8a7757f6
JL
3818 (put-text-property nbeg nend 'font-lock-face 'info-header-node)
3819 (put-text-property nbeg nend 'font-lock-face 'info-header-xref)
3820 (put-text-property tbeg nend 'mouse-face 'highlight)
3821 (put-text-property tbeg nend
3822 'help-echo
3c1498ac 3823 (concat "mouse-2: Go to node "
8a7757f6
JL
3824 (buffer-substring nbeg nend)))
3825 ;; Always set up the text property keymap.
3826 ;; It will either be used in the buffer
3827 ;; or copied in the header line.
99315976
JL
3828 (put-text-property
3829 tbeg nend 'keymap
3830 (cond
3831 ((string-equal (downcase tag) "prev") Info-prev-link-keymap)
3832 ((string-equal (downcase tag) "next") Info-next-link-keymap)
3833 ((string-equal (downcase tag) "up" ) Info-up-link-keymap))))))
fdeb777a
SM
3834
3835 (when (> Info-breadcrumbs-depth 0)
3836 (Info-insert-breadcrumbs))
3837
3838 ;; Treat header line.
8a7757f6
JL
3839 (when Info-use-header-line
3840 (goto-char (point-min))
de22f49c
SM
3841 (let* ((header-end (line-end-position))
3842 (header
3843 ;; If we find neither Next: nor Prev: link, show the entire
3844 ;; node header. Otherwise, don't show the File: and Node:
3845 ;; parts, to avoid wasting precious space on information that
3846 ;; is available in the mode line.
3847 (if (re-search-forward
3848 "\\(next\\|up\\|prev[ious]*\\): "
3849 header-end t)
3850 (progn
3851 (goto-char (match-beginning 1))
3852 (buffer-substring (point) header-end))
3853 (if (re-search-forward "node:[ \t]*[^ \t]+[ \t]*"
3854 header-end t)
8a7757f6 3855 (concat "No next, prev or up links -- "
de22f49c
SM
3856 (buffer-substring (point) header-end))
3857 (buffer-substring (point) header-end)))))
8a7757f6 3858 (put-text-property (point-min) (1+ (point-min))
69672704
JL
3859 'header-line
3860 (replace-regexp-in-string
3861 "%"
3862 ;; Preserve text properties on duplicated `%'.
3863 (lambda (s) (concat s s)) header))
8a7757f6
JL
3864 ;; Hide the part of the first line
3865 ;; that is in the header, if it is just part.
fdeb777a
SM
3866 (cond
3867 ((> Info-breadcrumbs-depth 0)
3868 (put-text-property (point-min) (1+ header-end) 'invisible t))
3869 ((not (bobp))
8a7757f6
JL
3870 ;; Hide the punctuation at the end, too.
3871 (skip-chars-backward " \t,")
fdeb777a 3872 (put-text-property (point) header-end 'invisible t))))))
8a7757f6
JL
3873
3874 ;; Fontify titles
3875 (goto-char (point-min))
de22f49c
SM
3876 (when (and font-lock-mode not-fontified-p)
3877 (while (and (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*\\*+\\|==+\\|--+\\|\\.\\.+\\)$"
3878 nil t)
3879 ;; Only consider it as an underlined title if the ASCII
3880 ;; underline has the same size as the text. A typical
3881 ;; counter example is when a continuation "..." is alone
3882 ;; on a line.
4dacf0ff
JL
3883 (= (string-width (match-string 1))
3884 (string-width (match-string 2))))
8a7757f6
JL
3885 (let* ((c (preceding-char))
3886 (face
a335c06e
MB
3887 (cond ((= c ?*) 'info-title-1)
3888 ((= c ?=) 'info-title-2)
3889 ((= c ?-) 'info-title-3)
3890 (t 'info-title-4))))
8a7757f6
JL
3891 (put-text-property (match-beginning 1) (match-end 1)
3892 'font-lock-face face))
3893 ;; This is a serious problem for trying to handle multiple
3894 ;; frame types at once. We want this text to be invisible
3895 ;; on frames that can display the font above.
edfda783 3896 (when (memq (framep (selected-frame)) '(x pc w32 mac ns))
8a7757f6
JL
3897 (add-text-properties (1- (match-beginning 2)) (match-end 2)
3898 '(invisible t front-sticky nil rear-nonsticky t)))))
3899
3900 ;; Fontify cross references
3901 (goto-char (point-min))
3902 (when (or not-fontified-p fontify-visited-p)
3903 (while (re-search-forward "\\(\\*Note[ \n\t]+\\)\\([^:]*\\)\\(:[ \t]*\\([^.,:(]*\\)\\(\\(([^)]*)\\)[^.,:]*\\)?[,:]?\n?\\)" nil t)
3904 (let ((start (match-beginning 0))
3905 (next (point))
3906 other-tag)
3907 (when not-fontified-p
3908 (when Info-hide-note-references
fdeb777a
SM
3909 (when (and (not (eq Info-hide-note-references 'hide))
3910 (> (line-number-at-pos) 4)) ; Skip breadcrumbs
fc11ddc8
JL
3911 ;; *Note is often used where *note should have been
3912 (goto-char start)
3913 (skip-syntax-backward " ")
d75baf12
RS
3914 (when (memq (char-before) '(?\( ?\[ ?\{))
3915 ;; Check whether the paren is preceded by
3916 ;; an end of sentence
3917 (skip-syntax-backward " ("))
fc11ddc8 3918 (setq other-tag
e8e6694b
RS
3919 (cond ((save-match-data (looking-back "\\<see"))
3920 "")
dc7ef9f0
CY
3921 ((save-match-data (looking-back "\\<in"))
3922 "")
e8e6694b 3923 ((memq (char-before) '(nil ?\. ?! ??))
fc11ddc8 3924 "See ")
d75baf12
RS
3925 ((save-match-data
3926 (save-excursion
3927 (search-forward "\n\n" start t)))
3928 "See ")
3929 (t "see "))))
8a7757f6
JL
3930 (goto-char next)
3931 (add-text-properties
3932 (match-beginning 1)
3933 (or (save-match-data
3934 ;; Don't hide \n after *Note
3935 (let ((start1 (match-beginning 1)))
3936 (if (string-match "\n" (match-string 1))
3937 (+ start1 (match-beginning 0)))))
3938 (match-end 1))
fc11ddc8 3939 (if other-tag
8a7757f6
JL
3940 `(display ,other-tag front-sticky nil rear-nonsticky t)
3941 '(invisible t front-sticky nil rear-nonsticky t))))
3942 (add-text-properties
3943 (match-beginning 2) (match-end 2)
3944 (list
3945 'help-echo (if (or (match-end 5)
3946 (not (equal (match-string 4) "")))
3947 (concat "mouse-2: go to " (or (match-string 5)
3948 (match-string 4)))
3949 "mouse-2: go to this node")
3950 'mouse-face 'highlight)))
3951 (when (or not-fontified-p fontify-visited-p)
e14105f6
TTN
3952 (setq rbeg (match-beginning 2)
3953 rend (match-end 2))
3954 (put-text-property
3955 rbeg rend
3956 'font-lock-face
3957 ;; Display visited nodes in a different face
3958 (if (and Info-fontify-visited-nodes
3959 (save-match-data
3960 (let* ((node (replace-regexp-in-string
3961 "^[ \t]+" ""
3962 (replace-regexp-in-string
3963 "[ \t\n]+" " "
bec275ed 3964 (or (match-string-no-properties 5)
e14105f6 3965 (and (not (equal (match-string 4) ""))
bec275ed
JL
3966 (match-string-no-properties 4))
3967 (match-string-no-properties 2)))))
d8fbba22
CY
3968 (external-link-p
3969 (string-match "(\\([^)]+\\))\\([^)]*\\)" node))
3970 (file (if external-link-p
3971 (file-name-nondirectory
bec275ed 3972 (match-string-no-properties 1 node))
d8fbba22 3973 Info-current-file))
e14105f6
TTN
3974 (hl Info-history-list)
3975 res)
d8fbba22
CY
3976 (if external-link-p
3977 (setq node (if (equal (match-string 2 node) "")
e14105f6 3978 "Top"
bec275ed 3979 (match-string-no-properties 2 node))))
d590dbd2
CY
3980 (while hl
3981 (if (and (string-equal node (nth 1 (car hl)))
bec275ed
JL
3982 (equal file
3983 (if (and external-link-p
3984 (stringp (caar hl)))
3985 (file-name-nondirectory
3986 (caar hl))
3987 (caar hl))))
d590dbd2
CY
3988 (setq res (car hl) hl nil)
3989 (setq hl (cdr hl))))
e14105f6
TTN
3990 res))) 'info-xref-visited 'info-xref))
3991 ;; For multiline ref, unfontify newline and surrounding whitespace
3992 (save-excursion
3993 (goto-char rbeg)
3994 (save-match-data
3995 (while (re-search-forward "\\s-*\n\\s-*" rend t nil)
3996 (remove-text-properties (match-beginning 0)
3997 (match-end 0)
3998 '(font-lock-face t))))))
8a7757f6
JL
3999 (when not-fontified-p
4000 (when (memq Info-hide-note-references '(t hide))
4001 (add-text-properties (match-beginning 3) (match-end 3)
4002 '(invisible t front-sticky nil rear-nonsticky t))
4003 ;; Unhide the file name of the external reference in parens
89b064a9 4004 (if (and (match-string 6) (not (eq Info-hide-note-references 'hide)))
8a7757f6
JL
4005 (remove-text-properties (match-beginning 6) (match-end 6)
4006 '(invisible t front-sticky nil rear-nonsticky t)))
4007 ;; Unhide newline because hidden newlines cause too long lines
4008 (save-match-data
89b064a9
JL
4009 (let ((beg3 (match-beginning 3))
4010 (end3 (match-end 3)))
4011 (if (and (string-match "\n[ \t]*" (match-string 3))
4012 (not (save-match-data
4013 (save-excursion
4014 (goto-char (1+ end3))
4015 (looking-at "[.)]*$")))))
4016 (remove-text-properties (+ beg3 (match-beginning 0))
4017 (+ beg3 (match-end 0))
8a7757f6
JL
4018 '(invisible t front-sticky nil rear-nonsticky t))))))
4019 (when (and Info-refill-paragraphs Info-hide-note-references)
4020 (push (set-marker (make-marker) start)
4021 paragraph-markers))))))
4022
4023 ;; Refill paragraphs (experimental feature)
4024 (when (and not-fontified-p
4025 Info-refill-paragraphs
4026 paragraph-markers)
4027 (let ((fill-nobreak-invisible t)
4028 (fill-individual-varying-indent nil)
4029 (paragraph-start "\f\\|[ \t]*[-*]\\|[ \t]*$")
4030 (paragraph-separate ".*\\.[ \t]*\n[ \t]\\|[ \t]*[-*]\\|[ \t\f]*$")
4031 (adaptive-fill-mode nil))
4032 (goto-char (point-max))
a73db543
SM
4033 (dolist (m paragraph-markers)
4034 (when (< m (point))
4035 (goto-char m)
4036 (beginning-of-line)
4037 (let ((beg (point)))
4038 (when (zerop (forward-paragraph))
4039 (fill-individual-paragraphs beg (point) nil nil)
4040 (goto-char beg))))
4041 (set-marker m nil))))
8a7757f6
JL
4042
4043 ;; Fontify menu items
4044 (goto-char (point-min))
4045 (when (and (or not-fontified-p fontify-visited-p)
4046 (search-forward "\n* Menu:" nil t)
8a7757f6 4047 ;; Don't take time to annotate huge menus
3d0fc0b7 4048 Info-fontify-maximum-menu-size
8a7757f6
JL
4049 (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
4050 (let ((n 0)
4051 cont)
4052 (while (re-search-forward
1ecc5922
RS
4053 (concat "^\\* Menu:\\|\\(?:^\\* +\\(" Info-menu-entry-name-re "\\)\\(:"
4054 Info-node-spec-re "\\([ \t]*\\)\\)\\)")
8a7757f6 4055 nil t)
1ecc5922
RS
4056 (when (match-beginning 1)
4057 (when not-fontified-p
4058 (setq n (1+ n))
4059 (if (and (<= n 9) (zerop (% n 3))) ; visual aids to help with 1-9 keys
4060 (put-text-property (match-beginning 0)
4061 (1+ (match-beginning 0))
185cff95 4062 'font-lock-face 'info-menu-star)))
1ecc5922
RS
4063 (when not-fontified-p
4064 (add-text-properties
4065 (match-beginning 1) (match-end 1)
4066 (list
4067 'help-echo (if (and (match-end 3)
4068 (not (equal (match-string 3) "")))
4069 (concat "mouse-2: go to " (match-string 3))
4070 "mouse-2: go to this node")
4071 'mouse-face 'highlight)))
4072 (when (or not-fontified-p fontify-visited-p)
a73db543 4073 (put-text-property
1ecc5922 4074 (match-beginning 1) (match-end 1)
a73db543
SM
4075 'font-lock-face
4076 ;; Display visited menu items in a different face
4077 (if (and Info-fontify-visited-nodes
4078 (save-match-data
d8fbba22 4079 (let* ((node (if (equal (match-string 3) "")
bec275ed
JL
4080 (match-string-no-properties 1)
4081 (match-string-no-properties 3)))
d8fbba22
CY
4082 (external-link-p
4083 (string-match "(\\([^)]+\\))\\([^)]*\\)" node))
4084 (file (if external-link-p
4085 (file-name-nondirectory
bec275ed 4086 (match-string-no-properties 1 node))
d8fbba22
CY
4087 Info-current-file))
4088 (hl Info-history-list)
4089 res)
4090 (if external-link-p
4091 (setq node (if (equal (match-string 2 node) "")
a73db543 4092 "Top"
bec275ed 4093 (match-string-no-properties 2 node))))
d590dbd2
CY
4094 (while hl
4095 (if (and (string-equal node (nth 1 (car hl)))
bec275ed
JL
4096 (equal file
4097 (if (and external-link-p
4098 (stringp (caar hl)))
4099 (file-name-nondirectory
4100 (caar hl))
4101 (caar hl))))
d590dbd2
CY
4102 (setq res (car hl) hl nil)
4103 (setq hl (cdr hl))))
a73db543 4104 res))) 'info-xref-visited 'info-xref)))
0f218170
JL
4105 (when (and not-fontified-p
4106 (memq Info-hide-note-references '(t hide))
4107 (not (Info-index-node)))
1ecc5922
RS
4108 (put-text-property (match-beginning 2) (1- (match-end 6))
4109 'invisible t)
4110 ;; Unhide the file name in parens
4111 (if (and (match-end 4) (not (eq (char-after (match-end 4)) ?.)))
4112 (remove-text-properties (match-beginning 4) (match-end 4)
4113 '(invisible t)))
4114 ;; We need a stretchable space like :align-to but with
4115 ;; a minimum value.
4116 (put-text-property (1- (match-end 6)) (match-end 6) 'display
4117 (if (>= 22 (- (match-end 1)
4118 (match-beginning 0)))
4119 '(space :align-to 24)
4120 '(space :width 2)))
4121 (setq cont (looking-at "."))
4122 (while (and (= (forward-line 1) 0)
4123 (looking-at "\\([ \t]+\\)[^*\n]"))
4124 (put-text-property (match-beginning 1) (1- (match-end 1))
4125 'invisible t)
4126 (put-text-property (1- (match-end 1)) (match-end 1)
4127 'display
4128 (if cont
4129 '(space :align-to 26)
4130 '(space :align-to 24)))
4131 (setq cont t)))))))
8a7757f6
JL
4132
4133 ;; Fontify menu headers
4134 ;; Add the face `info-menu-header' to any header before a menu entry
4135 (goto-char (point-min))
4136 (when (and not-fontified-p (re-search-forward "^\\* Menu:" nil t))
4137 (put-text-property (match-beginning 0) (match-end 0)
4138 'font-lock-face 'info-menu-header)
4139 (while (re-search-forward "\n\n\\([^*\n ].*\\)\n\n?[*]" nil t)
4140 (put-text-property (match-beginning 1) (match-end 1)
4141 'font-lock-face 'info-menu-header)))
4142
836bb478
JL
4143 ;; Hide index line numbers
4144 (goto-char (point-min))
141b49f5 4145 (when (and not-fontified-p (Info-index-node))
836bb478
JL
4146 (while (re-search-forward "[ \t\n]*(line +[0-9]+)" nil t)
4147 (put-text-property (match-beginning 0) (match-end 0)
4148 'invisible t)))
4149
8a7757f6
JL
4150 ;; Fontify http and ftp references
4151 (goto-char (point-min))
4152 (when not-fontified-p
68b77b64
GM
4153 (while (re-search-forward "\\(https?\\|ftp\\)://[^ \t\n\"`({<>})']+"
4154 nil t)
8a7757f6
JL
4155 (add-text-properties (match-beginning 0) (match-end 0)
4156 '(font-lock-face info-xref
fdeb777a
SM
4157 mouse-face highlight
4158 help-echo "mouse-2: go to this URL"))))
8a7757f6
JL
4159
4160 (set-buffer-modified-p nil))))
c5fe2ff1 4161\f
3cb6768f
EL
4162;;; Speedbar support:
4163;; These functions permit speedbar to display the "tags" in the
185cff95 4164;; current Info node.
96ee3f29 4165(eval-when-compile (require 'speedbar))
3cb6768f 4166
96ee3f29
KH
4167(defvar Info-speedbar-key-map nil
4168 "Keymap used when in the info display mode.")
3cb6768f 4169
96ee3f29
KH
4170(defun Info-install-speedbar-variables ()
4171 "Install those variables used by speedbar to enhance Info."
4172 (if Info-speedbar-key-map
4173 nil
4174 (setq Info-speedbar-key-map (speedbar-make-specialized-keymap))
4175
4176 ;; Basic tree features
4177 (define-key Info-speedbar-key-map "e" 'speedbar-edit-line)
4178 (define-key Info-speedbar-key-map "\C-m" 'speedbar-edit-line)
4179 (define-key Info-speedbar-key-map "+" 'speedbar-expand-line)
4180 (define-key Info-speedbar-key-map "-" 'speedbar-contract-line)
4181 )
4182
4183 (speedbar-add-expansion-list '("Info" Info-speedbar-menu-items
4184 Info-speedbar-key-map
4185 Info-speedbar-hierarchy-buttons)))
3cb6768f
EL
4186
4187(defvar Info-speedbar-menu-items
96ee3f29
KH
4188 '(["Browse Node" speedbar-edit-line t]
4189 ["Expand Node" speedbar-expand-line
4190 (save-excursion (beginning-of-line)
4191 (looking-at "[0-9]+: *.\\+. "))]
4192 ["Contract Node" speedbar-contract-line
4193 (save-excursion (beginning-of-line)
4194 (looking-at "[0-9]+: *.-. "))]
4195 )
3cb6768f
EL
4196 "Additional menu-items to add to speedbar frame.")
4197
96ee3f29
KH
4198;; Make sure our special speedbar major mode is loaded
4199(if (featurep 'speedbar)
4200 (Info-install-speedbar-variables)
4201 (add-hook 'speedbar-load-hook 'Info-install-speedbar-variables))
4202
4203;;; Info hierarchy display method
4204;;;###autoload
4205(defun Info-speedbar-browser ()
185cff95 4206 "Initialize speedbar to display an Info node browser.
96ee3f29
KH
4207This will add a speedbar major display mode."
4208 (interactive)
4209 (require 'speedbar)
4210 ;; Make sure that speedbar is active
4211 (speedbar-frame-mode 1)
4212 ;; Now, throw us into Info mode on speedbar.
4213 (speedbar-change-initial-expansion-list "Info")
4214 )
4215
4216(defun Info-speedbar-hierarchy-buttons (directory depth &optional node)
4217 "Display an Info directory hierarchy in speedbar.
4218DIRECTORY is the current directory in the attached frame.
4219DEPTH is the current indentation depth.
4220NODE is an optional argument that is used to represent the
4221specific node to expand."
4222 (if (and (not node)
4223 (save-excursion (goto-char (point-min))
1bcedb3b
RS
4224 (let ((case-fold-search t))
4225 (looking-at "Info Nodes:"))))
96ee3f29
KH
4226 ;; Update our "current node" maybe?
4227 nil
4228 ;; We cannot use the generic list code, that depends on all leaves
4229 ;; being known at creation time.
4230 (if (not node)
4231 (speedbar-with-writable (insert "Info Nodes:\n")))
24bbdbef
CY
4232 (let ((completions nil))
4233 (speedbar-select-attached-frame)
5fdc7997
EL
4234 (save-window-excursion
4235 (setq completions
4236 (Info-speedbar-fetch-file-nodes (or node '"(dir)top"))))
222a6c9b 4237 (select-frame (speedbar-current-frame))
96ee3f29
KH
4238 (if completions
4239 (speedbar-with-writable
dde7f979 4240 (dolist (completion completions)
96ee3f29 4241 (speedbar-make-tag-line 'bracket ?+ 'Info-speedbar-expand-node
dde7f979
SM
4242 (cdr completion)
4243 (car completion)
96ee3f29 4244 'Info-speedbar-goto-node
dde7f979
SM
4245 (cdr completion)
4246 'info-xref depth))
96ee3f29
KH
4247 t)
4248 nil))))
399c88ad 4249
96ee3f29 4250(defun Info-speedbar-goto-node (text node indent)
d1268e52 4251 "When user clicks on TEXT, go to an info NODE.
96ee3f29 4252The INDENT level is ignored."
24bbdbef 4253 (speedbar-select-attached-frame)
760d5cb3
GM
4254 (let* ((buff (or (get-buffer "*info*")
4255 (progn (info) (get-buffer "*info*"))))
4256 (bwin (get-buffer-window buff 0)))
4257 (if bwin
4258 (progn
4259 (select-window bwin)
4260 (raise-frame (window-frame bwin)))
4261 (if speedbar-power-click
4262 (let ((pop-up-frames t)) (select-window (display-buffer buff)))
24bbdbef 4263 (speedbar-select-attached-frame)
760d5cb3 4264 (switch-to-buffer buff)))
dde7f979
SM
4265 (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" node))
4266 (error "Invalid node %s" node)
4267 (Info-find-node (match-string 1 node) (match-string 2 node))
760d5cb3
GM
4268 ;; If we do a find-node, and we were in info mode, restore
4269 ;; the old default method. Once we are in info mode, it makes
4270 ;; sense to return to whatever method the user was using before.
4271 (if (string= speedbar-initial-expansion-list-name "Info")
4272 (speedbar-change-initial-expansion-list
4273 speedbar-previously-used-expansion-list-name)))))
96ee3f29
KH
4274
4275(defun Info-speedbar-expand-node (text token indent)
4276 "Expand the node the user clicked on.
4277TEXT is the text of the button we clicked on, a + or - item.
4278TOKEN is data related to this node (NAME . FILE).
4279INDENT is the current indentation depth."
4280 (cond ((string-match "+" text) ;we have to expand this file
4281 (speedbar-change-expand-button-char ?-)
4282 (if (speedbar-with-writable
760d5cb3
GM
4283 (save-excursion
4284 (end-of-line) (forward-char 1)
4285 (Info-speedbar-hierarchy-buttons nil (1+ indent) token)))
96ee3f29
KH
4286 (speedbar-change-expand-button-char ?-)
4287 (speedbar-change-expand-button-char ??)))
4288 ((string-match "-" text) ;we have to contract this node
4289 (speedbar-change-expand-button-char ?+)
4290 (speedbar-delete-subblock indent))
4291 (t (error "Ooops... not sure what to do")))
4292 (speedbar-center-buffer-smartly))
4293
4294(defun Info-speedbar-fetch-file-nodes (nodespec)
4295 "Fetch the subnodes from the info NODESPEC.
db90d225 4296NODESPEC is a string of the form: (file)node."
e8dab975
SM
4297 ;; Set up a buffer we can use to fake-out Info.
4298 (with-current-buffer (get-buffer-create " *info-browse-tmp*")
96ee3f29
KH
4299 (if (not (equal major-mode 'Info-mode))
4300 (Info-mode))
4301 ;; Get the node into this buffer
dde7f979
SM
4302 (if (not (string-match "^(\\([^)]+\\))\\([^.]+\\)$" nodespec))
4303 (error "Invalid node specification %s" nodespec)
4304 (Info-find-node (match-string 1 nodespec) (match-string 2 nodespec)))
96ee3f29
KH
4305 ;; Scan the created buffer
4306 (goto-char (point-min))
4307 (let ((completions nil)
1bcedb3b 4308 (case-fold-search t)
96ee3f29
KH
4309 (thisfile (progn (string-match "^(\\([^)]+\\))" nodespec)
4310 (match-string 1 nodespec))))
4311 ;; Always skip the first one...
4312 (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
4313 (while (re-search-forward "\n\\* \\([^:\t\n]*\\):" nil t)
4314 (let ((name (match-string 1)))
dde7f979
SM
4315 (push (cons name
4316 (if (looking-at " *\\(([^)]+)[^.\n]+\\)\\.")
4317 (match-string 1)
4318 (if (looking-at " *\\(([^)]+)\\)\\.")
4319 (concat (match-string 1) "Top")
4320 (concat "(" thisfile ")"
4321 (if (looking-at " \\([^.]+\\).")
4322 (match-string 1)
4323 name)))))
4324 completions)))
96ee3f29
KH
4325 (nreverse completions))))
4326
4327;;; Info mode node listing
24bbdbef 4328;; This is called by `speedbar-add-localized-speedbar-support'
3cb6768f
EL
4329(defun Info-speedbar-buttons (buffer)
4330 "Create a speedbar display to help navigation in an Info file.
4331BUFFER is the buffer speedbar is requesting buttons for."
96ee3f29 4332 (if (save-excursion (goto-char (point-min))
1bcedb3b
RS
4333 (let ((case-fold-search t))
4334 (not (looking-at "Info Nodes:"))))
96ee3f29 4335 (erase-buffer))
24bbdbef 4336 (Info-speedbar-hierarchy-buttons nil 0))
49116ac0 4337
6270bbe7
JL
4338(dolist (mess '("^First node in file$"
4339 "^No `.*' in index$"
4340 "^No cross-reference named"
4341 "^No cross.references in this node$"
185cff95 4342 "^No current Info node$"
48e5b471 4343 "^No menu in this node$"
6270bbe7
JL
4344 "^No more items in menu$"
4345 "^No more nodes$"
4346 "^No pointer \\(?:forward\\|backward\\) from this node$"
4347 "^No previous `i' command$"
4348 "^No previous items in menu$"
4349 "^No previous nodes$"
4350 "^No such item in menu$"
4351 "^No such node or anchor"
4352 "^Node has no"
4353 "^Point neither on reference nor in menu item description$"
4354 "^This is the \\(?:first\\|last\\) Info node you looked at$"
4355 search-failed))
48e5b471
DL
4356 (add-to-list 'debug-ignored-errors mess))
4357
f3a45db0
LH
4358;;;; Desktop support
4359
4360(defun Info-desktop-buffer-misc-data (desktop-dirname)
4361 "Auxiliary information to be saved in desktop file."
bec275ed
JL
4362 (unless (member Info-current-file '(apropos history toc nil))
4363 (list Info-current-file Info-current-node)))
f3a45db0 4364
f3a45db0
LH
4365(defun Info-restore-desktop-buffer (desktop-buffer-file-name
4366 desktop-buffer-name
4367 desktop-buffer-misc)
185cff95 4368 "Restore an Info buffer specified in a desktop file."
f3a45db0
LH
4369 (let ((first (nth 0 desktop-buffer-misc))
4370 (second (nth 1 desktop-buffer-misc)))
4371 (when (and first second)
c9ce1e40
JL
4372 (when desktop-buffer-name
4373 (set-buffer (get-buffer-create desktop-buffer-name))
4374 (Info-mode))
2a3f604d 4375 (Info-find-node first second)
f3a45db0
LH
4376 (current-buffer))))
4377
9e7357b0
AS
4378(add-to-list 'desktop-buffer-mode-handlers
4379 '(Info-mode . Info-restore-desktop-buffer))
80123c9e 4380
23ceed9a 4381;;;; Bookmark support
43f8b275
SM
4382(declare-function bookmark-make-record-default "bookmark" (&optional pos-only))
4383(declare-function bookmark-prop-get "bookmark" (bookmark prop))
4384(declare-function bookmark-default-handler "bookmark" (bmk))
4385(declare-function bookmark-get-bookmark-record "bookmark" (bmk))
e1af69b4 4386
32a091dd 4387(defun Info-bookmark-make-record ()
136894c8 4388 `(,Info-current-node
43f8b275
SM
4389 ,@(bookmark-make-record-default 'point-only)
4390 (filename . ,Info-current-file)
32a091dd
SM
4391 (info-node . ,Info-current-node)
4392 (handler . Info-bookmark-jump)))
23ceed9a
TH
4393
4394;;;###autoload
4395(defun Info-bookmark-jump (bmk)
4396 ;; This implements the `handler' function interface for record type returned
e0385bf4 4397 ;; by `Info-bookmark-make-record', which see.
43f8b275
SM
4398 (let* ((file (bookmark-prop-get bmk 'filename))
4399 (info-node (bookmark-prop-get bmk 'info-node))
4400 (buf (save-window-excursion ;FIXME: doesn't work with frames!
4401 (Info-find-node file info-node) (current-buffer))))
4402 ;; Use bookmark-default-handler to move to the appropriate location
4403 ;; within the node.
4404 (bookmark-default-handler
4405 (list* "" `(buffer . ,buf) (bookmark-get-bookmark-record bmk)))))
23ceed9a 4406
49116ac0
JB
4407(provide 'info)
4408
83040cb0 4409;; arch-tag: f2480fe2-2139-40c1-a49b-6314991164ac
1a06eabd 4410;;; info.el ends here