]> code.delx.au - gnu-emacs/blob - lisp/progmodes/idlw-help.el
Initial checkin of HTML help support module.
[gnu-emacs] / lisp / progmodes / idlw-help.el
1 ;;; idlw-help.el --- HTML Help code for IDLWAVE
2 ;; Copyright (c) 2000 Carsten Dominik
3 ;; Copyright (c) 2001, 2002 J.D. Smith
4 ;; Copyright (c) 2003,2004 Free Software Foundation
5 ;;
6 ;; Authors: J.D. Smith <jdsmith@as.arizona.edu>
7 ;; Carsten Dominik <dominik@astro.uva.nl>
8 ;; Maintainer: J.D. Smith <jdsmith@as.arizona.edu>
9 ;; Version: 5.5
10
11 ;; This file is part of GNU Emacs.
12
13 ;; This file is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; This file is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29
30 ;; The help link information for IDLWAVE's online help feature for
31 ;; system routines is extracted automatically from the IDL
32 ;; documentation, and is available, along with general routine
33 ;; information, in the file idlw-rinfo.el. The HTML help file
34 ;; themselves are not distributable with Emacs, but are available,
35 ;; along with new versions of IDLWAVE, documentation, and more
36 ;; information, at:
37 ;;
38 ;; http://idlwave.org
39 ;;
40 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 \f
42
43 ;;; Code:
44 (require 'browse-url)
45
46 (defgroup idlwave-online-help nil
47 "Online Help options for IDLWAVE mode."
48 :group 'idlwave)
49
50 (defcustom idlwave-html-help-pre-v6 nil
51 "Whether pre or post-v6.0 IDL help documents are being used."
52 :group 'idlwave-online-help
53 :type 'boolean)
54
55 (defvar idlwave-html-link-sep
56 (if idlwave-html-help-pre-v6 "#" "#wp"))
57
58 (defcustom idlwave-html-help-location
59 (if (memq system-type '(ms-dos windows-nt))
60 nil
61 "/usr/local/etc/")
62 "The directory where the idl_html_help/ dir or idl.chm help file
63 (Windows only) lives."
64 :group 'idlwave-online-help
65 :type 'directory)
66
67 (defcustom idlwave-help-use-hh nil
68 "Whether to use the HTMLHelp viewer with idl.chm (Windows only)."
69 :group 'idlwave-online-help
70 :type '(choice :tag "use help viewer"
71 (const :tag "<none>" nil)
72 (const :tag "hh" 'hh)
73 (const :tag "keyhh" 'keyhh)))
74
75 (defcustom idlwave-help-browser-function browse-url-browser-function
76 "Function to use to display html help.
77 Defaults to `browse-url-browser-function', which see."
78 :group 'idlwave-online-help
79 :type 'function)
80
81 (defcustom idlwave-help-browser-generic-program browse-url-generic-program
82 "Program to run if using browse-url-generic-program."
83 :group 'idlwave-online-help
84 :type 'string)
85
86 (defcustom idlwave-help-browser-generic-args
87 (if (boundp 'browse-url-generic-args)
88 browse-url-generic-args "")
89 "Program args to use if using browse-url-generic-program."
90 :group 'idlwave-online-help
91 :type 'string)
92
93 (defcustom idlwave-help-browser-is-local nil
94 "Whether the browser will display locally in an Emacs window.
95 Several browsers run and/or display inside Emacs windows, but most are
96 external programs. If the browser name contains \"-w3\", it is
97 assumed to be local to Emacs. For other local browsers, this variable
98 must be explicitly set non-nil in order for the variable
99 `idlwave-help-use-dedicated-frame' to function."
100 :group 'idlwave-online-help
101 :type 'boolean)
102
103 (defvar idlwave-help-directory ""
104 "Obsolete variable. See idlwave-html-help-location.")
105
106 (defcustom idlwave-help-use-dedicated-frame t
107 "*Non-nil means, use a separate frame for Online Help if possible."
108 :group 'idlwave-online-help
109 :type 'boolean)
110
111 (defcustom idlwave-help-frame-parameters
112 '((height . 32) (unsplittable . t))
113 "The frame parameters for the special Online Help frame.
114 See also `idlwave-help-use-dedicated-frame'.
115 If you do not set the frame width here, the value specified in
116 `idlw-help.el' will be used."
117 :group 'idlwave-online-help
118 :type '(repeat
119 (cons symbol sexp)))
120
121 (defcustom idlwave-max-popup-menu-items 20
122 "Maximum number of items per pane in popup menus.
123 Currently only used for class selection during completion help."
124 :group 'idlwave-online-help
125 :type 'integer)
126
127 (defcustom idlwave-extra-help-function 'idlwave-help-with-source
128 "The function to call for online help if the normal help fails.
129 Online help works only for system routines which are described in the
130 IDL manuals. A function may be specified to access help from other sources.
131
132 The function must accept four arguments: NAME, TYPE, CLASS, KEYWORD.
133 The Help buffer is current when this function is called, and the help
134 text should be loaded into this buffer. If help is found, the
135 function should return the buffer position which should be used as
136 `window-start' in the help window. Also, the variable
137 `idlwave-help-mode-line-indicator' should be set to a useful string,
138 which will be displayed in the mode line of the help window. If
139 should also set the variable `idlwave-help-min-frame-width' to a
140 positive integer. IDLWAVE will ensure that the help frame is at least
141 that many columns wide. Failure to find help should be indicated by
142 throwing an error.
143
144 When this variable is non-nil, IDLWAVE will allow the mouse-3 help click
145 for every routine and keyword, even though the item may not be highlighted
146 in blue (indicating the availability of system documentation).
147
148 The default value for this function is `idlwave-help-with-source' which
149 loads the routine source file into the help buffer. If you try to write
150 a different function which accesses a special help file or so, it is
151 probably a good idea to still call this function as a fallback."
152 :group 'idlwave-online-help
153 :type 'symbol)
154
155 (defcustom idlwave-help-fontify-source-code nil
156 "*Non-nil means, fontify source code displayed as help like normal code."
157 :group 'idlwave-online-help
158 :type 'boolean)
159
160 (defcustom idlwave-help-source-try-header t
161 "*Non-nil means, try to find help in routine header when displaying source.
162 Routines which are not documented in the system manual use their source as
163 help text. When this variable is non-nil, we try to find a description of
164 the help item in the first routine doclib header above the routine definition.
165 If the variable is nil, or if we cannot find/parse the header, the routine
166 definition is displayed instead."
167 :group 'idlwave-online-help
168 :type 'boolean)
169
170
171 (defcustom idlwave-help-doclib-name "name"
172 "*A regexp for the heading word to search for in doclib headers
173 which specifies the `name' section. Can be used for localization
174 support."
175 :group 'idlwave-online-help
176 :type 'string)
177
178 (defcustom idlwave-help-doclib-keyword "KEYWORD"
179 "*A regexp for the heading word to search for in doclib headers
180 which specifies the `keywords' section. Can be used for localization
181 support."
182 :group 'idlwave-online-help
183 :type 'string)
184
185 (defface idlwave-help-link-face
186 '((((class color)) (:foreground "Blue"))
187 (t (:weight bold)))
188 "Face for highlighting links into IDLWAVE online help."
189 :group 'idlwave-online-help)
190
191 (defvar idlwave-help-activate-links-aggressively nil
192 "Obsolete variable.")
193
194 (defvar idlwave-completion-help-info)
195
196 (defvar idlwave-help-frame nil
197 "The frame for display of IDL online help.")
198 (defvar idlwave-help-frame-width 102
199 "The default width of the help frame.")
200
201 (defvar idlwave-html-help-is-available nil
202 "Is the system online help text avaiable?")
203
204 (defvar idlwave-help-mode-line-indicator ""
205 "Used for the special mode line in the idlwave-help-mode.")
206
207 (defvar idlwave-help-window-configuration nil)
208 (defvar idlwave-help-special-topic-words nil) ; defined by get_rinfo
209
210 ;; Define the key bindings for the Help application
211
212 (defvar idlwave-help-mode-map (make-sparse-keymap)
213 "The keymap used in idlwave-help-mode.")
214
215 (define-key idlwave-help-mode-map "q" 'idlwave-help-quit)
216 (define-key idlwave-help-mode-map "w" 'widen)
217 (define-key idlwave-help-mode-map "\C-m" (lambda (arg)
218 (interactive "p")
219 (scroll-up arg)))
220 (define-key idlwave-help-mode-map " " 'scroll-up)
221 (define-key idlwave-help-mode-map [delete] 'scroll-down)
222 (define-key idlwave-help-mode-map "h" 'idlwave-help-find-header)
223 (define-key idlwave-help-mode-map "H" 'idlwave-help-find-first-header)
224 (define-key idlwave-help-mode-map "." 'idlwave-help-toggle-header-match-and-def)
225 (define-key idlwave-help-mode-map "F" 'idlwave-help-fontify)
226 (define-key idlwave-help-mode-map "\M-?" 'idlwave-help-return-to-calling-frame)
227 (define-key idlwave-help-mode-map "x" 'idlwave-help-return-to-calling-frame)
228
229 ;; Define the menu for the Help application
230
231 (easy-menu-define
232 idlwave-help-menu idlwave-help-mode-map
233 "Menu for Help IDLWAVE system"
234 '("IDLHelp"
235 ["Definition <-> Help Text" idlwave-help-toggle-header-match-and-def t]
236 ["Find DocLib Header" idlwave-help-find-header t]
237 ["Find First DocLib Header" idlwave-help-find-first-header t]
238 ["Fontify help buffer" idlwave-help-fontify t]
239 "--"
240 ["Quit" idlwave-help-quit t]))
241
242 (defun idlwave-help-mode ()
243 "Major mode for displaying IDL Help.
244
245 This is a VIEW mode for the ASCII version of IDL Help files,
246 with some extras. Its main purpose is speed - so don't
247 expect a fully hyper-linked help.
248
249 Scrolling: SPC DEL RET
250 Text Searches: Inside Topic: Use Emacs search functions
251 Exit: [q]uit or mouse button 3 will kill the frame
252
253 When the hep text is a source file, the following commands are available
254
255 Fontification: [F]ontify the buffer like source code
256 Jump: [h] to function doclib header
257 [H] to file doclib header
258 [.] back and forward between header and definition
259
260 Here are all keybindings.
261 \\{idlwave-help-mode-map}"
262 (kill-all-local-variables)
263 (buffer-disable-undo)
264 (setq major-mode 'idlwave-help-mode
265 mode-name "IDLWAVE Help")
266 (use-local-map idlwave-help-mode-map)
267 (easy-menu-add idlwave-help-menu idlwave-help-mode-map)
268 (setq truncate-lines t)
269 (setq case-fold-search t)
270 (setq mode-line-format
271 (list ""
272 'mode-line-modified
273 'mode-line-buffer-identification
274 ": " 'idlwave-help-mode-line-indicator
275 " -%-"))
276 (setq buffer-read-only t)
277 (set (make-local-variable 'idlwave-help-def-pos) nil)
278 (set (make-local-variable 'idlwave-help-args) nil)
279 (set (make-local-variable 'idlwave-help-in-header) nil)
280 (run-hooks 'idlwave-help-mode-hook))
281
282 (defun idlwave-html-help-location ()
283 "Return the help directory where HTML files are, or nil if that is unknown."
284 (or (and (stringp idlwave-html-help-location)
285 (> (length idlwave-html-help-location) 0)
286 (file-directory-p idlwave-html-help-location)
287 idlwave-html-help-location)
288 (getenv "IDLWAVE_HELP_LOCATION")
289 (and (memq system-type '(ms-dos windows-nt)) ; Base it on sysdir
290 idlwave-help-use-hh
291 (stringp idlwave-system-directory)
292 (> (length idlwave-system-directory) 0)
293 (file-directory-p idlwave-system-directory)
294 (expand-file-name "HELP" idlwave-system-directory))))
295
296 (defvar idlwave-current-obj_new-class)
297 (defvar idlwave-help-diagnostics)
298 (defvar idlwave-experimental)
299 (defvar idlwave-last-context-help-pos)
300 (defun idlwave-do-context-help (&optional arg)
301 "Wrapper around the call to idlwave-context-help1.
302 It collects and prints the diagnostics messages."
303 (let ((marker (list (current-buffer) (point)))
304 (idlwave-help-diagnostics nil))
305 ;; Check for frame switching. When the command is invoked twice
306 ;; at the same position, we try to switch to the help frame
307 ;; FIXME: Frame switching works only on XEmacs
308 (if (and idlwave-experimental
309 (equal last-command this-command)
310 (equal idlwave-last-context-help-pos marker))
311 (idlwave-help-select-help-frame)
312 ;; Do the real thing.
313 (setq idlwave-last-context-help-pos marker)
314 (idlwave-do-context-help1 arg)
315 (if idlwave-help-diagnostics
316 (message "%s" (mapconcat 'identity
317 (nreverse idlwave-help-diagnostics)
318 "; "))))))
319
320 (defvar idlwave-help-do-class-struct-tag nil)
321 (defvar idlwave-help-do-struct-tag nil)
322 (defun idlwave-do-context-help1 (&optional arg)
323 "The work-horse version of `idlwave-context-help', which see."
324 (save-excursion
325 (if (equal (char-after) ?/)
326 (forward-char 1)
327 (if (equal (char-before) ?=)
328 (backward-char 1)))
329 (let* ((idlwave-query-class nil)
330 (idlwave-force-class-query (equal arg '(4)))
331 (chars "a-zA-Z0-9_$.!")
332 (beg (save-excursion (skip-chars-backward chars) (point)))
333 (end (save-excursion (skip-chars-forward chars) (point)))
334 (this-word (buffer-substring-no-properties beg end))
335 (st-ass (assoc (downcase this-word)
336 idlwave-help-special-topic-words))
337 (classtag (and (string-match "self\\." this-word)
338 (< beg (- end 4))))
339 (structtag (and (fboundp 'idlwave-complete-structure-tag)
340 (string-match "\\`\\([^.]+\\)\\." this-word)
341 (< beg (- end 4))))
342 module keyword cw mod1 mod2 mod3)
343 (if (or arg
344 (and (not st-ass)
345 (not classtag)
346 (not structtag)
347 (not (member (string-to-char this-word) '(?! ?.)))))
348 ;; Need the module information
349 (progn
350 ;; MODULE is (name type class), for this or any inheriting class
351 (setq module (idlwave-what-module-find-class)
352 cw (nth 2 (idlwave-where))) ;what would we complete here?
353 ;; Correct for OBJ_NEW, we may need an INIT method instead.
354 (if (equal (idlwave-downcase-safe (car module)) "obj_new")
355 (let* ((bos (save-excursion (idlwave-beginning-of-statement)
356 (point)))
357 (str (buffer-substring bos (point))))
358 (if (string-match "OBJ_NEW([ \t]*['\"]\\([a-zA-Z][a-zA-Z0-9$_]+\\)['\"]" str)
359 (setq module (list "init" 'fun (match-string 1 str))
360 idlwave-current-obj_new-class (match-string 1 str))
361 )))))
362 (cond
363 (arg (setq mod1 module))
364
365 ;; A special topic -- only system help
366 (st-ass (setq mod1 (list (cdr st-ass))))
367
368 ;; A system variable -- only system help
369 ((string-match
370 "\\`!\\([a-zA-Z0-9_]+\\)\\(\.\\([A-Za-z0-9_]+\\)\\)?"
371 this-word)
372 (let* ((word (match-string-no-properties 1 this-word))
373 (entry (assq (idlwave-sintern-sysvar word)
374 idlwave-system-variables-alist))
375 (tag (match-string-no-properties 3 this-word))
376 (tag-target (if tag
377 (cdr
378 (assq (idlwave-sintern-sysvartag tag)
379 (cdr (assq 'tags entry))))))
380 (link (nth 1 (assq 'link entry))))
381 (if tag-target
382 (setq link (idlwave-substitute-link-target link
383 tag-target)))
384 (setq mod1 (list link))))
385
386 ;; An executive command -- only system help
387 ((string-match "^\\.\\([A-Z_]+\\)" this-word)
388 (let* ((word (match-string 1 this-word))
389 (link (cdr (assoc-ignore-case
390 word
391 idlwave-executive-commands-alist))))
392 (setq mod1 (list link))))
393
394 ;; A class -- system OR in-text help (via class__define).
395 ((and (eq cw 'class)
396 (or (idlwave-in-quote) ; e.g. obj_new
397 (re-search-backward "\\<inherits[ \t]+[A-Za-z0-9_]*\\="
398 (max (point-min) (- (point) 40)) t)))
399 ;; Class completion inside string delimiters must be
400 ;; the class inside OBJ_NEW.
401 (let* ((entry (assq
402 (idlwave-sintern-class this-word)
403 idlwave-system-class-info))
404 (name (concat (downcase this-word) "__define"))
405 (link (nth 1 (assq 'link entry))))
406 (setq mod1 (list link name 'pro))))
407
408 ;; A class structure tag (self.BLAH) -- only in-text help available
409 (classtag
410 (let ((tag (substring this-word (match-end 0)))
411 class-with found-in)
412 (when (setq class-with
413 (idlwave-class-or-superclass-with-tag
414 (nth 2 (idlwave-current-routine))
415 tag))
416 (setq found-in (idlwave-class-found-in class-with))
417 (if (assq (idlwave-sintern-class class-with)
418 idlwave-system-class-info)
419 (error "No help available for system class tags"))
420 (setq idlwave-help-do-class-struct-tag t)
421 (setq mod1 (list nil
422 (if found-in
423 (cons (concat found-in "__define") class-with)
424 (concat class-with "__define"))
425 'pro
426 nil ; no class.... it's a procedure!
427 tag)))))
428
429 ;; A regular structure tag -- only in text, and if
430 ;; optional `complete-structtag' loaded.
431 (structtag
432 (let ((var (match-string 1 this-word))
433 (tag (substring this-word (match-end 0))))
434 ;; Check if we need to update the "current" structure
435 (idlwave-prepare-structure-tag-completion var)
436 (setq idlwave-help-do-struct-tag
437 idlwave-structtag-struct-location
438 mod1 (list nil nil nil nil tag))))
439
440 ;; A routine keyword -- in text or system help
441 ((and (memq cw '(function-keyword procedure-keyword))
442 (stringp this-word)
443 (string-match "\\S-" this-word)
444 (not (string-match "!" this-word)))
445 (cond ((or (= (char-before beg) ?/)
446 (save-excursion (goto-char end)
447 (looking-at "[ \t]*=")))
448 ;; Certainly a keyword. Check for abbreviation etc.
449 (setq keyword (idlwave-expand-keyword this-word module))
450 (cond
451 ((null keyword)
452 (idlwave-help-diagnostics
453 (format "%s does not accept `%s' kwd"
454 (idlwave-make-full-name (nth 2 module)
455 (car module))
456 (upcase this-word))
457 'ding))
458 ((consp keyword)
459 (idlwave-help-diagnostics
460 (format "%d matches for kwd abbrev `%s'"
461 (length keyword) this-word)
462 'ding)
463 ;; We continue anyway with the first match...
464 (setq keyword (car keyword))))
465 ;; Keyword, or just module
466 (setq mod1 (append (list t) module (list keyword)))
467 (setq mod2 (append (list t) module)))
468 ((equal (char-after end) ?\()
469 ;; A function - what-module will have caught this
470 (setq mod1 (append (list t) module)))
471 (t
472 ;; undecided - try function, keyword, then enclosing mod.
473 ;; Check for keyword abbreviations, but do not report
474 ;; errors, because it might be something else.
475 ;; FIXME: is this a good way to handle this?
476 (setq keyword (idlwave-expand-keyword this-word module))
477 (if (consp keyword) (setq keyword (car keyword)))
478 (setq mod1 (append (list t) module (list keyword))
479 mod2 (list t this-word 'fun nil)
480 mod3 (append (list t) module)))))
481
482 ;; Everything else
483 (t
484 (setq mod1 (append (list t) module))))
485 (if mod3
486 (condition-case nil
487 (apply 'idlwave-online-help mod1)
488 (error (condition-case nil
489 (apply 'idlwave-online-help mod2)
490 (error (apply 'idlwave-online-help mod3)))))
491 (if mod2
492 (condition-case nil
493 (apply 'idlwave-online-help mod1)
494 (error (apply 'idlwave-online-help mod2)))
495 (if mod1
496 (apply 'idlwave-online-help mod1)
497 (error "Don't know which item to show help for")))))))
498
499 (defun idlwave-do-mouse-completion-help (ev)
500 "Display online help on an item in the *Completions* buffer.
501 Needs additional info stored in global `idlwave-completion-help-info'."
502 (let* ((cw (selected-window))
503 (info idlwave-completion-help-info) ; global passed in
504 (what (nth 0 info))
505 (name (nth 1 info))
506 (type (nth 2 info))
507 (class (nth 3 info))
508 (need-class class)
509 (kwd (nth 4 info))
510 (sclasses (nth 5 info))
511 word link)
512 (mouse-set-point ev)
513
514
515 ;; See if we can also find help somewhere, e.g. for multiple classes
516 (setq word (idlwave-this-word))
517 (if (string= word "")
518 (error "No help item selected"))
519 (setq link (get-text-property 0 'link word))
520 (select-window cw)
521 (cond
522 ;; Routine name
523 ((memq what '(procedure function routine))
524 (setq name word)
525 (if (or (eq class t)
526 (and (stringp class) sclasses))
527 (let* ((classes (idlwave-all-method-classes
528 (idlwave-sintern-method name)
529 type)))
530 (setq link t) ; No specific link valid yet
531 (if sclasses
532 (setq classes (idlwave-members-only
533 classes (cons class sclasses))))
534 (setq class (idlwave-popup-select ev classes
535 "Select Class" 'sort))))
536
537 ;; XXX is this necessary, given all-method-classes?
538 (if (stringp class)
539 (setq class (idlwave-find-inherited-class
540 (idlwave-sintern-routine-or-method name class)
541 type (idlwave-sintern-class class)))))
542
543 ;; Keyword
544 ((eq what 'keyword)
545 (setq kwd word)
546 (if (or (eq class t)
547 (and (stringp class) sclasses))
548 (let ((classes (idlwave-all-method-keyword-classes
549 (idlwave-sintern-method name)
550 (idlwave-sintern-keyword kwd)
551 type)))
552 (setq link t) ; Link can't be correct yet
553 (if sclasses
554 (setq classes (idlwave-members-only
555 classes (cons class sclasses))))
556 (setq class (idlwave-popup-select ev classes
557 "Select Class" 'sort))
558 ;; XXX is this necessary, given all-method-keyword-classes?
559 (if (stringp class)
560 (setq class (idlwave-find-inherited-class
561 (idlwave-sintern-routine-or-method name class)
562 type (idlwave-sintern-class class)))))
563 (if (string= (downcase name) "obj_new")
564 (setq class idlwave-current-obj_new-class
565 name "Init"))))
566
567 ;; Class name
568 ((eq what 'class)
569 (setq class word
570 word nil))
571
572 ;; A special named function to call which sets some of our variables
573 ((and (symbolp what)
574 (fboundp what))
575 (funcall what 'set word))
576
577 (t (error "Cannot help with this item")))
578 (if (and need-class (not class) (not (and link (not (eq link t)))))
579 (error "Cannot help with this item"))
580 (idlwave-online-help link (or name word) type class kwd)))
581
582 (defvar idlwave-highlight-help-links-in-completion)
583 (defvar idlwave-completion-help-links)
584 (defun idlwave-highlight-linked-completions ()
585 "Highlight all completions for which help is available and attach link.
586 Those words in `idlwave-completion-help-links' have links. The
587 `idlwave-help-link-face' face is used for this."
588 (if idlwave-highlight-help-links-in-completion
589 (with-current-buffer (get-buffer "*Completions*")
590 (save-excursion
591 (let* ((case-fold-search t)
592 (props (list 'face 'idlwave-help-link-face))
593 (info idlwave-completion-help-info) ; global passed in
594 (what (nth 0 info)) ; what was completed, or a func
595 (class (nth 3 info)) ; any class
596 word beg end doit)
597 (goto-char (point-min))
598 (re-search-forward "possible completions are:" nil t)
599 (while (re-search-forward "\\s-\\([A-Za-z0-9_.]+\\)\\(\\s-\\|\\'\\)"
600 nil t)
601 (setq beg (match-beginning 1) end (match-end 1)
602 word (match-string 1) doit nil)
603 ;; Call special completion function test
604 (if (and (symbolp what)
605 (fboundp what))
606 (setq doit (funcall what 'test word))
607 ;; Look for special link property passed in help-links
608 (if idlwave-completion-help-links
609 (setq doit (assoc-ignore-case
610 word idlwave-completion-help-links))))
611 (when doit
612 (if (consp doit)
613 (setq props (append props `(link ,(cdr doit)))))
614 (let ((buffer-read-only nil))
615 (add-text-properties beg end props)))
616 (goto-char end)))))))
617
618 ;; Arrange for this function to be called after completion
619 (add-hook 'idlwave-completion-setup-hook
620 'idlwave-highlight-linked-completions)
621
622 (defvar idlwave-help-return-frame nil
623 "The frame to return to from the help frame.")
624
625 (defun idlwave-help-quit ()
626 "Exit IDLWAVE Help buffer. Kill the dedicated frame if any."
627 (interactive)
628 (cond ((and idlwave-help-use-dedicated-frame
629 (eq (selected-frame) idlwave-help-frame))
630 (if (and idlwave-experimental
631 (frame-live-p idlwave-help-return-frame))
632 ;; Try to select the return frame.
633 ;; This can crash on slow network connections, obviously when
634 ;; we kill the help frame before the return-frame is selected.
635 ;; To protect the workings, we wait for up to one second
636 ;; and check if the return-frame *is* now selected.
637 ;; This is marked "eperimental" since we are not sure when its OK.
638 (let ((maxtime 1.0) (time 0.) (step 0.1))
639 (select-frame idlwave-help-return-frame)
640 (while (and (sit-for step)
641 (not (eq (selected-frame)
642 idlwave-help-return-frame))
643 (< (setq time (+ time step)) maxtime)))))
644 (delete-frame idlwave-help-frame))
645 ((window-configuration-p idlwave-help-window-configuration)
646 (set-window-configuration idlwave-help-window-configuration)
647 (select-window (previous-window)))
648 (t (kill-buffer (idlwave-help-get-help-buffer)))))
649
650
651 (defvar default-toolbar-visible-p)
652
653 (defun idlwave-help-display-help-window (&optional pos-or-func)
654 "Display the help window.
655 Move window start to POS-OR-FUNC, if passed as a position, or call it
656 if passed as a function. See `idlwave-help-use-dedicated-frame'."
657 (let ((cw (selected-window))
658 (buf (idlwave-help-get-help-buffer)))
659 (if (and window-system idlwave-help-use-dedicated-frame)
660 (progn
661 (idlwave-help-show-help-frame)
662 (switch-to-buffer buf))
663 ;; Do it in this frame and save the window configuration
664 (if (not (get-buffer-window buf nil))
665 (setq idlwave-help-window-configuration
666 (current-window-configuration)))
667 (display-buffer buf nil (selected-frame))
668 (select-window (get-buffer-window buf)))
669 (raise-frame)
670 (if pos-or-func
671 (if (functionp pos-or-func)
672 (funcall pos-or-func)
673 (goto-char pos-or-func)
674 (recenter 0)))
675 (select-window cw)))
676
677 (defun idlwave-help-select-help-frame ()
678 "Select the help frame."
679 (if (and (frame-live-p idlwave-help-frame)
680 (not (eq (selected-frame) idlwave-help-frame)))
681 (progn
682 (setq idlwave-help-return-frame (selected-frame))
683 (select-frame idlwave-help-frame))))
684
685 (defun idlwave-help-return-to-calling-frame ()
686 "Select the frame from which the help frame was selected."
687 (interactive)
688 (if (and (frame-live-p idlwave-help-return-frame)
689 (not (eq (selected-frame) idlwave-help-return-frame)))
690 (select-frame idlwave-help-return-frame)))
691
692 (defun idlwave-online-help (link &optional name type class keyword)
693 "Display HTML or other special help on a certain topic.
694 Either loads an HTML link, if LINK is non-nil, or gets special-help on
695 the optional arguments, if any special help is defined. If LINK is
696 `t', first look up the optional arguments in the routine info list to
697 see if a link is set for it. Try extra help functions if necessary."
698 ;; Lookup link
699 (if (eq link t)
700 (let ((entry (idlwave-best-rinfo-assoc name type class
701 (idlwave-routines) nil t)))
702 (cond
703 ;; Try keyword link
704 ((and keyword
705 (setq link (cdr (idlwave-entry-find-keyword entry keyword)))))
706 ;; Default, regular entry link
707 (t (setq link (idlwave-entry-has-help entry))))))
708
709 (cond
710 ;; An explicit link
711 ((stringp link)
712 (idlwave-help-html-link link))
713
714 ;; Any extra help
715 (idlwave-extra-help-function
716 (idlwave-help-get-special-help name type class keyword))
717
718 ;; Nothing worked
719 (t (idlwave-help-error name type class keyword))))
720
721
722 (defun idlwave-help-get-special-help (name type class keyword)
723 "Call the function given by `idlwave-extra-help-function'."
724 (let* ((cw (selected-window))
725 (help-pos (save-excursion
726 (set-buffer (idlwave-help-get-help-buffer))
727 (let ((buffer-read-only nil))
728 (funcall idlwave-extra-help-function
729 name type class keyword)))))
730 (if help-pos
731 (idlwave-help-display-help-window help-pos)
732 (idlwave-help-error name type class keyword))
733 (select-window cw)))
734
735 (defun idlwave-help-html-link (link)
736 "Get html help on a given LINK."
737 (let ((browse-url-browser-function idlwave-help-browser-function)
738 (help-loc (idlwave-html-help-location))
739 (browse-url-generic-program idlwave-help-browser-generic-program)
740 ;(browse-url-generic-args idlwave-help-browser-generic-args)
741 full-link)
742
743 (if (and (memq system-type '(ms-dos windows-nt))
744 idlwave-help-use-hh)
745 (progn
746 (setq browse-url-browser-function 'browse-url-generic
747 full-link (concat (expand-file-name "idl.chm" help-loc)
748 "::/"
749 link))
750 (if (memq 'keyhh idlwave-help-use-hh)
751 (setq browse-url-generic-program "KEYHH"
752 browse-url-generic-args '("-IDLWAVE"))
753 (setq browse-url-generic-program "HH")))
754 ;; Just a regular file name (+ anchor name)
755 (unless (and (stringp help-loc)
756 (file-directory-p help-loc))
757 (error
758 "Invalid help location; customize `idlwave-html-help-location'."))
759 (setq full-link (concat
760 "file://"
761 (expand-file-name
762 link
763 (expand-file-name "idl_html_help" help-loc)))))
764
765 ;; Check for a local browser
766 (if (or idlwave-help-browser-is-local
767 (string-match "w3" (symbol-name idlwave-help-browser-function)))
768 (idlwave-help-display-help-window '(lambda () (browse-url full-link)))
769 (browse-url full-link))))
770
771 ;; A special help routine for source-level syntax help in files.
772 (defvar idlwave-help-def-pos)
773 (defvar idlwave-help-args)
774 (defvar idlwave-help-in-header)
775 (defvar idlwave-help-fontify-source-code)
776 (defvar idlwave-help-source-try-header)
777 (defun idlwave-help-with-source (name type class keyword)
778 "Provide help for routines not documented in the IDL manuals. Works
779 by loading the routine source file into the help buffer. Depending on
780 the value of `idlwave-help-source-try-header', it attempts to show the
781 routine definition or the header description. If
782 `idlwave-help-do-class-struct-tag' is non-nil, keyword is a tag to
783 show help on from the class definition structure. If
784 `idlwave-help-do-struct-tag' is non-nil, show help from the matching
785 structure tag definition.
786
787 This function can be used as `idlwave-extra-help-function'."
788 (let* ((class-struct-tag idlwave-help-do-class-struct-tag)
789 (struct-tag idlwave-help-do-struct-tag)
790 (case-fold-search t)
791 (real-class (if (consp name) (cdr name)))
792 (name (if (consp name) (car name) name))
793 (class-only (and (stringp class) (not (stringp name))))
794 file header-pos def-pos in-buf)
795 (if class-only ;Help with class? Using "Init" as source.
796 (setq name "Init"
797 type 'fun))
798 (if (not struct-tag)
799 (setq file
800 (idlwave-routine-source-file
801 (nth 3 (idlwave-best-rinfo-assoc
802 name (or type t) class (idlwave-routines))))))
803 (setq idlwave-help-def-pos nil
804 idlwave-help-args (list name type class keyword)
805 idlwave-help-in-header nil
806 idlwave-help-do-struct-tag nil
807 idlwave-help-do-class-struct-tag nil)
808 (if (or struct-tag (stringp file))
809 (progn
810 (setq in-buf ; structure-tag completion is always in current buffer
811 (if struct-tag
812 idlwave-current-tags-buffer
813 (idlwave-get-buffer-visiting file)))
814 ;; see if file is in a visited buffer, insert those contents
815 (if in-buf
816 (progn
817 (setq file (buffer-file-name in-buf))
818 (erase-buffer)
819 (insert-buffer in-buf))
820 (if (file-exists-p file) ;; otherwise just load the file
821 (progn
822 (erase-buffer)
823 (insert-file-contents file nil nil nil 'replace))
824 (idlwave-help-error name type class keyword)))
825 (if (and idlwave-help-fontify-source-code (not in-buf))
826 (idlwave-help-fontify)))
827 (idlwave-help-error name type class keyword))
828 (setq idlwave-help-mode-line-indicator file)
829
830 ;; Try to find a good place to display
831 (setq def-pos
832 ;; Find the class structure tag if that's what we're after
833 (cond
834 ;; Class structure tags: find the class or named structure
835 ;; definition
836 (class-struct-tag
837 (save-excursion
838 (setq class
839 (if (string-match "[a-zA-Z0-9]\\(__\\)" name)
840 (substring name 0 (match-beginning 1))
841 idlwave-current-tags-class))
842 (and
843 (idlwave-find-class-definition class nil real-class)
844 (idlwave-find-struct-tag keyword))))
845
846 ;; Generic structure tags: the structure definition
847 ;; location within the file has been recorded in
848 ;; `struct-tag'
849 (struct-tag
850 (save-excursion
851 (and
852 (integerp struct-tag)
853 (goto-char struct-tag)
854 (idlwave-find-struct-tag keyword))))
855
856 ;; Just find the routine definition
857 (t
858 (if class-only (point-min)
859 (idlwave-help-find-routine-definition name type class keyword))))
860 idlwave-help-def-pos def-pos)
861
862 (if (and idlwave-help-source-try-header
863 (not (or struct-tag class-struct-tag)))
864 ;; Check if we can find the header
865 (save-excursion
866 (goto-char (or def-pos (point-max)))
867 (setq header-pos (idlwave-help-find-in-doc-header
868 name type class keyword 'exact)
869 idlwave-help-in-header header-pos)))
870
871 (if (or header-pos def-pos)
872 (progn
873 (if (boundp 'idlwave-help-min-frame-width)
874 (setq idlwave-help-min-frame-width 80))
875 (goto-char (or header-pos def-pos)))
876 (idlwave-help-error name type class keyword))
877
878 (point)))
879
880
881 (defun idlwave-help-find-routine-definition (name type class keyword)
882 "Find the definition of routine CLASS::NAME in current buffer.
883 KEYWORD is ignored. Returns the point of match if successful, nil otherwise."
884 (save-excursion
885 (goto-char (point-max))
886 (if (re-search-backward
887 (concat "^[ \t]*"
888 (if (eq type 'pro) "pro"
889 (if (eq type 'fun) "function"
890 "\\(pro\\|function\\)"))
891 "[ \t]+"
892 (regexp-quote (downcase (idlwave-make-full-name class name)))
893 "[, \t\r\n]")
894 nil t)
895 (match-beginning 0)
896 nil)))
897
898 (defvar idlwave-doclib-start)
899 (defvar idlwave-doclib-end)
900 (defun idlwave-help-find-in-doc-header (name type class keyword
901 &optional exact)
902 "Find the requested help in the doc-header above point.
903
904 First checks if there is a doc-lib header which describes the correct
905 routine. Then tries to find the KEYWORDS section and the KEYWORD, if
906 given. Returns the point which should be window start of the help
907 window. If EXACT is non-nil, the full help position must be found -
908 down to the keyword requested. This setting is for context help, if
909 the exact spot is needed.
910
911 If EXACT is nil, the position of the header is returned if it
912 describes the correct routine - even if the keyword description cannot
913 be found. TYPE is ignored.
914
915 This function expects a more or less standard routine header. In
916 particlar it looks for the `NAME:' tag, either with a colon, or alone
917 on a line. Then `NAME:' must be followed by the routine name on the
918 same or the next line. When KEYWORD is non-nil, looks first for a
919 `KEYWORDS' section. It is amazing how inconsisten this is through
920 some IDL libraries I have seen. We settle for a line containing an
921 upper case \"KEYWORD\" string. If this line is not fould we search
922 for the keyword anyway to increase the hit-rate
923
924 When one of these sections exists we check for a line starting with any of
925
926 /KEYWORD KEYWORD- KEYWORD= KEYWORD
927
928 with spaces allowed between the keyword and the following dash or equal sign.
929 If there is a match, we assume it is the keyword description."
930 (let* ((case-fold-search t)
931 (rname (if (stringp class)
932 (concat
933 "\\("
934 ;; Traditional name or class::name
935 "\\("
936 "\\(" (regexp-quote (downcase class)) "::\\)?"
937 (regexp-quote (downcase name))
938 "\\>\\)"
939 (concat
940 "\\|"
941 ;; class__define or just class
942 (regexp-quote (downcase class)) "\\(__define\\)?")
943 "\\)")
944 (regexp-quote (downcase name))))
945
946 ;; NAME tag plus the routine name. The new version is from JD.
947 (name-re (concat
948 "\\(^;+\\*?[ \t]*"
949 idlwave-help-doclib-name
950 "\\([ \t]*:\\|[ \t]*$\\)[ \t]*\\(\n;+[ \t]*\\)*"
951 rname
952 "\\|"
953 "^;+[ \t]*"
954 rname
955 ":[ \t]*$\\)"))
956
957 ;; Header start plus name
958 (header-re (concat "\\(" idlwave-doclib-start "\\).*\n"
959 "\\(^;+.*\n\\)*"
960 "\\(" name-re "\\)"))
961 ;; A keywords section
962 (kwds-re (concat ; forgiving
963 "^;+\\*?[ \t]*"
964 "\\([-A-Z_ ]*"
965 idlwave-help-doclib-keyword
966 "[-A-Z_ ]*\\)"
967 "\\(:\\|[ \t]*\n\\)"))
968
969 ;; The individual keyword description line.
970 (kwd-re (if keyword ; hard (well...)
971 (concat
972 "^;+[ \t]+"
973 "\\(/" (regexp-quote (upcase keyword))
974 "\\|" (regexp-quote (upcase keyword)) "[ \t]*[-=:\n]"
975 "\\)")))
976 (kwd-re2 (if keyword ; forgiving
977 (concat
978 "^;+[ \t]+"
979 (regexp-quote (upcase keyword))
980 "\\>")))
981 dstart dend name-pos kwds-pos kwd-pos)
982 (catch 'exit
983 (save-excursion
984 (goto-char (point-min))
985 (while (and (setq dstart (re-search-forward idlwave-doclib-start nil t))
986 (setq dend (re-search-forward idlwave-doclib-end nil t)))
987 ;; found a routine header
988 (goto-char dstart)
989 (if (setq name-pos (re-search-forward name-re dend t))
990 (progn
991 (if keyword
992 ;; We do need a keyword
993 (progn
994 ;; Try to find a keyword section, but don't force it.
995 (goto-char name-pos)
996 (if (let ((case-fold-search nil))
997 (re-search-forward kwds-re dend t))
998 (setq kwds-pos (match-beginning 0)))
999 ;; Find the keyword description
1000 (if (or (let ((case-fold-search nil))
1001 (re-search-forward kwd-re dend t))
1002 (re-search-forward kwd-re dend t)
1003 (let ((case-fold-search nil))
1004 (re-search-forward kwd-re2 dend t))
1005 (re-search-forward kwd-re2 dend t))
1006 (setq kwd-pos (match-beginning 0))
1007 (if exact
1008 (progn
1009 (idlwave-help-diagnostics
1010 (format "Could not find description of kwd %s"
1011 (upcase keyword)))
1012 (throw 'exit nil))))))
1013 ;; Return the best position we got
1014 (throw 'exit (or kwd-pos kwds-pos name-pos dstart)))
1015 (goto-char dend))))
1016 (idlwave-help-diagnostics "Could not find doclib header")
1017 (throw 'exit nil))))
1018
1019 (defun idlwave-help-diagnostics (string &optional ding)
1020 "Add a diagnostics string to the list.
1021 When DING is non-nil, ring the bell as well."
1022 (if (boundp 'idlwave-help-diagnostics)
1023 (progn
1024 (setq idlwave-help-diagnostics
1025 (cons string idlwave-help-diagnostics))
1026 (if ding (ding)))))
1027
1028 (defun idlwave-help-toggle-header-top-and-def (arg)
1029 (interactive "P")
1030 (let (pos)
1031 (if idlwave-help-in-header
1032 ;; Header was the last thing displayed
1033 (progn
1034 (setq idlwave-help-in-header nil)
1035 (setq pos idlwave-help-def-pos))
1036 ;; Try to display header
1037 (setq pos (idlwave-help-find-in-doc-header
1038 (nth 0 idlwave-help-args)
1039 (nth 1 idlwave-help-args)
1040 (nth 2 idlwave-help-args)
1041 nil))
1042 (if pos
1043 (setq idlwave-help-in-header t)
1044 (error "Cannot find doclib header for routine %s"
1045 (idlwave-make-full-name (nth 2 idlwave-help-args)
1046 (nth 0 idlwave-help-args)))))
1047 (if pos
1048 (progn
1049 (goto-char pos)
1050 (recenter 0)))))
1051
1052 (defun idlwave-help-find-first-header (arg)
1053 (interactive "P")
1054 (let (pos)
1055 (save-excursion
1056 (goto-char (point-min))
1057 (if (re-search-forward idlwave-doclib-start nil t)
1058 (setq pos (match-beginning 0))))
1059 (if pos
1060 (progn
1061 (goto-char pos)
1062 (recenter 0))
1063 (error "No DocLib Header in current file"))))
1064
1065 (defun idlwave-help-find-header (arg)
1066 "Jump to the DocLib Header."
1067 (interactive "P")
1068 (if arg
1069 (idlwave-help-find-first-header nil)
1070 (setq idlwave-help-in-header nil)
1071 (idlwave-help-toggle-header-match-and-def arg 'top)))
1072
1073 (defun idlwave-help-toggle-header-match-and-def (arg &optional top)
1074 (interactive "P")
1075 (let ((args idlwave-help-args)
1076 pos)
1077 (if idlwave-help-in-header
1078 ;; Header was the last thing displayed
1079 (progn
1080 (setq idlwave-help-in-header nil)
1081 (setq pos idlwave-help-def-pos))
1082 ;; Try to display header
1083 (setq pos (apply 'idlwave-help-find-in-doc-header
1084 (if top
1085 (list (car args) (nth 1 args) (nth 2 args) nil)
1086 args)))
1087 (if pos
1088 (setq idlwave-help-in-header t)
1089 (error "Cannot find doclib header for routine %s"
1090 (idlwave-make-full-name (nth 2 idlwave-help-args)
1091 (nth 0 idlwave-help-args)))))
1092 (if pos
1093 (progn
1094 (goto-char pos)
1095 (recenter 0)))))
1096
1097 (defvar font-lock-verbose)
1098 (defvar idlwave-mode-syntax-table)
1099 (defvar idlwave-font-lock-defaults)
1100 (defun idlwave-help-fontify ()
1101 "Fontify the Help buffer as source code.
1102 Useful when source code is displayed as help. See the option
1103 `idlwave-help-fontify-source-code'."
1104 (interactive)
1105 (if (featurep 'font-lock)
1106 (let ((major-mode 'idlwave-mode)
1107 (font-lock-verbose
1108 (if (interactive-p) font-lock-verbose nil))
1109 (syntax-table (syntax-table)))
1110 (unwind-protect
1111 (progn
1112 (set-syntax-table idlwave-mode-syntax-table)
1113 (set (make-local-variable 'font-lock-defaults)
1114 idlwave-font-lock-defaults)
1115 (font-lock-fontify-buffer))
1116 (set-syntax-table syntax-table)))))
1117
1118
1119 (defun idlwave-help-error (name type class keyword)
1120 (error "Can't find help on %s%s %s"
1121 (or (and (or class name) (idlwave-make-full-name class name))
1122 "<unknown>")
1123 (if keyword (format ", keyword %s" (upcase keyword)) "")
1124 (if idlwave-html-help-location
1125 ""
1126 "(help location unknown)")))
1127
1128 (defun idlwave-help-show-help-frame ()
1129 "Show the help frame, creating it if necessary"
1130 ;; Use a special frame for this
1131 (unless (frame-live-p idlwave-help-frame)
1132 (setq idlwave-help-frame
1133 (make-frame idlwave-help-frame-parameters))
1134 ;; Strip menubar (?) and toolbar from the Help frame.
1135 (if (fboundp 'set-specifier)
1136 (progn
1137 ;; XEmacs
1138 (let ((sval (cons idlwave-help-frame nil)))
1139 ;; (set-specifier menubar-visible-p sval)
1140 (set-specifier default-toolbar-visible-p sval)))
1141 ;; Emacs
1142 (modify-frame-parameters idlwave-help-frame
1143 '(;;(menu-bar-lines . 0)
1144 (tool-bar-lines . 0)))))
1145 (select-frame idlwave-help-frame))
1146
1147 (defun idlwave-help-get-help-buffer ()
1148 "Return the IDLWAVE Help buffer. Make it first if necessary."
1149 (let ((buf (get-buffer "*IDLWAVE Help*")))
1150 (if buf
1151 nil
1152 (setq buf (get-buffer-create "*IDLWAVE Help*"))
1153 (save-excursion
1154 (set-buffer buf)
1155 (idlwave-help-mode)))
1156 buf))
1157
1158 (defun idlwave-grep (regexp list)
1159 (let (rtn)
1160 (while list
1161 (if (string-match regexp (car list))
1162 (setq rtn (cons (car list) rtn)))
1163 (setq list (cdr list)))
1164 (nreverse rtn)))
1165
1166 (defun idlwave-entry-has-help (entry)
1167 (and entry (car (nth 5 entry))))
1168
1169 (defun idlwave-has-help (name type class)
1170 "Does this have help associated with it?"
1171 (let ((entry (idlwave-best-rinfo-assoc name type class (idlwave-routines))))
1172 (idlwave-entry-has-help entry)))
1173
1174 (provide 'idlw-help)
1175 (provide 'idlwave-help)
1176
1177 ;;; idlw-help.el ends here