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