new version
[emacs.git] / lisp / word-help.el
blob3a7644d7be9e43093917e5a32283e1addc55e91a
1 ;;; word-help.el --- keyword help for any language doc'd in TeXinfo.
3 ;; Copyright (c) 1996 Free Software Foundation, Inc.
5 ;; Author: Jens T. Berger Thielemann <jensthi@ifi.uio.no>
6 ;; Keywords: help, keyword, languages, completion
8 ;; This file is part of GNU Emacs.
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.
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.
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.
25 ;;; Commentary:
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.
32 ;; Installation
33 ;; ************
35 ;; For the default setup to work for all supported modes, make sure
36 ;; the Texinfo files from the following packages are installed:
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 | -
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.
56 ;; Usage
57 ;; *****
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.
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:
68 ;; (global-set-key [?\M-\t] 'word-help-complete)
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.
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.
78 ;; Customizing
79 ;; ***********
81 ;; User interface
82 ;; --------------
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.
89 ;; Adding more Texinfo files
90 ;; -------------------------
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.
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.
101 ;; Contacting the author
102 ;; *********************
104 ;; If you wish to contact me for any reason, please feel free to write
105 ;; to:
107 ;; Jens Berger
108 ;; Spektrumveien 4
109 ;; N-0666 Oslo
110 ;; Norway
112 ;; E-mail: <jensthi@ifi.uio.no>
114 ;; Have fun.
117 ;;; Code:
120 (require 'info)
122 ;;;--------------------
123 ;;; USER OPTIONS
124 ;;;--------------------
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.")
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.")
137 ;;; ---- end of user configurable variables
139 ;;;-------------------------
140 ;;; ADVANCED USER OPTIONS
141 ;;;-------------------------
143 (defvar word-help-mode-alist
145 ("autoconf"
146 (("autoconf" "Macro Index") ("m4" "Macro index"))
147 (("AC_\\([A-Za-z0-9_]+\\)" 1)
148 ("[a-z]+"))
151 (("AC_\\([A-Za-z0-9_]+\\)" 1 nil (("^[A-Z_]+$")))
152 ("[a-z_][a-z_]*" 0 nil (("^[a-z_]+$")))))
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_]*"))
161 (("%[A-Za-z]*" nil nil (("^%")))
162 ("[A-Za-z_][A-Za-z0-9_]*" nil nil (("[A-Za-z_][A-Za-z0-9_]*")))))
164 ("YACC" . "Bison")
166 ("C" (("libc" "Type Index" "Function Index" "Variable Index")))
167 ("C++" . "C")
169 ("Emacs-Lisp"
170 (("elisp" "Index"))
171 (("[^][ ()\n\t.\"'#]+"))
174 lisp-complete-symbol)
176 ("LaTeX"
177 (("latex" "Command Index"))
178 (("\\\\\\(begin\\|end\\){\\([^}\n]+\\)}" 2 0)
179 ("\\\\[A-Za-z]+")
180 ("\\\\[^A-Za-z]")
181 ("[A-Za-z]+"))
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]+")))))
191 ("latex" . "LaTeX")
193 ("Nroff"
194 (("groff" "Macro Index" "Register Index" "Request Index"))
195 (("\\.[^A-Za-z]")
196 ("\\.[A-Za-z]+")
197 ("\\.\\([A-Za-z]+\\)" 1))
200 (("\\.[A-Za-z]*" nil nil (("^\\.[A-Za-z]+$")))
201 ("\\.\\([A-Za-z]*\\)" 1 nil (("^[A-Za-z]+$")))))
203 ("Groff" . "Nroff")
205 ("m4"
206 (("m4" "Macro index"))
207 (("\\([mM]4_\\)?\\([A-Za-z_][A-Za-z_0-9]*\\)" 2))
210 (("[mM]4_\\([A-Za-z_]?[A-Za-z_0-9]*\\)" 1)
211 ("[A-Za-z_][A-Za-z_0-9]*")))
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
222 (("\\.[A-Za-z]*" nil ":" (("^\\.[A-Za-z]+$")))
223 ("\\$(\\([A-Z]*\\)" 1 ")" (("^[A-Z]")))
224 ("[a-z]+" nil nil (("^[a-z]+$")))))
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
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
238 ("Simula" (("simula" "Index")) nil t)
239 ("Ifi Simula" . "Simula")
240 ("SIMULA" . "Simula")
242 ("Texinfo"
243 (("texinfo" "Command and Variable Index"))
244 (("@\\([A-Za-z]+\\)" 1))
247 (("@\\([A-Za-z]*\\)" 1)))
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:
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)
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'.
264 Example entry (for C):
266 \(\"C\" ((\"libc\" \"Type Index\" \"Function Index\" \"Variable Index\"))
267 ((\"[A-Za-z_][A-Za-z0-9]+\")))
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:
273 word-help-keyword-regexps: (\"[A-Za-z_][A-Za-z0-9]+\")
274 word-help-ignore-case: nil
276 More settings may be defined in the future.
278 You may also define aliases, if there are several relevant mode-names
279 to a single entry. These should be of the form:
281 \(MODE-NAME-ALIAS . MODE-NAME-REAL)
283 For C++, you would use the alias
285 \(\"C++\" . \"C\")
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.")
291 ;;; --- end of advanced user options
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)
297 (defvar word-help-info-files nil
298 "List of info files with respective nodes, for the current mode.
300 This should be a list of the following form:
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 ...))
307 An example entry for e.g. C would be:
309 \((\"/local/share/gnu/info/libc\" \"Function Index\" \"Type Index\"
310 \"Variable Index\"))
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)
317 (defvar word-help-keyword-regexps nil
318 "Regexps for finding keywords in the current mode.
320 This is constructed as a list of the following form:
322 \((REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
323 (REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR)
324 : : :
325 (REGEXP SUBMATCH-LOOKUP SUBMATCH-CURSOR))
327 The regexps will be searched in order for a match which the cursor is
328 within.
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:
336 \(\"AC_\\\\([A-Za-z0-9]+\\\\)\" 1)
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]*")))
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.
348 The regexp list should be formatted as:
350 ((REGEXP SUBEXP) (REGEXP SUBEXP) ... )
352 If the index entry does not match any of the regexps, it will be ignored.
354 Example:
356 Perl has index entries of the following form:
358 * abs VALUE: perlfunc.
359 * accept NEWSOCKET,GENERICSOCKET: perlfunc.
360 * alarm SECONDS: perlfunc.
361 * atan2 Y,X: perlfunc.
362 * bind SOCKET,NAME: perlfunc.
363 : : :
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:
368 \((\"^\\\\([^ \\t\\n]+\\\\)\" 1))
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)))
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:
379 ((REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...)
380 : : : : :
381 (REGEXP SUBMATCH TEXT-APPEND (RE-FILTER-1 REG-FILTER-2 ...))
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.
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).
393 If you would like to use another function instead, you may.
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]*")))
400 ;;; Work variables
403 (defvar word-help-main-index nil
404 "List of all index entries.
406 See `word-help-process-indexes' for structure formatting.
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)
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)
418 (defvar word-help-main-obarray nil
419 "Global work variable for `word-help' system.
420 Do Not mess with this!")
422 (defvar word-help-history nil
423 "History for `word-help' minibuffer queries.")
424 (make-local-variable 'word-help-history)
426 (defvar word-help-current-help-file nil
427 "Current help file active for this mode.")
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.")
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)
437 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
438 ;;;;;;;;;;;;;;;;;;;;;;;;;;; User Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
439 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
441 ;;; Debugging
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))
455 ;;; Changing help file
457 ;;;###autoload
458 (defun set-word-help-file ()
459 "Change which set of Texinfo files used for word-help.
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))))
484 ;;; Main user interface
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'.
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.
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)
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))))))))
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'.
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.
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))
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."))))
607 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
608 ;;;;;;;;;;;;;;;;;;;;;;;;;;; Index mapping ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)))))
624 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
625 ;;;;;;;;;;;;;;;;;;;;;;;;; Switch mode files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
626 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
628 ;;; Mode lookup
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)))))
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))))
659 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
660 ;;;;;;;;;;;;;;;;;;;;;;;;;; Index collection ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)))
693 (defun word-help-collect-indexes (info-file)
694 "Process all the indexes in an info file.
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
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))))
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:
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)))
726 The symbols in the obarrays may contain the additional property
727 `word-help-real-name', which tells the *real* node to go to.
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.
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))))))
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)))))
759 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
760 ;;;;;;;;;;;;;;;;;;;;;;;;;;; Keyword guess ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
761 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)))
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)))))
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)))
815 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
816 ;;;;;;;;;;;;;;;;;;;;;;; Show node for keyword ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
817 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
819 ;;; Find an index entry
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'.
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))
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)))))
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)))))
864 ;;; Switch to a node in an index
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:
870 \(FILE-NAME INDEX-NODE-NAME INDEX-ENTRY KEYWORD)"
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))))
908 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
909 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Completion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
910 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)
925 (defun word-help-make-complete ()
926 "Generates the `word-help-complete-index'."
927 (if word-help-complete-index
929 (word-help-find-help-file)
930 (cond
931 ((symbolp word-help-complete-list)
932 (setq word-help-complete-index word-help-complete-list))
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))))))))
952 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
953 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Misc. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
954 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
957 ;;; Default mapping
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))))
968 (provide 'word-help)
970 ;;; word-help.el ends here