(with_echo_area_buffer): Avoid confusion from reusing
[bpt/emacs.git] / lisp / word-help.el
1 ;;; word-help.el --- keyword help for any language doc'd in TeXinfo.
2
3 ;; Copyright (c) 1996 Free Software Foundation, Inc.
4
5 ;; Author: Jens T. Berger Thielemann <jensthi@ifi.uio.no>
6 ;; Keywords: help, keyword, languages, completion
7
8 ;; This file is part of GNU Emacs.
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; This package provides a rather general interface for doing keyword
28 ;; help in most languages. In short, it'll determine which TeXinfo
29 ;; file which is relevant for the current mode; cache the index and
30 ;; use regexps to give you help on the keyword you're looking at.
31
32 ;; Installation
33 ;; ************
34
35 ;; For the default setup to work for all supported modes, make sure
36 ;; the Texinfo files from the following packages are installed:
37
38 ;; Texinfo file | Available in archive or URL | Notes
39 ;; autoconf.info | autoconf-2.10.tar.gz | -
40 ;; bison.info | bison-1.25.tar.gz | -
41 ;; libc.info | glibc-1.09.1.tar.gz | -
42 ;; elisp.info | elisp-manual-19-2.4.tar.gz | -
43 ;; latex.info | ftp://ftp.dante.de/pub/tex/info/latex2e-help-texinfo/latex2e.texi
44 ;; groff.info | groff-1.10.tar.gz | -
45 ;; m4.info | m4-1.4.tar.gz | -
46 ;; make.info | make-3.75.tar.gz | -
47 ;; perl.info | http://www.perl.com/CPAN/doc/manual/info/
48 ;; simula.info | Mail bjort@ifi.uio.no | Written in Norwegian
49 ;; texinfo.info | texinfo-3.9.tar.gz | -
50
51 ;; BTW: We refer to Texinfo files by just their last component, not
52 ;; with an absolute file name. You must thus set up
53 ;; `Info-directory-list' and `Info-default-directory-list' so that
54 ;; these can automatically be located.
55
56 ;; Usage
57 ;; *****
58 ;;
59 ;; Place the cursor over the function/variable/type/whatever you want
60 ;; help on. Type "C-h C-i". `word-help' will then make a suggestion
61 ;; to an index topic; press return to accept this. If not, you may use
62 ;; tab-completion to find the topic you're interested in.
63
64 ;; `word-help' is also able to do symbol completion via the
65 ;; `word-help-complete' function. Bind this function to C-TAB by
66 ;; adding the following line to your .emacs file:
67 ;;
68 ;; (global-set-key [?\M-\t] 'word-help-complete)
69 ;;
70 ;; Note that some modes automatically override this key; you may
71 ;; therefore wish to either put the above statement in a hook or
72 ;; associate the function with an other key.
73
74 ;; Usually, `word-help' is able to determine the relevant Texinfo
75 ;; file from looking at the buffer's `mode-name'; if not, you can use
76 ;; the interactive function `set-help-file' to set this.
77
78 ;; Customizing
79 ;; ***********
80 ;;
81 ;; User interface
82 ;; --------------
83 ;;
84 ;; Two variables control the behaviour of the user-interface of
85 ;; `word-help': `word-help-split-window' and
86 ;; `word-help-magic-index'. Do C-h v to get more information on
87 ;; these.
88
89 ;; Adding more Texinfo files
90 ;; -------------------------
91 ;;
92 ;; Associations between mode-names and Texinfo files can be done
93 ;; through the `word-help-mode-alist' variable, which defines an
94 ;; `alist' making `set-help-file' able to initialize the necessary
95 ;; variable.
96
97 ;; NOTE: If you have to customize the regexps, it is *CRUCIAL* that
98 ;; none of your regexps match the empty string! Not adhering to this
99 ;; restriction will make `word-help' enter an infinite loop.
100
101 ;; Contacting the author
102 ;; *********************
103 ;;
104 ;; If you wish to contact me for any reason, please feel free to write
105 ;; to:
106
107 ;; Jens Berger
108 ;; Spektrumveien 4
109 ;; N-0666 Oslo
110 ;; Norway
111 ;;
112 ;; E-mail: <jensthi@ifi.uio.no>
113
114 ;; Have fun.
115
116 ;;
117 ;;; Code:
118 ;;
119
120 (require 'info)
121
122 ;;;--------------------
123 ;;; USER OPTIONS
124 ;;;--------------------
125
126 (defvar word-help-split-window t
127 "*Non-nil means that the info buffer will pop up in a separate window.
128 If nil, we will just switch to it.")
129
130 (defvar word-help-magic-index t
131 "*Non-nil means that the keyword will be searched for in the requested node.
132 This is done by determining whether the line the point is positioned
133 on after using `Info-goto-node', actually contains the keyword. If
134 not, we will search for the first occurence of the keyword. This may
135 help when the info file isn't correctly indexed.")
136
137 ;;; ---- end of user configurable variables
138
139 ;;;-------------------------
140 ;;; ADVANCED USER OPTIONS
141 ;;;-------------------------
142
143 (defvar word-help-mode-alist
144 '(
145 ("autoconf"
146 (("autoconf" "Macro Index") ("m4" "Macro index"))
147 (("AC_\\([A-Za-z0-9_]+\\)" 1)
148 ("[a-z]+"))
149 nil
150 nil
151 (("AC_\\([A-Za-z0-9_]+\\)" 1 nil (("^[A-Z_]+$")))
152 ("[a-z_][a-z_]*" 0 nil (("^[a-z_]+$")))))
153
154 ("Bison"
155 (("bison" "Index")
156 ("libc" "Type Index" "Function Index" "Variable Index"))
157 (("%[A-Za-z]*")
158 ("[A-Za-z_][A-Za-z0-9_]*"))
159 nil
160 nil
161 (("%[A-Za-z]*" nil nil (("^%")))
162 ("[A-Za-z_][A-Za-z0-9_]*" nil nil (("[A-Za-z_][A-Za-z0-9_]*")))))
163
164 ("YACC" . "Bison")
165
166 ("C" (("libc" "Type Index" "Function Index" "Variable Index")))
167 ("C++" . "C")
168
169 ("Emacs-Lisp"
170 (("elisp" "Index"))
171 (("[^][ ()\n\t.\"'#]+"))
172 nil
173 nil
174 lisp-complete-symbol)
175
176 ("LaTeX"
177 (("latex" "Command Index"))
178 (("\\\\\\(begin\\|end\\){\\([^}\n]+\\)}" 2 0)
179 ("\\\\[A-Za-z]+")
180 ("\\\\[^A-Za-z]")
181 ("[A-Za-z]+"))
182 nil
183 nil
184 (("\\\\begin{\\([A-Za-z]*\\)" 1 "}" (("^[A-Za-z]+$")))
185 ("\\\\end{\\([A-Za-z]*\\)" 1 "}" (("^[A-Za-z]+$")))
186 ("\\\\renewcommand{\\(\\\\?[A-Za-z]*\\)" 1 "}" (("^\\\\[A-Za-z]+")))
187 ("\\\\renewcommand\\(\\\\?[A-Za-z]*\\)" 1 "" (("^\\\\[A-Za-z]+")))
188 ("\\\\renewenvironment{?\\([A-Za-z]*\\)" 1 "}"(("^[A-Za-z]+$")))
189 ("\\\\[A-Za-z]*" 0 "" (("^\\\\[A-Za-z]+")))))
190
191 ("latex" . "LaTeX")
192
193 ("Nroff"
194 (("groff" "Macro Index" "Register Index" "Request Index"))
195 (("\\.[^A-Za-z]")
196 ("\\.[A-Za-z]+")
197 ("\\.\\([A-Za-z]+\\)" 1))
198 nil
199 nil
200 (("\\.[A-Za-z]*" nil nil (("^\\.[A-Za-z]+$")))
201 ("\\.\\([A-Za-z]*\\)" 1 nil (("^[A-Za-z]+$")))))
202
203 ("Groff" . "Nroff")
204
205 ("m4"
206 (("m4" "Macro index"))
207 (("\\([mM]4_\\)?\\([A-Za-z_][A-Za-z_0-9]*\\)" 2))
208 nil
209 nil
210 (("[mM]4_\\([A-Za-z_]?[A-Za-z_0-9]*\\)" 1)
211 ("[A-Za-z_][A-Za-z_0-9]*")))
212
213 ("Makefile"
214 (("make" "Name Index"))
215 (("\\.[A-Za-z]+") ;; .SUFFIXES
216 ("\\$[^()]") ;; $@
217 ("\\$([^A-Za-z].)") ;; $(<@)
218 ("\\$[\(\{]\\([a-zA-Z+]\\)" 1) ;; $(wildcard)
219 ("[A-Za-z]+")) ;; foreach
220 nil
221 nil
222 (("\\.[A-Za-z]*" nil ":" (("^\\.[A-Za-z]+$")))
223 ("\\$(\\([A-Z]*\\)" 1 ")" (("^[A-Z]")))
224 ("[a-z]+" nil nil (("^[a-z]+$")))))
225
226 ("Perl"
227 (("perl" "Variable Index" "Function Index"))
228 (("\\$[^A-Za-z^]") ;; $@
229 ("\\$\\^[A-Za-z]?") ;; $^D
230 ("\\$[A-Za-z][A-Za-z_0-9]+") ;; $foobar
231 ("[A-Za-z_][A-Za-z_0-9]+")) ;; dbmopen
232 nil
233 nil
234 (("\\$[A-Za-z]*" nil nil (("^\\$[A-Za-z]+$"))) ;; $variable
235 ("[A-Za-z_][A-Za-z_0-9]*" nil nil
236 (("^[A-Za-z_][A-Za-z_0-9]*$"))))) ;; function
237
238 ("Simula" (("simula" "Index")) nil t)
239 ("Ifi Simula" . "Simula")
240 ("SIMULA" . "Simula")
241
242 ("Texinfo"
243 (("texinfo" "Command and Variable Index"))
244 (("@\\([A-Za-z]+\\)" 1))
245 nil
246 nil
247 (("@\\([A-Za-z]*\\)" 1)))
248
249 )
250 "Assoc list between `mode-name' and Texinfo files.
251 The variable should be initialized with a list of elements with the
252 following form:
253
254 \(mode-name (word-help-info-files) (word-help-keyword-regexps)
255 word-help-ignore-case word-help-index-mapper
256 word-help-complete-list)
257
258 where `word-help-info-files', `word-help-keyword-regexps' and so
259 forth of course are the values which should be put in these variables
260 for this mode. Note that `mode-name' doesn't have to be a legal
261 mode-name; the user may use the call `set-help-file', where
262 `mode-name' will be used in the `completing-read'.
263
264 Example entry (for C):
265
266 \(\"C\" ((\"libc\" \"Type Index\" \"Function Index\" \"Variable Index\"))
267 ((\"[A-Za-z_][A-Za-z0-9]+\")))
268
269 The two first variables must be initialized; the two remaining will
270 get default values if you omit them or set them to nil. The default
271 values are:
272
273 word-help-keyword-regexps: (\"[A-Za-z_][A-Za-z0-9]+\")
274 word-help-ignore-case: nil
275
276 More settings may be defined in the future.
277
278 You may also define aliases, if there are several relevant mode-names
279 to a single entry. These should be of the form:
280
281 \(MODE-NAME-ALIAS . MODE-NAME-REAL)
282
283 For C++, you would use the alias
284
285 \(\"C++\" . \"C\")
286
287 to make C++ mode use the same help files as C files do. Please note
288 that you can shoot yourself in the foot with this possibility, by
289 defining recursive aliases.")
290
291 ;;; --- end of advanced user options
292
293 (defvar word-help-ignore-case nil
294 "Non-nil means that case is ignored when doing lookup.")
295 (make-variable-buffer-local 'word-help-ignore-case)
296
297 (defvar word-help-info-files nil
298 "List of info files with respective nodes, for the current mode.
299
300 This should be a list of the following form:
301
302 \((INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...)
303 (INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...)
304 : : :
305 (INFO-FILE-1 NODE-NAME-1 NODE-NAME-2 ...))
306
307 An example entry for e.g. C would be:
308
309 \((\"/local/share/gnu/info/libc\" \"Function Index\" \"Type Index\"
310 \"Variable Index\"))
311
312 The files and nodes will be searched/cached in the order specified.
313 This variable is usually set by the `word-help-switch-help-file'
314 function, which utilizes the `word-help-mode-alist'.")
315 (make-variable-buffer-local 'word-help-info-files)
316
317 (defvar word-help-keyword-regexps nil
318 "Regexps for finding keywords in the current mode.
319
320 This is constructed as a list of the following form:
321
322 \((REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
323 (REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
324 : : :
325 (REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR))
326
327 The regexps will be searched in order for a match which the cursor is
328 within.
329
330 submatch-lookup is the submatch number which will be looked for in the
331 index. May be omitted; defaults to 0 (e.g. the entire pattern). This is
332 useful in for instance configure lookup; each command is there prefixed
333 with 'AC_', which must be ignored when doing a lookup. Example regexp
334 entry for this:
335
336 \(\"AC_\\\\([A-Za-z0-9]+\\\\)\" 1)
337
338 submatch-cursor is the part of the match which the cursor must be within.
339 May be omitted; defaults to 0 (e.g. the entire pattern).")
340 (make-variable-buffer-local 'word-help-keyword-regexps)
341 (set-default 'word-help-keyword-regexps '(("[A-Za-z_][A-Za-z_0-9]*")))
342
343 (defvar word-help-index-mapper nil
344 "Regexps to use for massaging index-entries into keywords.
345 This variable should contain a list of regexps with sub-expressions,
346 where we will only look for the sub-expression in the user text.
347
348 The regexp list should be formatted as:
349
350 ((REGEXP SUBEXP) (REGEXP SUBEXP) ... )
351
352 If the index entry does not match any of the regexps, it will be ignored.
353
354 Example:
355
356 Perl has index entries of the following form:
357
358 * abs VALUE: perlfunc.
359 * accept NEWSOCKET,GENERICSOCKET: perlfunc.
360 * alarm SECONDS: perlfunc.
361 * atan2 Y,X: perlfunc.
362 * bind SOCKET,NAME: perlfunc.
363 : : :
364
365 We will thus try to extract the first word in the index entry -
366 \"abs\" from \"abs VALUE\", etc. This is done by the following entry:
367
368 \((\"^\\\\([^ \\t\\n]+\\\\)\" 1))
369
370 This value is btw. the default one, and works with most Texinfo files")
371 (make-variable-buffer-local 'word-help-index-mapper)
372 (set-default 'word-help-index-mapper '(("^\\([^ \t\n]+\\)" 1)))
373
374
375 (defvar word-help-complete-list nil
376 "Regexps or function to use for completion of symbols.
377 The list should have the following format:
378
379 ((REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...)
380 : : : : :
381 (REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...))
382
383 The two first entries are similar to `word-help-keyword-regexps',
384 REGEXP is a regular expression which should match any relevant
385 expression, and where SUBMATCH should be used for look up. By
386 specifying non-nil REGEXP-FILTERs, we'll only include entries in the
387 index which matches the regexp specified.
388
389 If the contents of this variable is a symbol of a function, this
390 function will be called instead. This is useful for modes providing
391 a more intelligent function (like `lisp-complete-symbol' in Emacs Lisp mode).
392
393 If you would like to use another function instead, you may.
394
395 Non-nil TEXT-APPEND means that this text will be inserted after the
396 completion, if we manage to do make a completion.")
397 (make-variable-buffer-local 'word-help-complete-list)
398 (set-default 'word-help-complete-list '(("[A-Za-z_][A-Za-z_0-9]*")))
399
400 ;;; Work variables
401
402
403 (defvar word-help-main-index nil
404 "List of all index entries.
405
406 See `word-help-process-indexes' for structure formatting.
407
408 Minor note: This variable is a list if it is initialized, t if
409 initializing failed and nil if uninitialized.")
410 (make-variable-buffer-local 'word-help-main-index)
411
412 (defvar word-help-complete-index nil
413 "List of regexps for completion, with matching index entries.
414 Value is nil if uninitialized, t if initialized but not accessible,
415 a list if we're feeling ok.")
416 (make-variable-buffer-local 'word-help-complete-index)
417
418 (defvar word-help-main-obarray nil
419 "Global work variable for `word-help' system.
420 Do Not mess with this!")
421
422 (defvar word-help-history nil
423 "History for `word-help' minibuffer queries.")
424 (make-local-variable 'word-help-history)
425
426 (defvar word-help-current-help-file nil
427 "Current help file active for this mode.")
428
429 (defvar word-help-index-alist nil
430 "An assoc list mapping help files to info indexes.
431 This means that `word-help-mode-index' can be init'ed faster.")
432
433 (defvar word-help-help-mode nil
434 "Which mode the help system is bound to for the current mode.")
435 (make-variable-buffer-local 'word-help-help-mode)
436
437 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
438 ;;;;;;;;;;;;;;;;;;;;;;;;;;; User Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
439 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
440
441 ;;; Debugging
442
443 ;;;###autoload
444 (defun reset-word-help ()
445 "Clear all cached indexes in the `word-help' system.
446 You should only need this when installing new info files, and/or
447 adding more Texinfo files to the `word-help' system."
448 (interactive)
449 (setq word-help-index-alist nil
450 word-help-main-index nil
451 word-help-info-files nil
452 word-help-complete-index nil))
453
454
455 ;;; Changing help file
456
457 ;;;###autoload
458 (defun set-word-help-file ()
459 "Change which set of Texinfo files used for word-help.
460
461 `word-help' maintains a list over which Texinfo files which are
462 relevant for each programming language (`word-help-mode-alist'). It
463 usually selects the correct one, based upon the value of `mode-name'.
464 If this guess is incorrect, you may also use this function manually to
465 instruct future `word-help' calls which Texinfo files to use."
466 (interactive)
467 (let (helpfile helpguess (completion-ignore-case t))
468 ;; Try to make a guess
469 (setq helpguess (cond
470 (word-help-current-help-file)
471 ((word-help-guess-help-file))))
472 ;; Ask the user
473 (setq helpfile (completing-read
474 (if helpguess
475 (format "Select help mode (default %s): " helpguess)
476 "Select help mode: ")
477 word-help-mode-alist
478 nil t nil nil))
479 (if (equal "" helpfile)
480 (setq helpfile helpguess))
481 (if helpfile
482 (word-help-switch-help-file helpfile))))
483
484 ;;; Main user interface
485
486 ;;;###autoload
487 (defun word-help ()
488 "Find documentation on the keyword under the cursor.
489 The determination of which language the keyword belongs to, is based upon
490 The relevant info file is selected by matching `mode-name' (the major
491 mode) against the assoc list `word-help-mode-alist'.
492
493 If this is not possible, `set-help-file' will be invoked for selecting
494 the relevant info file. `set-help-file' may also be invoked
495 interactively by the user.
496
497 If the keyword you are looking at is not available in any index, no
498 default suggestion will be presented. "
499 (interactive)
500 (let (myguess guess index-info
501 (completion-ignore-case word-help-ignore-case))
502 ;; Set necessary variables for later lookup
503 (word-help-find-help-file)
504 ;; Have we previously cached datas?
505 (word-help-process-indexes)
506 (if
507 (atom word-help-main-index)
508 (message "No help file available for this mode.")
509 ;; First make a guess at what the user is looking for
510 (setq myguess (word-help-guess
511 (point)
512 (cond
513 ((not (atom word-help-main-index))
514 (car word-help-main-index)))
515 word-help-keyword-regexps))
516 ;; Ask the user himself
517 (setq guess (completing-read
518 ; Format string
519 (if myguess
520 (format "Look up keyword (default %s): " myguess)
521 "Look up keyword: ")
522 ; Collection
523 (car word-help-main-index)
524 nil t nil 'word-help-history))
525 (if (equal guess "")
526 (setq guess myguess))
527 ;; If we've got anything meaningful to lookup, do so
528 (if (not guess)
529 (message "Help aborted.")
530 (setq index-info (word-help-find-index-node
531 guess
532 word-help-main-index))
533 (if (not index-info)
534 (message "Oops, I could not find \"%s\" anyway! Bug?" guess)
535 (word-help-goto-index-node (nconc index-info (list guess))))))))
536
537 ;;;###autoload
538 (defun word-help-complete ()
539 "Perform completion on the symbol preceding the point.
540 The determination of which language the keyword belongs to, is based upon
541 The relevant info file is selected by matching `mode-name' (the major
542 mode) against the assoc list `word-help-mode-alist'.
543
544 If this is not possible, `set-help-file' will be invoked for selecting
545 the relevant info file. `set-help-file' may also be invoked
546 interactively by the user.
547
548 The keywords are extracted from the index of the info file defined for
549 this mode, by using the `word-help-complete-list' variable."
550 (interactive)
551 (word-help-make-complete)
552 (cond
553 ((not word-help-complete-index)
554 (message "No completion available for this mode."))
555 ((symbolp word-help-complete-index)
556 (call-interactively word-help-complete-index))
557 ((listp word-help-complete-index)
558 (let ((all-match (word-help-guess-all (point)
559 word-help-complete-index t))
560 (completion-ignore-case word-help-ignore-case)
561 (c-list word-help-complete-index)
562 c-entry word-match completion completed)
563 ;; Loop over and try to find a match
564 (while (and all-match (not completed))
565 (setq word-match (car all-match)
566 c-entry (car c-list)
567 c-list (cdr c-list)
568 all-match (cdr all-match))
569 ;; Check whether the current pattern matched
570 (if word-match
571 (let ((close (nth 3 c-entry))
572 (words (nth 4 c-entry)))
573 ;; Find the maximum completion for this word
574 ; (print word-match)
575 ; (print c-entry)
576 ; (print close)
577 (setq completion (try-completion word-match words))
578 ;; Was the match exact
579 (cond ((eq completion t)
580 (and close
581 (not (looking-at (regexp-quote close)))
582 (insert close))
583 (setq completed t))
584 ;; Silently ignore non-matches
585 ((not completion))
586 ;; May we complete more unambiguously
587 ((not (string-equal completion word-match))
588 (delete-region (- (point) (length word-match))
589 (point))
590 (insert completion)
591 (if (eq t (try-completion completion words))
592 (progn
593 (and close
594 (not (looking-at (regexp-quote close)))
595 (insert close))))
596 (setq completed t))
597 (t
598 (message "Making completion list...")
599 (let ((list (all-completions word-match words nil)))
600 (setq completed list)
601 (with-output-to-temp-buffer "*Completions*"
602 (display-completion-list list)))
603 (message "Making completion list...done"))))))
604 (if (not completed) (message "No match."))))
605 (t (message "No completion available for this mode."))))
606
607 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
608 ;;;;;;;;;;;;;;;;;;;;;;;;;;; Index mapping ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
610
611
612 (defun word-help-map-index-entries (str re-list)
613 "Transform an Info index entry into a programming keyword.
614 Uses this by mapping the entries through `word-help-index-mapper'."
615 (let ((regexp (car (car re-list)))
616 (subexp (car (cdr (car re-list))))
617 (next (cdr re-list)))
618 (cond
619 ((string-match regexp str)
620 (substring str (match-beginning subexp) (match-end subexp)))
621 (next
622 (word-help-map-index-entries str next)))))
623
624 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
625 ;;;;;;;;;;;;;;;;;;;;;;;;; Switch mode files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
626 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
627
628 ;;; Mode lookup
629
630 (defun word-help-guess-help-file ()
631 "Guesses a relevant help file based on mode name.
632 Returns nil if no guess could be made. Uses `word-help-mode-alist'."
633 (let (guess)
634 (cond
635 ((setq guess (assoc mode-name word-help-mode-alist))
636 (car guess)))))
637
638
639 (defun word-help-switch-help-file (helpfile)
640 "Changes the help-file to the mode name given.
641 Uses `word-help-mode-alist'."
642 (if helpfile
643 (let (helpdesc)
644 (if (not (setq helpdesc (assoc helpfile word-help-mode-alist)))
645 (message "No help defined for \"%s\"." helpfile)
646 (if (stringp (cdr helpdesc))
647 (word-help-switch-help-file (cdr helpdesc))
648 (word-help-make-default-map
649 helpdesc
650 (list 'word-help-help-mode
651 'word-help-info-files
652 'word-help-keyword-regexps
653 'word-help-ignore-case
654 'word-help-index-mapper
655 'word-help-complete-list))))
656 (setq word-help-main-index nil
657 word-help-complete-index nil))))
658
659 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
660 ;;;;;;;;;;;;;;;;;;;;;;;;;; Index collection ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
662
663
664 (defun word-help-extract-index (file-name index-list index-map ignore-case)
665 "Extract index from filename and the first node name in index list.
666 `file-name' is the name of the info file, while `index-list' is a list
667 of node-names to search."
668 (let (cmd1 cmdlow nodename ob-array next (case-fold-search word-help-ignore-case))
669 (setq nodename (car index-list))
670 (setq ob-array (make-vector 211 0))
671 (message "Processing \"%s\" in %s..." nodename file-name)
672 (save-window-excursion
673 (Info-goto-node (concat "(" file-name ")" nodename))
674 (end-of-buffer)
675 (while (re-search-backward "\\* \\([^\n:]+\\):" nil t)
676 (setq cmd1 (buffer-substring (match-beginning 1) (match-end 1)))
677 (setq cmdlow (if ignore-case (downcase cmd1) cmd1))
678 (if index-map
679 (setq cmdlow (word-help-map-index-entries cmdlow
680 index-map)))
681 ;; We have to do this workaround to support case-insensitive matching
682 (cond
683 (cmdlow
684 (put (intern cmdlow ob-array) 'word-help-real-name cmd1)
685 (intern cmdlow word-help-main-obarray)))))
686 (setq next (cond
687 ((cdr index-list)
688 (word-help-extract-index file-name (cdr index-list)
689 index-map ignore-case))))
690 (nconc (list (list nodename ob-array)) next)))
691
692
693 (defun word-help-collect-indexes (info-file)
694 "Process all the indexes in an info file.
695
696 Uses `word-help-extract-index' on each node, and returns an entry
697 suitable for merging into `word-help-process-indexes'. `info-file'
698 is an entry of the form
699
700 \(FILE-NAME INDEX-NAME-1 INDEX-NAME-2 ...)"
701 (let ((file (car info-file))
702 (nodes (cdr info-file)))
703 (nconc (list file) (word-help-extract-index file nodes
704 word-help-index-mapper
705 word-help-ignore-case))))
706
707 (defun word-help-process-indexes ()
708 "Process all the entries in the global variable `word-help-info-files'.
709 Returns a list formatted as follows:
710
711 \(all-entries-ob
712 (file-name-1 (node-name-1 this-node-entries-ob)
713 (node-name-2 this-node-entries-ob)
714 : : :
715 (node-name-n this-node-entries-ob))
716 (file-name-2 (node-name-1 this-node-entries-ob)
717 (node-name-2 this-node-entries-ob)
718 : : :
719 (node-name-n this-node-entries-ob))
720 : : : : : : : : :
721 (file-name-n (node-name-1 this-node-entries-ob)
722 (node-name-2 this-node-entries-ob)
723 : : :
724 (node-name-n this-node-entries-ob)))
725
726 The symbols in the obarrays may contain the additional property
727 `word-help-real-name', which tells the *real* node to go to.
728
729 Note that we use `word-help-index-alist' to speed up the process. Note
730 that `word-help-switch-help-file' must have been called before this function.
731
732 This structure is then later searched by `word-help-find-index-node'."
733 (let (index-words old-index)
734 (if (not word-help-main-index)
735 (cond
736 ((setq old-index
737 (assoc word-help-help-mode word-help-index-alist))
738 (setq word-help-main-index (nth 1 old-index)))
739 (word-help-info-files
740 (setq word-help-main-obarray (make-vector 307 0)
741 index-words (mapcar 'word-help-collect-indexes
742 word-help-info-files)
743 word-help-main-index
744 (append (list word-help-main-obarray) index-words))
745 (setq word-help-index-alist (cons (list word-help-help-mode
746 word-help-main-index)
747 word-help-index-alist)))
748 (t (setq word-help-main-index t))))))
749
750 (defun word-help-find-help-file ()
751 "Tries to find and set a relevant help file for the current mode."
752 (let (helpguess)
753 (if (not word-help-info-files)
754 (if (setq helpguess (word-help-guess-help-file))
755 (word-help-switch-help-file helpguess)
756 (set-help-file)))))
757
758
759 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
760 ;;;;;;;;;;;;;;;;;;;;;;;;;;; Keyword guess ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
761 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
762
763 (defun word-help-guess-all (cur-point re-list
764 &optional copy-to-point)
765 "Guesses *all* keywords the user possibly may be looking at.
766 Returns a list of all possible keywords. "
767 (let ((regexp (car (car re-list)))
768 (submatch (cond ((nth 1 (car re-list))) (0)))
769 (cursmatch (cond ((nth 2 (car re-list))) (0)))
770 (guess nil)
771 (next-guess nil)
772 (case-fold-search word-help-ignore-case)
773 (end-point nil))
774 (save-excursion
775 (end-of-line)
776 (setq end-point (point))
777 ;; Start at the beginning
778 (beginning-of-line)
779 (while (and (not guess) (re-search-forward regexp end-point t))
780 ;; Look whether the cursor is within the match
781 (if (and (<= (match-beginning cursmatch) cur-point)
782 (>= (match-end cursmatch) cur-point))
783 (if (or (not copy-to-point) (<= cur-point (match-end submatch)))
784 (setq guess (buffer-substring (match-beginning submatch)
785 (if copy-to-point
786 cur-point
787 (match-end submatch)))))))
788 ;; If we found anything, return it and call ourselves again
789 (if (cdr re-list)
790 (setq next-guess (word-help-guess-all cur-point (cdr re-list)
791 copy-to-point))))
792 (cons guess next-guess)))
793
794 (defun word-help-guess-match (all-match cmd-array)
795 (let ((sym (car all-match)))
796 (cond
797 ((and sym (intern-soft (if word-help-ignore-case
798 (downcase sym)
799 sym) cmd-array)
800 sym))
801 ((cdr all-match)
802 (word-help-guess-match (cdr all-match) cmd-array)))))
803
804
805 (defun word-help-guess (cur-point cmd-array re-list)
806 "Guesses what keyword the user is looking at, and returns that.
807 CUR-POINT should be the current value of `point', CMD-ARRAY an obarray
808 of all the keywords which are defined for the current mode, and
809 RE-LIST a list of regexps use for the hunt. See also
810 `word-help-keyword-regexps'."
811 (let ((all-matches (word-help-guess-all cur-point re-list)))
812 ; (print all-matches)
813 (word-help-guess-match all-matches cmd-array)))
814
815 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
816 ;;;;;;;;;;;;;;;;;;;;;;; Show node for keyword ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
817 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
818
819 ;;; Find an index entry
820
821 (defun word-help-find-index-node (node index-reg)
822 "Finds the node named `node' in the index-register `index-reg'.
823 `index-reg' has the format as returned (and documented) by the
824 `word-help-process-indexes' call. In most cases, this will be equal to
825 `word-help-main-index'.
826
827 Returns a list with format
828 (file-name index-node-name index-entry)
829 which contains the file and index where the entry can be found.
830 Returns nil if the entry can't be found."
831 (let (file-info node-name)
832 (setq node-name (cond (word-help-ignore-case (downcase node)) (node)))
833 (if (intern-soft node-name (car index-reg))
834 (setq file-info (word-help-index-search-file node-name
835 (cdr index-reg))))
836 file-info))
837
838 (defun word-help-index-search-file (entry file-data)
839 "Searches a cached file for the index-entry `entry'."
840 (let (this-file next-files file-name node node-infos)
841 (setq this-file (car file-data)
842 next-files (cdr file-data)
843 file-name (car this-file)
844 node-infos (cdr this-file)
845 node (word-help-index-search-nodes entry node-infos))
846 (cond
847 (node
848 (cons file-name node))
849 (next-files (word-help-index-search-file entry next-files)))))
850
851 (defun word-help-index-search-nodes (entry node-info)
852 "Searches a cached list of nodes for the entry `entry'."
853 (let (this-node next-nodes node-name node-ob node-sym)
854 (setq this-node (car node-info)
855 next-nodes (cdr node-info)
856 node-name (car this-node)
857 node-ob (car (cdr this-node))
858 node-sym (intern-soft entry node-ob))
859 (cond
860 (node-sym
861 (list node-name (get node-sym 'word-help-real-name)))
862 (next-nodes (word-help-index-search-nodes entry next-nodes)))))
863
864 ;;; Switch to a node in an index
865
866 (defun word-help-goto-index-node (index-info)
867 "Jumps to an index node.
868 `index-info' should be a list with the following format:
869
870 \(FILE-NAME INDEX-NODE-NAME INDEX-ENTRY KEYWORD)"
871
872 (let* ((file-name (car index-info))
873 (node-name (nth 1 index-info))
874 (entry-name (nth 2 index-info))
875 (kw-name (nth 3 index-info))
876 (buffer (current-buffer)))
877 (if word-help-split-window
878 (pop-to-buffer nil))
879 (Info-goto-node (concat "(" file-name ")" node-name))
880 (Info-menu entry-name)
881 ;; Do magic keyword search
882 (if word-help-magic-index
883 (let (end-point regs this-re found entry-re)
884 (setq entry-re (regexp-quote kw-name)
885 regs (list (concat
886 (if (string-match "^[A-Za-z]" entry-name)
887 "\\<" "")
888 entry-re
889 (if (string-match "[A-Za-z]$" entry-name)
890 "\\>" ""))
891 (concat "[`\"\(]" entry-re)
892 (concat "^" entry-re
893 (if (string-match "[A-Za-z]$" entry-name)
894 "\\>" ""))))
895 (end-of-line)
896 (setq end-point (point))
897 (beginning-of-line)
898 (if (not (re-search-forward (car regs) end-point t))
899 (while (and (not found) (car regs))
900 (setq this-re (car regs)
901 regs (cdr regs)
902 found (re-search-forward this-re nil t))))
903 (recenter 0)))
904 (if word-help-split-window
905 (pop-to-buffer buffer))))
906
907
908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
909 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Completion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
910 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
911
912
913
914 (defun word-help-extract-matches (from-ob dest-ob re-list)
915 "Takes atoms from from-ob, and puts them in dest-ob if they match re-list."
916 (let ((regexp (car (car re-list))))
917 (mapatoms (lambda (x)
918 (if (or (not regexp) (string-match regexp (symbol-name x)))
919 (intern (symbol-name x) dest-ob)))
920 from-ob)
921 (if (cdr re-list)
922 (word-help-extract-matches from-ob dest-ob (cdr re-list))))
923 dest-ob)
924
925 (defun word-help-make-complete ()
926 "Generates the `word-help-complete-index'."
927 (if word-help-complete-index
928 nil
929 (word-help-find-help-file)
930 (cond
931 ((symbolp word-help-complete-list)
932 (setq word-help-complete-index word-help-complete-list))
933 (t
934 (word-help-process-indexes)
935 (if (not (atom word-help-main-index))
936 (let ((from-ob (car word-help-main-index)))
937 (message "Processing keywords...")
938 (setq word-help-complete-index
939 (mapcar
940 (lambda (cmpl)
941 (let
942 ((regexp (car cmpl))
943 (subm (cond ((nth 1 cmpl)) (0)))
944 (app (cond ((nth 2 cmpl)) ("")))
945 (re-list (cond ((nth 3 cmpl)) ('((".")))))
946 (obarr (make-vector 47 0)))
947 (list regexp subm subm app
948 (word-help-extract-matches from-ob obarr
949 re-list))))
950 word-help-complete-list))))))))
951
952 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
953 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Misc. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
954 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
955
956
957 ;;; Default mapping
958
959 (defun word-help-make-default-map (list vars)
960 "Makes a default mapping for `vars', which must be listed in order.
961 vars is a list of quoted symbols. If the nth entry in the list is
962 non-nil, the nth variable will be given this value. If nil, the var
963 will be given the global default value."
964 (set (car vars) (cond ((car list)) ((default-value (car vars)))))
965 (if (cdr vars)
966 (word-help-make-default-map (cdr list) (cdr vars))))
967
968 (provide 'word-help)
969
970 ;;; word-help.el ends here