info-look.el 35.3 KB
Newer Older
1
;;; info-look.el --- major-mode-sensitive Info index lookup facility
Richard M. Stallman's avatar
Richard M. Stallman committed
2 3
;; An older version of this was known as libc.el.

4
;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003,
Glenn Morris's avatar
Glenn Morris committed
5
;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
6

Gerd Moellmann's avatar
Gerd Moellmann committed
7
;; Author: Ralph Schleicher <rs@nunatak.allgaeu.org>
8
;;         (did not show signs of life (Nov 2001)  -stef)
Richard M. Stallman's avatar
Richard M. Stallman committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
;; Keywords: help languages

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
25 26
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
27

28 29 30
;;; Commentary:

;; Really cool code to lookup info indexes.
31
;; Try especially info-lookup-symbol (aka C-h S).
32

Richard M. Stallman's avatar
Richard M. Stallman committed
33 34 35
;;; Code:

(require 'info)
36 37 38 39

(defgroup info-lookup nil
  "Major mode sensitive help agent."
  :group 'help :group 'languages)
Richard M. Stallman's avatar
Richard M. Stallman committed
40 41

(defvar info-lookup-mode nil
42 43
  "Symbol of the current buffer's help mode.
Help is provided according to the buffer's major mode if value is nil.
Richard M. Stallman's avatar
Richard M. Stallman committed
44 45 46
Automatically becomes buffer local when set in any fashion.")
(make-variable-buffer-local 'info-lookup-mode)

47 48 49
(defcustom info-lookup-other-window-flag t
  "Non-nil means pop up the Info buffer in another window."
  :group 'info-lookup :type 'boolean)
Richard M. Stallman's avatar
Richard M. Stallman committed
50

51
(defcustom info-lookup-highlight-face 'match
52 53 54
  "Face for highlighting looked up help items.
Setting this variable to nil disables highlighting."
  :group 'info-lookup :type 'face)
Richard M. Stallman's avatar
Richard M. Stallman committed
55 56 57 58

(defvar info-lookup-highlight-overlay nil
  "Overlay object used for highlighting.")

Karl Heuer's avatar
Karl Heuer committed
59
(defcustom info-lookup-file-name-alist
60
  '(("\\`ac\\(local\\|site\\|include\\)\\.m4\\'" . autoconf-mode))
Karl Heuer's avatar
Karl Heuer committed
61 62 63 64 65 66 67 68 69 70
  "Alist of file names handled specially.
List elements are cons cells of the form

    (REGEXP . MODE)

If a file name matches REGEXP, then use help mode MODE instead of the
buffer's major mode."
  :group 'info-lookup :type '(repeat (cons (string :tag "Regexp")
					   (symbol :tag "Mode"))))

Richard M. Stallman's avatar
Richard M. Stallman committed
71 72 73
(defvar info-lookup-history nil
  "History of previous input lines.")

74 75
(defvar info-lookup-alist nil
  "Alist of known help topics.
Richard M. Stallman's avatar
Richard M. Stallman committed
76 77
Cons cells are of the form

78
    (HELP-TOPIC . HELP-DATA)
Richard M. Stallman's avatar
Richard M. Stallman committed
79 80

HELP-TOPIC is the symbol of a help topic.
81
HELP-DATA is a HELP-TOPIC's public data set.
Richard M. Stallman's avatar
Richard M. Stallman committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
 Value is an alist with elements of the form

    (HELP-MODE REGEXP IGNORE-CASE DOC-SPEC PARSE-RULE OTHER-MODES)

HELP-MODE is a mode's symbol.
REGEXP is a regular expression matching those help items whose
 documentation can be looked up via DOC-SPEC.
IGNORE-CASE is non-nil if help items are case insensitive.
DOC-SPEC is a list of documentation specifications of the form

    (INFO-NODE TRANS-FUNC PREFIX SUFFIX)

INFO-NODE is the name (including file name part) of an Info index.
TRANS-FUNC is a function translating index entries into help items;
 nil means add only those index entries matching REGEXP, a string
 means prepend string to the first word of all index entries.
PREFIX and SUFFIX are parts of a regular expression.  If one of
 them is non-nil then search the help item's Info node for the
 first occurrence of the regular expression `PREFIX ITEM SUFFIX'.
 ITEM will be highlighted with `info-lookup-highlight-face' if this
 variable is not nil.
PARSE-RULE is either the symbol name of a function or a regular
 expression for guessing the default help item at point.  Fuzzy
 regular expressions like \"[_a-zA-Z0-9]+\" do a better job if
 there are no clear delimiters; do not try to write too complex
 expressions.  PARSE-RULE defaults to REGEXP.
OTHER-MODES is a list of cross references to other help modes.")

(defsubst info-lookup->topic-value (topic)
111
  (cdr (assoc topic info-lookup-alist)))
Richard M. Stallman's avatar
Richard M. Stallman committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

(defsubst info-lookup->mode-value (topic mode)
  (assoc mode (info-lookup->topic-value topic)))

(defsubst info-lookup->regexp (topic mode)
  (nth 1 (info-lookup->mode-value topic mode)))

(defsubst info-lookup->ignore-case (topic mode)
  (nth 2 (info-lookup->mode-value topic mode)))

(defsubst info-lookup->doc-spec (topic mode)
  (nth 3 (info-lookup->mode-value topic mode)))

(defsubst info-lookup->parse-rule (topic mode)
  (nth 4 (info-lookup->mode-value topic mode)))

(defsubst info-lookup->other-modes (topic mode)
  (nth 5 (info-lookup->mode-value topic mode)))

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
(defun info-lookup-add-help (&rest arg)
  "Add or update a help specification.
Function arguments are one or more options of the form

    KEYWORD ARGUMENT

KEYWORD is either `:topic', `:mode', `:regexp', `:ignore-case',
 `:doc-spec', `:parse-rule', or `:other-modes'.
ARGUMENT has a value as explained in the documentation of the
 variable `info-lookup-alist'.

If no topic or mode option has been specified, then the help topic defaults
to `symbol', and the help mode defaults to the current major mode."
  (apply 'info-lookup-add-help* nil arg))

(defun info-lookup-maybe-add-help (&rest arg)
147
  "Add a help specification iff none is defined.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
See the documentation of the function `info-lookup-add-help'
for more details."
  (apply 'info-lookup-add-help* t arg))

(defun info-lookup-add-help* (maybe &rest arg)
  (let (topic mode regexp ignore-case doc-spec
	      parse-rule other-modes keyword value)
    (setq topic 'symbol
	  mode major-mode
	  regexp "\\w+")
    (while arg
      (setq keyword (car arg))
      (or (symbolp keyword)
	  (error "Junk in argument list \"%S\"" arg))
      (setq arg (cdr arg))
      (and (null arg)
	   (error "Keyword \"%S\" is missing an argument" keyword))
      (setq value (car arg)
	    arg (cdr arg))
      (cond ((eq keyword :topic)
	     (setq topic value))
	    ((eq keyword :mode)
	     (setq mode value))
	    ((eq keyword :regexp)
	     (setq regexp value))
	    ((eq keyword :ignore-case)
	     (setq ignore-case value))
	    ((eq keyword :doc-spec)
	     (setq doc-spec value))
	    ((eq keyword :parse-rule)
	     (setq parse-rule value))
	    ((eq keyword :other-modes)
	     (setq other-modes value))
	    (t
	     (error "Unknown keyword \"%S\"" keyword))))
    (or (and maybe (info-lookup->mode-value topic mode))
	(let* ((data (list regexp ignore-case doc-spec parse-rule other-modes))
	       (topic-cell (or (assoc topic info-lookup-alist)
			       (car (setq info-lookup-alist
					  (cons (cons topic nil)
						info-lookup-alist)))))
	       (mode-cell (assoc mode topic-cell)))
	  (if (null mode-cell)
	      (setcdr topic-cell (cons (cons mode data) (cdr topic-cell)))
	    (setcdr mode-cell data))))
    nil))

Richard M. Stallman's avatar
Richard M. Stallman committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
(defvar info-lookup-cache nil
  "Cache storing data maintained automatically by the program.
Value is an alist with cons cell of the form

    (HELP-TOPIC . ((HELP-MODE INITIALIZED COMPLETIONS REFER-MODES) ...))

HELP-TOPIC is the symbol of a help topic.
HELP-MODE is a mode's symbol.
INITIALIZED is nil if HELP-MODE is uninitialized, t if
 HELP-MODE is initialized, and `0' means HELP-MODE is
 initialized but void.
COMPLETIONS is an alist of documented help items.
REFER-MODES is a list of other help modes to use.")

(defsubst info-lookup->cache (topic)
  (or (assoc topic info-lookup-cache)
      (car (setq info-lookup-cache
		 (cons (cons topic nil)
		       info-lookup-cache)))))

215
(defun info-lookup->topic-cache (topic)
Richard M. Stallman's avatar
Richard M. Stallman committed
216 217
  (cdr (info-lookup->cache topic)))

218
(defun info-lookup->mode-cache (topic mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
219 220
  (assoc mode (info-lookup->topic-cache topic)))

221
(defun info-lookup->initialized (topic mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
222 223
  (nth 1 (info-lookup->mode-cache topic mode)))

224
(defun info-lookup->completions (topic mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
225 226 227 228
  (or (info-lookup->initialized topic mode)
      (info-lookup-setup-mode topic mode))
  (nth 2 (info-lookup->mode-cache topic mode)))

229
(defun info-lookup->refer-modes (topic mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
230 231 232 233
  (or (info-lookup->initialized topic mode)
      (info-lookup-setup-mode topic mode))
  (nth 3 (info-lookup->mode-cache topic mode)))

234
(defun info-lookup->all-modes (topic mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
235 236
  (cons mode (info-lookup->refer-modes topic mode)))

237 238 239
(defun info-lookup-quick-all-modes (topic mode)
  (cons mode (info-lookup->other-modes topic mode)))

Richard M. Stallman's avatar
Richard M. Stallman committed
240 241 242 243 244 245 246 247 248
;;;###autoload
(defun info-lookup-reset ()
  "Throw away all cached data.
This command is useful if the user wants to start at the beginning without
quitting Emacs, for example, after some Info documents were updated on the
system."
  (interactive)
  (setq info-lookup-cache nil))

249
;;;###autoload (put 'info-lookup-symbol 'info-file "emacs")
Richard M. Stallman's avatar
Richard M. Stallman committed
250 251
;;;###autoload
(defun info-lookup-symbol (symbol &optional mode)
252
  "Display the definition of SYMBOL, as found in the relevant manual.
253 254 255 256
When this command is called interactively, it reads SYMBOL from the
minibuffer.  In the minibuffer, use M-n to yank the default argument
value into the minibuffer so you can edit it.  The default symbol is the
one found at point.
257 258

With prefix arg a query for the symbol help mode is offered."
Richard M. Stallman's avatar
Richard M. Stallman committed
259
  (interactive
260
   (info-lookup-interactive-arguments 'symbol current-prefix-arg))
Richard M. Stallman's avatar
Richard M. Stallman committed
261 262
  (info-lookup 'symbol symbol mode))

263
;;;###autoload (put 'info-lookup-file 'info-file "emacs")
Richard M. Stallman's avatar
Richard M. Stallman committed
264 265 266
;;;###autoload
(defun info-lookup-file (file &optional mode)
  "Display the documentation of a file.
267 268 269
When this command is called interactively, it reads FILE from the minibuffer.
In the minibuffer, use M-n to yank the default file name
into the minibuffer so you can edit it.
270 271 272
The default file name is the one found at point.

With prefix arg a query for the file help mode is offered."
Richard M. Stallman's avatar
Richard M. Stallman committed
273
  (interactive
274
   (info-lookup-interactive-arguments 'file current-prefix-arg))
Richard M. Stallman's avatar
Richard M. Stallman committed
275 276
  (info-lookup 'file file mode))

277 278 279 280 281 282 283 284
(defun info-lookup-interactive-arguments (topic &optional query)
  "Read and return argument value (and help mode) for help topic TOPIC.
If optional argument QUERY is non-nil, query for the help mode."
  (let* ((mode (cond (query
		      (info-lookup-change-mode topic))
		     ((info-lookup->mode-value topic (info-lookup-select-mode))
		      info-lookup-mode)
		     ((info-lookup-change-mode topic))))
Richard M. Stallman's avatar
Richard M. Stallman committed
285 286 287 288 289
	 (completions (info-lookup->completions topic mode))
	 (default (info-lookup-guess-default topic mode))
	 (completion-ignore-case (info-lookup->ignore-case topic mode))
	 (enable-recursive-minibuffers t)
	 (value (completing-read
290
		 (if default
Richard M. Stallman's avatar
Richard M. Stallman committed
291 292
		     (format "Describe %s (default %s): " topic default)
		   (format "Describe %s: " topic))
293
		 completions nil nil nil 'info-lookup-history default)))
Richard M. Stallman's avatar
Richard M. Stallman committed
294 295
    (list (if (equal value "") default value) mode)))

Karl Heuer's avatar
Karl Heuer committed
296 297 298 299 300 301 302 303 304 305
(defun info-lookup-select-mode ()
  (when (and (not info-lookup-mode) (buffer-file-name))
    (let ((file-name (file-name-nondirectory (buffer-file-name)))
	  (file-name-alist info-lookup-file-name-alist))
      (while (and (not info-lookup-mode) file-name-alist)
	(when (string-match (caar file-name-alist) file-name)
	  (setq info-lookup-mode (cdar file-name-alist)))
	(setq file-name-alist (cdr file-name-alist)))))
  (or info-lookup-mode (setq info-lookup-mode major-mode)))

Richard M. Stallman's avatar
Richard M. Stallman committed
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
(defun info-lookup-change-mode (topic)
  (let* ((completions (mapcar (lambda (arg)
				(cons (symbol-name (car arg)) (car arg)))
			      (info-lookup->topic-value topic)))
	 (mode (completing-read
		(format "Use %s help mode: " topic)
		completions nil t nil 'info-lookup-history)))
    (or (setq mode (cdr (assoc mode completions)))
	(error "No %s help available" topic))
    (or (info-lookup->mode-value topic mode)
	(error "No %s help available for `%s'" topic mode))
    (setq info-lookup-mode mode)))

(defun info-lookup (topic item mode)
  "Display the documentation of a help item."
Karl Heuer's avatar
Karl Heuer committed
321
  (or mode (setq mode (info-lookup-select-mode)))
Richard M. Stallman's avatar
Richard M. Stallman committed
322 323
  (or (info-lookup->mode-value topic mode)
      (error "No %s help available for `%s'" topic mode))
324 325 326
  (let* ((completions (info-lookup->completions topic mode))
         (ignore-case (info-lookup->ignore-case topic mode))
         (entry (or (assoc (if ignore-case (downcase item) item) completions)
327
                    (assoc-string item completions t)
328 329 330 331
                    (error "Not documented as a %s: %s" topic (or item ""))))
         (modes (info-lookup->all-modes topic mode))
         (window (selected-window))
         found doc-spec node prefix suffix doc-found)
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    (if (not (eq major-mode 'Info-mode))
	(if (not info-lookup-other-window-flag)
	    (info)
	  (progn
	    (save-window-excursion (info))
	    ;; Determine whether or not the Info buffer is visible in
	    ;; another frame on the same display.  If it is, simply raise
	    ;; that frame.  Otherwise, display it in another window.
	    (let* ((window (get-buffer-window "*info*" t))
		   (info-frame (and window (window-frame window))))
	      (if (and info-frame
		       (display-multi-frame-p)
		       (memq info-frame (frames-on-display-list))
		       (not (eq info-frame (selected-frame))))
		  (select-frame info-frame)
		(switch-to-buffer-other-window "*info*"))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
348 349 350 351 352 353
    (while (and (not found) modes)
      (setq doc-spec (info-lookup->doc-spec topic (car modes)))
      (while (and (not found) doc-spec)
	(setq node (nth 0 (car doc-spec))
	      prefix (nth 2 (car doc-spec))
	      suffix (nth 3 (car doc-spec)))
354
	(when (condition-case error-data
Dave Love's avatar
Dave Love committed
355
		  (progn
356 357 358 359 360
		    ;; Don't need Index menu fontifications here, and
		    ;; they slow down the lookup.
		    (let (Info-fontify-maximum-menu-size)
		      (Info-goto-node node)
		      (setq doc-found t)))
Dave Love's avatar
Dave Love committed
361
		(error
362 363 364 365 366
		 (message "Cannot access Info node %s" node)
		 (sit-for 1)
		 nil))
	  (condition-case nil
	      (progn
367 368 369 370 371 372
                ;; Don't use Info-menu, it forces case-fold-search to t
                (let ((case-fold-search nil))
                  (re-search-forward
                   (concat "^\\* " (regexp-quote (or (cdr entry) (car entry)))
                           ":")))
                (Info-follow-nearest-node)
373 374 375 376 377 378 379
		(setq found t)
		(if (or prefix suffix)
		    (let ((case-fold-search
			   (info-lookup->ignore-case topic (car modes)))
			  (buffer-read-only nil))
		      (goto-char (point-min))
		      (re-search-forward
380
		       (concat prefix (regexp-quote (car entry)) suffix))
381
		      (goto-char (match-beginning 0))
382
		      (and (display-color-p) info-lookup-highlight-face
383
			   ;; Search again for ITEM so that the first
Richard M. Stallman's avatar
Richard M. Stallman committed
384
			   ;; occurrence of ITEM will be highlighted.
385
			   (re-search-forward (regexp-quote (car entry)))
386 387 388 389 390 391 392 393 394 395
			   (let ((start (match-beginning 0))
				 (end (match-end 0)))
			     (if (overlayp info-lookup-highlight-overlay)
				 (move-overlay info-lookup-highlight-overlay
					       start end (current-buffer))
			       (setq info-lookup-highlight-overlay
				     (make-overlay start end))))
			   (overlay-put info-lookup-highlight-overlay
					'face info-lookup-highlight-face)))))
	    (error nil)))
Richard M. Stallman's avatar
Richard M. Stallman committed
396 397
	(setq doc-spec (cdr doc-spec)))
      (setq modes (cdr modes)))
398 399 400 401
    ;; Alert the user if case was munged, and do this after bringing up the
    ;; info buffer since that can print messages
    (unless (or ignore-case
                (string-equal item (car entry)))
402
      (message "Found in different case: %s" (car entry)))
403 404
    (or doc-found
	(error "Info documentation for lookup was not found"))
Richard M. Stallman's avatar
Richard M. Stallman committed
405 406 407 408 409 410 411 412 413 414 415 416
    ;; Don't leave the Info buffer if the help item couldn't be looked up.
    (if (and info-lookup-other-window-flag found)
	(select-window window))))

(defun info-lookup-setup-mode (topic mode)
  "Initialize the internal data structure."
  (or (info-lookup->initialized topic mode)
      (let (cell data (initialized 0) completions refer-modes)
	(if (not (info-lookup->mode-value topic mode))
	    (message "No %s help available for `%s'" topic mode)
	  ;; Recursively setup cross references.
	  ;; But refer only to non-void modes.
417 418 419 420 421
	  (dolist (arg (info-lookup->other-modes topic mode))
	    (or (info-lookup->initialized topic arg)
		(info-lookup-setup-mode topic arg))
	    (and (eq (info-lookup->initialized topic arg) t)
		 (setq refer-modes (cons arg refer-modes))))
Richard M. Stallman's avatar
Richard M. Stallman committed
422 423 424
	  (setq refer-modes (nreverse refer-modes))
	  ;; Build the full completion alist.
	  (setq completions
425 426 427
		(nconc (condition-case nil
			   (info-lookup-make-completions topic mode)
			 (error nil))
Richard M. Stallman's avatar
Richard M. Stallman committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
		       (apply 'append
			      (mapcar (lambda (arg)
					(info-lookup->completions topic arg))
				      refer-modes))))
	  (setq initialized t))
	;; Update `info-lookup-cache'.
	(setq cell (info-lookup->mode-cache topic mode)
	      data (list initialized completions refer-modes))
	(if (not cell)
	    (setcdr (info-lookup->cache topic)
		    (cons (cons mode data) (info-lookup->topic-cache topic)))
	  (setcdr cell data))
	initialized)))

(defun info-lookup-make-completions (topic mode)
  "Create a unique alist from all index entries."
444 445 446
  (let ((doc-spec (info-lookup->doc-spec topic mode))
	(regexp (concat "^\\(" (info-lookup->regexp topic mode)
			"\\)\\([ \t].*\\)?$"))
447
	Info-fontify-maximum-menu-size
448
	node trans entry item prefix result doc-found
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
	(buffer (get-buffer-create " temp-info-look")))
    (with-current-buffer buffer
      (Info-mode))
    (while doc-spec
      (setq node (nth 0 (car doc-spec))
	    trans (cond ((eq (nth 1 (car doc-spec)) nil)
			 (lambda (arg)
			   (if (string-match regexp arg)
			       (match-string 1 arg))))
			((stringp (nth 1 (car doc-spec)))
			 (setq prefix (nth 1 (car doc-spec)))
			 (lambda (arg)
			   (if (string-match "^\\([^: \t\n]+\\)" arg)
			       (concat prefix (match-string 1 arg)))))
			(t (nth 1 (car doc-spec)))))
464 465 466
      (with-current-buffer buffer
	(message "Processing Info node `%s'..." node)
	(when (condition-case error-data
Dave Love's avatar
Dave Love committed
467
		  (progn
468 469
		    (Info-goto-node node)
		    (setq doc-found t))
Dave Love's avatar
Dave Love committed
470
		(error
471 472 473 474 475 476 477
		 (message "Cannot access Info node `%s'" node)
		 (sit-for 1)
		 nil))
	  (condition-case nil
	      (progn
		(goto-char (point-min))
		(and (search-forward "\n* Menu:" nil t)
478
		     (while (re-search-forward "\n\\* \\(.*\\): " nil t)
479 480
		       (setq entry (match-string 1)
			     item (funcall trans entry))
481 482 483
		       ;; `trans' can return nil if the regexp doesn't match.
		       (when (and item
				  ;; Sometimes there's more than one Menu:
Dave Love's avatar
Dave Love committed
484
				  (not (string= entry "Menu")))
485 486 487 488 489 490 491
			 (and (info-lookup->ignore-case topic mode)
			      (setq item (downcase item)))
			 (and (string-equal entry item)
			      (setq entry nil))
			 (and (or (assoc item result)
				  (setq result (cons (cons item entry)
						     result))))))))
492
	    (error nil))))
493 494
      (message "Processing Info node `%s'...done" node)
      (setq doc-spec (cdr doc-spec)))
495 496
    (or doc-found
	(error "Info documentation for lookup was not found"))
497
    result))
Richard M. Stallman's avatar
Richard M. Stallman committed
498 499

(defun info-lookup-guess-default (topic mode)
500 501 502
  "Return a guess for a symbol to look up, based on text around point.
Try all related modes applicable to TOPIC and MODE.
Return nil if there is nothing appropriate in the buffer near point."
Richard M. Stallman's avatar
Richard M. Stallman committed
503
  (let ((modes (info-lookup->all-modes topic mode))
504
	guess)
Richard M. Stallman's avatar
Richard M. Stallman committed
505 506
    (while (and (not guess) modes)
      (setq guess (info-lookup-guess-default* topic (car modes))
507
	    modes (cdr modes)))
Richard M. Stallman's avatar
Richard M. Stallman committed
508
    ;; Collapse whitespace characters.
509 510 511 512 513 514
    (when guess
      (let ((pos 0))
	(while (string-match "[ \t\n]+" guess pos)
	  (setq pos (1+ (match-beginning 0)))
	  (setq guess (replace-match " " t t guess)))))
    guess))
Richard M. Stallman's avatar
Richard M. Stallman committed
515 516 517 518 519 520

(defun info-lookup-guess-default* (topic mode)
  (let ((case-fold-search (info-lookup->ignore-case topic mode))
	(rule (or (info-lookup->parse-rule topic mode)
		  (info-lookup->regexp topic mode)))
	(start (point)) end regexp subexp result)
521 522 523 524 525 526 527 528
    (save-excursion
      (if (symbolp rule)
	  (setq result (funcall rule))
	(if (consp rule)
	    (setq regexp (car rule)
		  subexp (cdr rule))
	  (setq regexp rule
		subexp 0))
Dave Love's avatar
Dave Love committed
529 530 531 532 533
	;; If at start of symbol, don't go back to end of previous one.
	(if (save-match-data
	      (looking-at "[ \t\n]"))
	    (skip-chars-backward " \t\n"))
	(setq end (point))
534 535 536 537 538 539 540 541 542 543
	(while (and (re-search-backward regexp nil t)
		    (looking-at regexp)
		    (>= (match-end 0) end))
	  (setq result (match-string subexp)))
	(if (not result)
	    (progn
	      (goto-char start)
	      (skip-chars-forward " \t\n")
	      (and (looking-at regexp)
		   (setq result (match-string subexp)))))))
Richard M. Stallman's avatar
Richard M. Stallman committed
544 545 546 547 548 549
    result))

(defun info-lookup-guess-c-symbol ()
  "Get the C symbol at point."
  (condition-case nil
      (progn
550
	(skip-syntax-backward "w_")
Richard M. Stallman's avatar
Richard M. Stallman committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
	(let ((start (point)) prefix name)
	  ;; Test for a leading `struct', `union', or `enum' keyword
	  ;; but ignore names like `foo_struct'.
	  (setq prefix (and (< (skip-chars-backward " \t\n") 0)
			    (< (skip-chars-backward "_a-zA-Z0-9") 0)
			    (looking-at "\\(struct\\|union\\|enum\\)\\s ")
			    (concat (match-string 1) " ")))
	  (goto-char start)
	  (and (looking-at "[_a-zA-Z][_a-zA-Z0-9]*")
	       (setq name (match-string 0)))
	  ;; Caveat!  Look forward if point is at `struct' etc.
	  (and (not prefix)
	       (or (string-equal name "struct")
		   (string-equal name "union")
		   (string-equal name "enum"))
	       (looking-at "[a-z]+\\s +\\([_a-zA-Z][_a-zA-Z0-9]*\\)")
	       (setq prefix (concat name " ")
		     name (match-string 1)))
	  (and (or prefix name)
	       (concat prefix name))))
    (error nil)))

573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
(defun info-lookup-guess-custom-symbol ()
  "Get symbol at point in custom buffers."
  (condition-case nil
      (save-excursion
	(let ((case-fold-search t)
	      (ignored-chars "][()`',:.\" \t\n")
	      (significant-chars "^][()`',:.\" \t\n")
	      beg end)
	  (cond
	   ((and (memq (get-char-property (point) 'face)
			 '(custom-variable-tag custom-variable-tag-face))
		   (setq beg (previous-single-char-property-change
			      (point) 'face nil (line-beginning-position)))
		   (setq end (next-single-char-property-change
			      (point) 'face nil (line-end-position)))
		   (> end beg))
	    (subst-char-in-string
590
	     ?\s ?\- (buffer-substring-no-properties beg end)))
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
	   ((or (and (looking-at (concat "[" significant-chars "]"))
		     (save-excursion
		       (skip-chars-backward significant-chars)
		       (setq beg (point)))
		     (skip-chars-forward significant-chars)
		     (setq end (point))
		     (> end beg))
		(and (looking-at "[ \t\n]")
		     (looking-back (concat "[" significant-chars "]"))
		     (setq end (point))
		     (skip-chars-backward significant-chars)
		     (setq beg (point))
		     (> end beg))
		(and (skip-chars-forward ignored-chars)
		     (setq beg (point))
		     (skip-chars-forward significant-chars)
		     (setq end (point))
		     (> end beg)))
	    (buffer-substring-no-properties beg end)))))
    (error nil)))

Richard M. Stallman's avatar
Richard M. Stallman committed
612 613 614
;;;###autoload
(defun info-complete-symbol (&optional mode)
  "Perform completion on symbol preceding point."
615 616 617 618
  (interactive)
  (info-complete 'symbol
		 (or mode
		     (if (info-lookup->mode-value
Karl Heuer's avatar
Karl Heuer committed
619 620
			  'symbol (info-lookup-select-mode))
			 info-lookup-mode
621
		       (info-lookup-change-mode 'symbol)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
622 623 624 625

;;;###autoload
(defun info-complete-file (&optional mode)
  "Perform completion on file preceding point."
626 627 628 629
  (interactive)
  (info-complete 'file
		 (or mode
		     (if (info-lookup->mode-value
Karl Heuer's avatar
Karl Heuer committed
630 631
			  'file (info-lookup-select-mode))
			 info-lookup-mode
632
		       (info-lookup-change-mode 'file)))))
Richard M. Stallman's avatar
Richard M. Stallman committed
633 634 635 636

(defun info-complete (topic mode)
  "Try to complete a help item."
  (barf-if-buffer-read-only)
Karl Heuer's avatar
Karl Heuer committed
637
  (or mode (setq mode (info-lookup-select-mode)))
Richard M. Stallman's avatar
Richard M. Stallman committed
638 639
  (or (info-lookup->mode-value topic mode)
      (error "No %s completion available for `%s'" topic mode))
640 641 642
  (let ((modes (info-lookup-quick-all-modes topic mode))
	(start (point))
	try)
Richard M. Stallman's avatar
Richard M. Stallman committed
643 644 645 646 647 648
    (while (and (not try) modes)
      (setq mode (car modes)
	    modes (cdr modes)
	    try (info-lookup-guess-default* topic mode))
      (goto-char start))
    (and (not try)
649
	 (error "Found no %S to complete" topic))
650 651 652 653 654 655 656 657 658 659 660 661 662
    (let ((completions (info-lookup->completions topic mode))
	  (completion-ignore-case (info-lookup->ignore-case topic mode))
	  completion)
      (setq completion (try-completion try completions))
      (cond ((not completion)
	     (ding)
	     (message "No match"))
	    ((stringp completion)
	     (or (assoc completion completions)
		 (setq completion (completing-read
				   (format "Complete %S: " topic)
				   completions nil t completion
				   info-lookup-history)))
Dave Love's avatar
Dave Love committed
663 664 665 666 667
	     ;; Find the original symbol and zap it.
	     (end-of-line)
	     (while (and (search-backward try nil t)
			 (< start (point))))
	     (replace-match "")
668 669 670 671
	     (insert completion))
	    (t
	     (message "%s is complete"
		      (capitalize (prin1-to-string topic))))))))
672 673 674 675 676 677 678 679


;;; Initialize some common modes.

(info-lookup-maybe-add-help
 :mode 'c-mode :topic 'symbol
 :regexp "\\(struct \\|union \\|enum \\)?[_a-zA-Z][_a-zA-Z0-9]*"
 :doc-spec '(("(libc)Function Index" nil
680
	      "^[ \t]+-+ \\(Function\\|Macro\\): .*\\<" "\\>")
681 682 683 684 685 686
             ;; prefix/suffix has to match things like
             ;;   " -- Macro: int F_DUPFD"
             ;;   " -- Variable: char * tzname [2]"
             ;;   "`DBL_MAX'"    (texinfo @table)
             ;; suffix "\\>" is not used because that sends DBL_MAX to
             ;; DBL_MAX_EXP ("_" is a non-word char)
687
	     ("(libc)Variable Index" nil
688 689
              "^\\([ \t]+-+ \\(Variable\\|Macro\\): .*\\<\\|`\\)"
              "\\( \\|'?$\\)")
690
	     ("(libc)Type Index" nil
691
	      "^[ \t]+-+ Data Type: \\<" "\\>")
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
	     ("(termcap)Var Index" nil
	      "^[ \t]*`" "'"))
 :parse-rule 'info-lookup-guess-c-symbol)

(info-lookup-maybe-add-help
 :mode 'c-mode :topic 'file
 :regexp "[_a-zA-Z0-9./+-]+"
 :doc-spec '(("(libc)File Index")))

(info-lookup-maybe-add-help
 :mode 'bison-mode
 :regexp "[:;|]\\|%\\([%{}]\\|[_a-z]+\\)\\|YY[_A-Z]+\\|yy[_a-z]+"
 :doc-spec '(("(bison)Index" nil
	      "`" "'"))
 :parse-rule "[:;|]\\|%\\([%{}]\\|[_a-zA-Z][_a-zA-Z0-9]*\\)"
 :other-modes '(c-mode))

(info-lookup-maybe-add-help
 :mode 'makefile-mode
 :regexp "\\$[^({]\\|\\.[_A-Z]*\\|[_a-zA-Z][_a-zA-Z0-9-]*"
 :doc-spec '(("(make)Name Index" nil
713 714
	      "^[ \t]*`" "'")
	     ("(automake)Macro and Variable Index" nil
715
	      "^[ \t]*`" "'"))
716 717
 :parse-rule "\\$[^({]\\|\\.[_A-Z]*\\|[_a-zA-Z0-9-]+"
 :other-modes '(automake-mode))
718 719 720 721 722 723 724 725 726

(info-lookup-maybe-add-help
 :mode 'texinfo-mode
 :regexp "@\\([a-zA-Z]+\\|[^a-zA-Z]\\)"
 :doc-spec '(("(texinfo)Command and Variable Index"
	      ;; Ignore Emacs commands and prepend a `@'.
	      (lambda (item)
		(if (string-match "^\\([a-zA-Z]+\\|[^a-zA-Z]\\)\\( .*\\)?$" item)
		    (concat "@" (match-string 1 item))))
727
	      "`" "[' ]")))
728 729 730 731 732 733 734 735 736 737

(info-lookup-maybe-add-help
 :mode 'm4-mode
 :regexp "[_a-zA-Z][_a-zA-Z0-9]*"
 :doc-spec '(("(m4)Macro index"))
 :parse-rule "[_a-zA-Z0-9]+")

(info-lookup-maybe-add-help
 :mode 'autoconf-mode
 :regexp "A[CM]_[_A-Z0-9]+"
738 739 740 741 742 743
 :doc-spec '(;; Autoconf Macro Index entries are without an "AC_" prefix,
	     ;; but with "AH_" or "AU_" for those.  So add "AC_" if there
	     ;; isn't already an "A._".
             ("(autoconf)Autoconf Macro Index"
              (lambda (item)
                (if (string-match "^A._" item) item (concat "AC_" item)))
744
	      "^[ \t]+-+ \\(Macro\\|Variable\\): .*\\<" "\\>")
745 746 747 748 749 750 751 752 753 754 755 756 757 758
             ;; M4 Macro Index entries are without "AS_" prefixes, and
             ;; mostly without "m4_" prefixes.  "dnl" is an exception, not
             ;; wanting any prefix.  So AS_ is added back to upper-case
             ;; names, m4_ to others which don't already an m4_.
             ("(autoconf)M4 Macro Index"
              (lambda (item)
                (let ((case-fold-search nil))
                  (cond ((or (string-equal item "dnl")
                             (string-match "^m4_" item))
                         item)
                        ((string-match "^[A-Z0-9_]+$" item)
                         (concat "AS_" item))
                        (t
                         (concat "m4_" item)))))
759
	      "^[ \t]+-+ Macro: .*\\<" "\\>")
760 761
             ;; Autotest Macro Index entries are without "AT_".
             ("(autoconf)Autotest Macro Index" "AT_"
762
	      "^[ \t]+-+ Macro: .*\\<" "\\>")
763
	     ;; This is for older versions (probably pre autoconf 2.5x):
764
	     ("(autoconf)Macro Index" "AC_"
765
	      "^[ \t]+-+ \\(Macro\\|Variable\\): .*\\<" "\\>")
766 767 768
	     ;; Automake has index entries for its notes on various autoconf
	     ;; macros (eg. AC_PROG_CC).  Ensure this is after the autoconf
	     ;; index, so as to prefer the autoconf docs.
769
	     ("(automake)Macro and Variable Index" nil
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
	      "^[ \t]*`" "'"))
 ;; Autoconf symbols are M4 macros.  Thus use M4's parser.
 :parse-rule 'ignore
 :other-modes '(m4-mode))

(info-lookup-maybe-add-help
 :mode 'awk-mode
 :regexp "[_a-zA-Z]+"
 :doc-spec '(("(gawk)Index"
	      (lambda (item)
		(let ((case-fold-search nil))
		  (cond
		   ;; `BEGIN' and `END'.
		   ((string-match "^\\([A-Z]+\\) special pattern\\b" item)
		    (match-string 1 item))
		   ;; `if', `while', `do', ...
		   ((string-match "^\\([a-z]+\\) statement\\b" item)
		    (if (not (string-equal (match-string 1 item) "control"))
			(match-string 1 item)))
		   ;; `NR', `NF', ...
		   ((string-match "^[A-Z]+$" item)
		    item)
		   ;; Built-in functions (matches to many entries).
		   ((string-match "^[a-z]+$" item)
		    item))))
	      "`" "\\([ \t]*([^)]*)\\)?'")))

(info-lookup-maybe-add-help
 :mode 'perl-mode
 :regexp "[$@%][^a-zA-Z]\\|\\$\\^[A-Z]\\|[$@%]?[a-zA-Z][_a-zA-Z0-9]*"
 :doc-spec '(("(perl5)Function Index"
	      (lambda (item)
		(if (string-match "^\\([a-zA-Z0-9]+\\)" item)
		    (match-string 1 item)))
	      "^" "\\b")
	     ("(perl5)Variable Index"
	      (lambda (item)
		;; Work around bad formatted array variables.
		(let ((sym (cond ((or (string-match "^\\$\\(.\\|@@\\)$" item)
				      (string-match "^\\$\\^[A-Z]$" item))
				  item)
				 ((string-match
				   "^\\([$%@]\\|@@\\)?[_a-zA-Z0-9]+" item)
				  (match-string 0 item))
				 (t ""))))
		  (if (string-match "@@" sym)
		      (setq sym (concat (substring sym 0 (match-beginning 0))
					(substring sym (1- (match-end 0))))))
		  (if (string-equal sym "") nil sym)))
	      "^" "\\b"))
 :parse-rule "[$@%]?\\([_a-zA-Z0-9]+\\|[^a-zA-Z]\\)")

822 823 824 825 826
(info-lookup-maybe-add-help
 :mode 'cperl-mode
 :regexp "[$@%][^a-zA-Z]\\|\\$\\^[A-Z]\\|[$@%]?[a-zA-Z][_a-zA-Z0-9]*"
 :other-modes '(perl-mode))

827 828 829
(info-lookup-maybe-add-help
 :mode 'latex-mode
 :regexp "\\\\\\([a-zA-Z]+\\|[^a-zA-Z]\\)"
830
 :doc-spec '(("(latex)Command Index" nil
831 832 833 834
	      "`" "\\({[^}]*}\\)?'")))

(info-lookup-maybe-add-help
 :mode 'emacs-lisp-mode
835
 :regexp "[^][()`',\" \t\n]+"
836 837 838 839 840 841
 :doc-spec '(;; Commands with key sequences appear in nodes as `foo' and
             ;; those without as `M-x foo'.
             ("(emacs)Command Index"  nil "`\\(M-x[ \t\n]+\\)?" "'")
             ;; Variables normally appear in nodes as just `foo'.
             ("(emacs)Variable Index" nil "`" "'")
             ;; Almost all functions, variables, etc appear in nodes as
842
             ;; " -- Function: foo" etc.  A small number of aliases and
843 844 845 846 847
             ;; symbols appear only as `foo', and will miss out on exact
             ;; positions.  Allowing `foo' would hit too many false matches
             ;; for things that should go to Function: etc, and those latter
             ;; are much more important.  Perhaps this could change if some
             ;; sort of fallback match scheme existed.
848
             ("(elisp)Index"          nil "^ -+ .*: " "\\( \\|$\\)")))
849 850 851

(info-lookup-maybe-add-help
 :mode 'lisp-interaction-mode
852
 :regexp "[^][()`',\" \t\n]+"
853 854 855
 :parse-rule 'ignore
 :other-modes '(emacs-lisp-mode))

856 857
(info-lookup-maybe-add-help
 :mode 'lisp-mode
858
 :regexp "[^()`',\" \t\n]+"
859 860 861
 :parse-rule 'ignore
 :other-modes '(emacs-lisp-mode))

Karl Heuer's avatar
Karl Heuer committed
862 863
(info-lookup-maybe-add-help
 :mode 'scheme-mode
864
 :regexp "[^()`',\" \t\n]+"
Karl Heuer's avatar
Karl Heuer committed
865
 :ignore-case t
866
 ;; Aubrey Jaffer's rendition from <URL:ftp://ftp-swiss.ai.mit.edu/pub/scm>
Karl Heuer's avatar
Karl Heuer committed
867
 :doc-spec '(("(r5rs)Index" nil
868
	      "^[ \t]+-+ [^:]+:[ \t]*" "\\b")))
Karl Heuer's avatar
Karl Heuer committed
869

Stephen Eglen's avatar
Stephen Eglen committed
870 871 872
(info-lookup-maybe-add-help
 :mode 'octave-mode
 :regexp "[_a-zA-Z0-9]+"
Juanma Barranquero's avatar
Juanma Barranquero committed
873
 :doc-spec '(("(octave)Function Index" nil
874 875
	      "^ -+ [^:]+:[ ]+\\(\\[[^=]*=[ ]+\\)?" nil)
	     ("(octave)Variable Index" nil "^ -+ [^:]+:[ ]+" nil)
Stephen Eglen's avatar
Stephen Eglen committed
876
	     ;; Catch lines of the form "xyz statement"
Dave Love's avatar
Dave Love committed
877
	     ("(octave)Concept Index"
Stephen Eglen's avatar
Stephen Eglen committed
878 879 880 881 882
	      (lambda (item)
		(cond
		 ((string-match "^\\([A-Z]+\\) statement\\b" item)
		    (match-string 1 item))
		 (t nil)))
883
	      nil; "^ -+ [^:]+:[ ]+" don't think this prefix is useful here.
Stephen Eglen's avatar
Stephen Eglen committed
884
	      nil)))
885

886 887 888
(info-lookup-maybe-add-help
 :mode 'maxima-mode
 :ignore-case t
889
 :regexp "[a-zA-Z0-9_%]+"
890 891
 :doc-spec '( ("(maxima)Function and Variable Index" nil
	       "^ -+ [^:]+:[ ]+\\(\\[[^=]*=[ ]+\\)?" nil)))
892 893 894

(info-lookup-maybe-add-help
 :mode 'inferior-maxima-mode
895
 :regexp "[a-zA-Z0-9_%]+"
896 897
 :other-modes '(maxima-mode))

898
;; coreutils and bash builtins overlap in places, eg. printf, so there's a
Juanma Barranquero's avatar
Juanma Barranquero committed
899
;; question which should come first.  Some of the coreutils descriptions are
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
;; more detailed, but if bash is usually /bin/sh on a GNU system then the
;; builtins will be what's normally run.
;;
;; Maybe special variables like $? should be matched as $?, not just ?.
;; This would avoid a clash between variable $! and negation !, or variable
;; $# and comment # (though comment # is not currently indexed in bash).
;; Unfortunately if $? etc is the symbol, then we wouldn't be taken to the
;; exact spot in the relevant node, since the bash manual has just `?' etc
;; there.  Maybe an extension to the prefix/suffix scheme could help this.

(info-lookup-maybe-add-help
 :mode 'sh-mode :topic 'symbol
 ;; bash has "." and ":" in its index, but those chars will probably never
 ;; work in info, so don't bother matching them in the regexp.
 :regexp "\\([a-zA-Z0-9_-]+\\|[!{}@*#?$]\\|\\[\\[?\\|]]?\\)"
 :doc-spec '(("(bash)Builtin Index"       nil "^`" "[ .']")
             ("(bash)Reserved Word Index" nil "^`" "[ .']")
             ("(bash)Variable Index"      nil "^`" "[ .']")
             ;; coreutils (version 4.5.10) doesn't have a separate program
             ;; index, so exclude extraneous stuff (most of it) by demanding
             ;; "[a-z]+" in the trans-func.
             ("(coreutils)Index"
              (lambda (item) (if (string-match "\\`[a-z]+\\'" item) item)))
             ;; diff (version 2.8.1) has only a few programs, index entries
             ;; are things like "foo invocation".
             ("(diff)Index"
              (lambda (item)
		(if (string-match "\\`\\([a-z]+\\) invocation\\'" item)
                    (match-string 1 item))))
             ;; there's no plain "sed" index entry as such, mung another
             ;; hopefully unique one to get to the invocation section
             ("(sed)Concept Index"
              (lambda (item)
                (if (string-equal item "Standard input, processing as input")
                    "sed")))
             ;; there's no plain "awk" or "gawk" index entries, mung other
             ;; hopefully unique ones to get to the command line options
             ("(gawk)Index"
              (lambda (item)
                (cond ((string-equal item "gawk, extensions, disabling")
                       "awk")
                      ((string-equal item "gawk, versions of, information about, printing")
                       "gawk"))))))
943

944 945 946
;; This misses some things which occur as node names but not in the
;; index.  Unfortunately it also picks up the wrong one of multiple
;; entries for the same term in some cases.  --fx
947 948
(info-lookup-maybe-add-help
 :mode 'cfengine-mode
949
 :regexp "[[:alnum:]_]+\\(?:()\\)?"
950 951 952 953 954 955 956 957 958
 :doc-spec '(("(cfengine-Reference)Variable Index"
	      (lambda (item)
		;; Index entries may be like `IsPlain()'
		(if (string-match "\\([[:alnum:]_]+\\)()" item)
		    (match-string 1 item)
		  item))
	      ;; This gets functions in evaluated classes.  Other
	      ;; possible patterns don't seem to work too well.
	      "`" "(")))
959 960 961 962 963 964 965 966 967 968 969 970

(info-lookup-maybe-add-help
 :mode 'custom-mode
 :ignore-case t
 :regexp "[^][()`',:\" \t\n]+"
 :parse-rule 'info-lookup-guess-custom-symbol
 :other-modes '(emacs-lisp-mode))

(info-lookup-maybe-add-help
 :mode 'help-mode
 :regexp "[^][()`',:\" \t\n]+"
 :other-modes '(emacs-lisp-mode))
971

Richard M. Stallman's avatar
Richard M. Stallman committed
972 973
(provide 'info-look)

Miles Bader's avatar
Miles Bader committed
974
;;; arch-tag: 0f1e3ea3-32a2-4461-bbab-3cff93539a74
Richard M. Stallman's avatar
Richard M. Stallman committed
975
;;; info-look.el ends here