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