Initial Commit
[packages] / xemacs-packages / ess / lisp / ess-help.el
1 ;;; ess-help.el --- Support for viewing ESS help files
2
3 ;; Copyright (C) 1989-1994 Bates, Kademan, Ritter and Smith
4 ;; Copyright (C) 1997, A.J. Rossini <rossini@stat.sc.edu>
5 ;; Copyright (C) 1998--2001 A.J. Rossini, Martin Maechler, Kurt Hornik and
6 ;;      Richard M. Heiberger <rmh@temple.edu>.
7 ;; Copyright (C) 2001--2004 A.J. Rossini, Rich M. Heiberger, Martin
8 ;;      Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
9
10 ;; Original Author: David Smith <dsmith@stats.adelaide.edu.au>
11 ;; Created: 7 Jan 1994
12 ;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>
13
14 ;; This file is part of ESS
15
16 ;; This file is free software; you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; any later version.
20
21 ;; This file is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 ;; GNU General Public License for more details.
25
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs; see the file COPYING.  If not, write to
28 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29
30 ;;; Commentary:
31
32 ;; Code for dealing with ESS help files.  See README.<LANGUAGE> where
33 ;; <LANGUAGE> is one of `S', `SAS', `Stata'or `XLispStat'.
34
35 ;;; Code:
36
37 \f ; Requires and autoloads
38
39 (eval-when-compile
40   (require 'reporter)
41   (require 'ess-inf)
42   (require 'info))
43
44 (require 'ess)
45
46 (autoload 'ess-eval-region              "ess-inf" "[autoload]" t)
47 (autoload 'ess-eval-region-and-go       "ess-inf" "[autoload]" t)
48 (autoload 'ess-eval-function            "ess-inf" "[autoload]" t)
49 (autoload 'ess-eval-function-and-go     "ess-inf" "[autoload]" t)
50 (autoload 'ess-eval-line                "ess-inf" "[autoload]" t)
51 (autoload 'ess-eval-line-and-go         "ess-inf" "[autoload]" t)
52 (autoload 'ess-eval-line-and-step       "ess-inf" "[autoload]" t)
53
54 (autoload 'ess-beginning-of-function    "ess-mode" "[autoload]" t)
55 (autoload 'ess-end-of-function          "ess-mode" "[autoload]" t)
56
57 (autoload 'ess-load-file                "ess-inf" "[autoload]" t)
58 (autoload 'ess-command                  "ess-inf" "(autoload)" nil)
59 (autoload 'ess-display-temp-buffer      "ess-inf" "(autoload)" nil)
60 (autoload 'ess-switch-to-ESS            "ess-inf" "(autoload)" nil)
61 (autoload 'ess-read-object-name-default "ess-inf" "(autoload)" nil)
62 (autoload 'ess-make-buffer-current      "ess-inf" "(autoload)" nil)
63 (autoload 'ess-search-list              "ess-inf" "(autoload)" nil)
64 (autoload 'ess-get-object-list          "ess-inf" "(autoload)" nil)
65
66 \f ; ess-help-mode
67 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
68 ;;;; In this section:
69 ;;;;
70 ;;;; * The function ess-display-help-on-object
71 ;;;; * The major mode ess-help-mode
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
73
74 (defun ess-help-bogus-buffer-p (buffer &optional nr-first return-match debug)
75   "Return non-nil if  BUFFER  looks like a bogus ESS help buffer.
76  Return pair of (match-beg. match-end) when optional RETURN-MATCH is non-nil.
77  Utility used in \\[ess-display-help-on-object]."
78   (let* ((searching nil)
79          (buffer-ok (bufferp buffer))
80          (res
81           (or (not buffer-ok)
82               (save-excursion;; ask for new buffer if old one looks bogus ..
83                 (set-buffer buffer)
84                 (if debug
85                     (ess-write-to-dribble-buffer
86                      (format "(ess-help-bogus-buffer-p %s)" (buffer-name))))
87
88                 (let ((PM (point-min)))
89                   (or  ;; evaluate up to first non-nil (or end):
90                    (< (- (point-max) PM) 80); buffer less than 80 chars
91                    (not (setq searching t))
92                    (not (setq case-fold-search t))
93                    ;; search in first nr-first (default 120) chars only
94                    (and nil (if (not nr-first) (setq nr-first 120)))
95                    (progn (goto-char PM) ;; R:
96                           (re-search-forward "Error in help"    nr-first t))
97                    (progn (goto-char PM) ;; R with help.try.all.packages = TRUE:
98                           (re-search-forward "topic \`.*\' is not in any loaded"
99                                              nr-first t))
100                    (progn (goto-char PM) ;; S-plus 5.1 :
101                           (re-search-forward "^cat: .*--"       nr-first t))
102                    (progn (goto-char PM) ;; S version 3 ; R :
103                           (re-search-forward "no documentation" nr-first t))
104                    )))
105               )))
106     (if debug
107         (ess-write-to-dribble-buffer
108          (format " |--> %s [searching %s]\n" res searching)))
109
110     (if (and res return-match searching)
111         (list (match-beginning 0) (match-end 0))
112       ;; else
113       res)))
114
115 ;;*;; Access function for displaying help
116
117 (defun ess-display-help-on-object (object)
118   "Display documentation for OBJECT in another window.
119 If prefix arg is given, forces a query of the  ESS process for the help
120 file.  Otherwise just pops to an existing buffer if it exists.
121 Uses the variable `inferior-ess-help-command' for the actual help command."
122   (interactive (ess-find-help-file "Help on: "))
123   (let* ((hb-name (concat "*help["
124                           ess-current-process-name
125                           "](" object ")*"))
126          (old-hb-p      (get-buffer hb-name))
127          (curr-win-mode major-mode)
128          (tbuffer       (get-buffer-create hb-name))
129          (curr-help-command             inferior-ess-help-command)
130          ;;-- pass the buffer-local 'ess-help-sec-..'  to the ess-help buffer:
131          (curr-help-sec-regex           ess-help-sec-regex)
132          (curr-help-sec-keys-alist      ess-help-sec-keys-alist)
133          (curr-help-syntax-table        (syntax-table))
134          (alist         ess-local-customize-alist))
135
136     (set-buffer tbuffer)
137     (ess-setq-vars-local (eval alist))
138     (setq ess-help-sec-regex      curr-help-sec-regex)
139     (setq ess-help-sec-keys-alist curr-help-sec-keys-alist)
140     ;; see above, do same for inferior-ess-help-command... (i.e. remove
141     ;; hack, restore old code :-).
142
143     (if (or (not old-hb-p)
144             current-prefix-arg
145             (ess-help-bogus-buffer-p old-hb-p nil nil 'debug)
146             )
147
148         ;; Ask the corresponding ESS process for the help file:
149         (progn
150           (if buffer-read-only (setq buffer-read-only nil))
151           (delete-region (point-min) (point-max))
152           (ess-help-mode)
153           (setq ess-local-process-name ess-current-process-name)
154           (ess-command (format curr-help-command object) tbuffer)
155           ;; was inferior-ess-help-command
156
157           (ess-help-underline)
158           ;; Stata is clean, so we get a big BARF from this.
159           (if (not (string= ess-language "STA"))
160               (ess-nuke-help-bs))
161
162           (goto-char (point-min))))
163
164     (save-excursion
165       (let ((PM (point-min))
166             (nodocs (ess-help-bogus-buffer-p (current-buffer) nil 'give-match))
167             )
168         (goto-char PM)
169         (if (and nodocs
170                  ess-help-kill-bogus-buffers)
171             (progn
172               (if (not (listp nodocs))
173                   (setq nodocs (list PM (point-max))))
174               (ess-write-to-dribble-buffer
175                (format "(ess-help: error-buffer '%s' nodocs (%d %d)\n"
176                        (buffer-name) (car nodocs) (cadr nodocs)))
177               ;; Avoid using 'message here -- may be %'s in string
178               ;;(princ (buffer-substring (car nodocs) (cadr nodocs)) t)
179               ;; MM [3/2000]: why avoid?  Yes, I *do* want message:
180               (message "%s" (buffer-substring (car nodocs) (cadr nodocs)))
181               ;; ^^^ fixme : remove new lines from the above {and abbrev.}
182               (ding)
183               (kill-buffer tbuffer))
184
185           ;; else : show it
186
187           ;;dbg (ess-write-to-dribble-buffer
188           ;;dbg  (format "(ess-help '%s' before switch-to..\n" hb-name)
189           (let ((special-display-regexps 
190                  (if ess-help-own-frame '(".") nil))
191                 (special-display-frame-alist ess-help-frame-alist)
192                 (special-display-function 
193                  (if (eq ess-help-own-frame 'one)
194                      'ess-help-own-frame
195                    special-display-function)))
196           (if (eq curr-win-mode 'ess-help-mode)
197                 (if ess-help-own-frame
198                     (pop-to-buffer tbuffer)
199                   (switch-to-buffer tbuffer))
200               (ess-display-temp-buffer tbuffer)))
201           (if curr-help-syntax-table
202               (set-syntax-table curr-help-syntax-table))
203           (set-buffer-modified-p 'nil)
204           (toggle-read-only t))))))
205
206 (defvar ess-help-frame nil
207   "Stores the frame used for displaying R help buffers.")
208
209 (defun  ess-help-own-frame (buffer &rest ignore)
210   "Put all ESS help buffers into `ess-help-frame'."
211   ;; SJE: Code adapted from Kevin Rodgers.
212   (if (frame-live-p ess-help-frame)
213       (progn
214         (or (frame-visible-p ess-help-frame)
215             (make-frame-visible ess-help-frame))
216         (raise-frame ess-help-frame)
217         (select-frame ess-help-frame)
218         (switch-to-buffer buffer)
219         (selected-window))
220     ;; else
221     (let ((window (special-display-popup-frame buffer)))
222       (set-window-dedicated-p window nil)
223       (setq ess-help-frame (window-frame window))
224       window)))
225
226
227
228 ;;; THIS WORKS!
229 ;;(require 'w3)
230 ;(defun ess-display-w3-help-on-object-other-window (object)
231 ;  "Display R-documentation for OBJECT using W3"
232 ;  (interactive "s Help on :")
233 ;  (let* ((ess-help-url (concat ess-help-w3-url-prefix
234 ;                              ess-help-w3-url-funs
235 ;                              object
236 ;                              ".html")))
237     ;;(w3-fetch-other-window ess-help-url)
238 ;    ))
239
240
241 ;;*;; Major mode definition
242
243
244 (defvar ess-help-sec-map nil "Sub-keymap for ESS help mode.")
245 (if ess-help-sec-map
246     nil
247   (setq ess-help-sec-map (make-sparse-keymap))
248   (mapcar '(lambda (key)
249             (define-key ess-help-sec-map (char-to-string key)
250               'ess-skip-to-help-section))
251             (mapcar 'car ess-help-sec-keys-alist))
252   (define-key ess-help-sec-map "?" 'ess-describe-sec-map)
253   (define-key ess-help-sec-map ">" 'end-of-buffer)
254   (define-key ess-help-sec-map "<" 'beginning-of-buffer)
255 )
256
257 (defvar ess-help-mode-map nil "Keymap for ESS help mode.")
258 (if ess-help-mode-map
259     nil
260   (setq ess-help-mode-map (make-keymap)); Full keymap, in order to
261   (suppress-keymap ess-help-mode-map)   ; suppress all usual "printing" characters
262   (define-key ess-help-mode-map " " 'scroll-up)
263   (define-key ess-help-mode-map "b" 'scroll-down)
264   (define-key ess-help-mode-map "q" 'ess-switch-to-end-of-ESS)
265   (define-key ess-help-mode-map "\C-m" 'next-line)
266   (define-key ess-help-mode-map "\177" 'scroll-down) ; DEL
267   (define-key ess-help-mode-map "s" ess-help-sec-map)
268   (define-key ess-help-mode-map "h" 'ess-display-help-on-object)
269 ;; TODO: `electric mouse-2'
270 ;; (define-key ess-help-mode-map [mouse-2] 'ess-display-help-on-object)
271   (define-key ess-help-mode-map "l" 'ess-eval-line-and-step)
272   (define-key ess-help-mode-map "r" 'ess-eval-region-and-go)
273   (define-key ess-help-mode-map "n" 'ess-skip-to-next-section)
274   (define-key ess-help-mode-map "p" 'ess-skip-to-previous-section)
275   (define-key ess-help-mode-map "/" 'isearch-forward)
276   (define-key ess-help-mode-map ">" 'end-of-buffer)
277   (define-key ess-help-mode-map "<" 'beginning-of-buffer)
278   (define-key ess-help-mode-map "x" 'ess-kill-buffer-and-go)
279   (define-key ess-help-mode-map "k" 'kill-buffer)
280   (define-key ess-help-mode-map "?" 'ess-describe-help-mode)
281   ;;-- those should be "inherited" from ess-mode-map :
282   (define-key ess-help-mode-map "\C-c\C-s" 'ess-switch-process)
283   (define-key ess-help-mode-map "\C-c\C-r" 'ess-eval-region)
284   (define-key ess-help-mode-map "\C-c\M-r" 'ess-eval-region-and-go)
285   (define-key ess-help-mode-map "\C-c\C-f" 'ess-eval-function)
286   (define-key ess-help-mode-map "\M-\C-x"  'ess-eval-function)
287   (define-key ess-help-mode-map "\C-c\M-f" 'ess-eval-function-and-go)
288   (define-key ess-help-mode-map "\C-c\C-j" 'ess-eval-line)
289   (define-key ess-help-mode-map "\C-c\M-j" 'ess-eval-line-and-go)
290   (define-key ess-help-mode-map "\M-\C-a"  'ess-beginning-of-function)
291   (define-key ess-help-mode-map "\M-\C-e"  'ess-end-of-function)
292   (define-key ess-help-mode-map "\C-c\C-y" 'ess-switch-to-ESS)
293   (define-key ess-help-mode-map "\C-c\C-z" 'ess-switch-to-end-of-ESS)
294   (define-key ess-help-mode-map "\C-c\C-l" 'ess-load-file)
295   (define-key ess-help-mode-map "\C-c\C-v" 'ess-display-help-on-object)
296   (define-key ess-help-mode-map "\C-c\C-k" 'ess-request-a-process))
297
298 ;; One reason for the following menu is to <TEACH> the user about key strokes
299 (defvar ess-help-mode-menu
300   (list "ESS-help"
301         ["Next Section"                 ess-skip-to-next-section t]
302         ["Previous Section"             ess-skip-to-previous-section t]
303         ["Search Forwards"              isearch-forward t]
304         ["Help on Section Skipping"     ess-describe-sec-map t]
305         ["Beginning of Buffer"          beginning-of-buffer t]
306         ["End of Buffer"                end-of-buffer t]
307         "-"
308         ["Help on ..."                  ess-display-help-on-object t]
309         "-"
310         ["Eval Line"                    ess-eval-line-and-step t]
311         ["Eval Region & Go"             ess-eval-region-and-go t]
312         ["Switch to ESS Process"        ess-switch-to-ESS t]
313         "-"
314         ["Describe ESS-help Mode"       ess-describe-help-mode t]
315         "-"
316         ["Kill Buffer"                  kill-buffer t]
317         ["Kill Buffer & Go"             ess-kill-buffer-and-go t]
318         ["Back to end of ESS Pr."       ess-switch-to-end-of-ESS t]
319         )
320   "Menu used in ess-help mode.")
321
322
323 (defun ess-help-mode ()
324 ;;; Largely ripped from more-mode.el,
325 ;;; originally by Wolfgang Rupprecht wolfgang@mgm.mit.edu
326   "Mode for viewing ESS help files.
327 Use SPC and DEL to page back and forth through the file.
328 Use `n'  and `p' to move to next and previous section,
329     `s' to jump to a particular section;   `s ?' for help.
330 Use `q' to return to your ESS session; `x' to kill this buffer first.
331 The usual commands for evaluating ESS source are available.
332 Other keybindings are as follows:
333 \\{ess-help-mode-map}"
334   (interactive)
335   (setq major-mode 'ess-help-mode)
336   (setq mode-name "ESS Help")
337   (use-local-map ess-help-mode-map)
338   (make-local-variable 'ess-local-process-name)
339
340   ;;; Keep <tabs> out of the code.
341   (make-local-variable 'indent-tabs-mode)
342   (setq indent-tabs-mode nil)
343
344   (require 'easymenu)
345   (easy-menu-define ess-help-mode-menu-map ess-help-mode-map
346                     "Menu keymap for ess-help mode." ess-help-mode-menu)
347   (easy-menu-add ess-help-mode-menu-map ess-help-mode-map)
348
349   (run-hooks 'ess-help-mode-hook))
350
351 ;;*;; User commands defined in ESS help mode
352
353 (defun ess-skip-to-help-section nil
354   "Jump to a section heading of a help buffer.  The section selected
355 is determined by the command letter used to invoke the command, as
356 indicated by `ess-help-sec-keys-alist'.  Use \\[ess-describe-sec-map]
357 to see which keystrokes find which sections."
358   (interactive)
359   (let ((old-point (point))
360         (case-fold-search nil))
361     (goto-char (point-min))
362     (let ((the-sec (cdr (assoc last-command-char
363                                ess-help-sec-keys-alist))))
364       (if (not the-sec) (error "Invalid section key: %c"
365                                last-command-char)
366         (if (re-search-forward (concat "^" the-sec) nil t) nil
367             (message "No %s section in this help. Sorry." the-sec)
368             (goto-char old-point))))))
369
370 (defun ess-skip-to-next-section nil
371   "Jump to next section in ESS help buffer."
372   (interactive)
373   (let ((case-fold-search nil))
374     (if (re-search-forward ess-help-sec-regex nil 'no-error) nil
375       (message "No more sections."))))
376
377 (defun ess-skip-to-previous-section nil
378   "Jump to previous section in ESS help buffer."
379   (interactive)
380   (let ((case-fold-search nil))
381     (if (re-search-backward ess-help-sec-regex nil 'no-error) nil
382       (message "No previous section."))))
383
384 (defun ess-describe-help-mode nil
385   "Display help for `ess-mode'."
386  (interactive)
387  (describe-function 'ess-help-mode))
388
389 (defun ess-kill-buffer-and-go nil
390   "Kill the current buffer and switch back to the ESS process."
391   (interactive)
392   (kill-buffer (current-buffer))
393   (ess-switch-to-ESS nil))
394
395 (defun ess-describe-sec-map nil
396   "Display help for the `s' key."
397   (interactive)
398   (describe-function 'ess-skip-to-help-section)
399   (save-excursion
400     (set-buffer "*Help*")
401     (toggle-read-only nil)
402     (goto-char (point-max))
403     (insert "\n\nCurrently defined keys are:
404
405 Keystroke    Section
406 ---------    -------\n")
407     (mapcar '(lambda (cs) (insert "    "
408                                   (car cs)
409                                   "        "
410                                   (cdr cs) "\n"))
411             ess-help-sec-keys-alist)
412     (insert "\nFull list of key definitions:\n"
413             (substitute-command-keys
414              "\\{ess-help-sec-map}"))))
415
416 (defun ess-read-helpobj-name-default (olist)
417   ;;; Returns the object name at point, or else the name of the
418   ;;; function call point is in if that has a help file. A name has a
419   ;;; help file if it is a member of olist.
420   (or (car (assoc (ess-read-object-name-default) olist))
421       (condition-case ()
422           (save-excursion
423             (save-restriction
424               (narrow-to-region (max (point-min) (- (point) 1000))
425                                 (point-max))
426               (backward-up-list 1)
427               (backward-char 1)
428               (car (assoc (ess-read-object-name-default) olist))))
429         (error nil))))
430
431 (defun ess-find-help-file (p-string)
432   "Find help, prompting for P-STRING.  Note that we can't search SAS,
433 Stata or XLispStat for additional information."
434   (ess-make-buffer-current)
435   (if (not
436        (or
437         (string-match "XLS" ess-language)
438         (string-match "STA" ess-language)
439         (string-match "SAS" ess-language)))
440       (let* ((help-files-list (or (ess-get-help-files-list)
441                                   (mapcar 'list
442                                           (ess-get-object-list
443                                            ess-current-process-name))))
444              (default (ess-read-helpobj-name-default help-files-list))
445              (prompt-string (if default
446                                 (format "%s(default %s) " p-string default)
447                               p-string))
448              (spec (completing-read prompt-string help-files-list)))
449         (list (cond
450                ((string= spec "") default)
451                (t spec))))
452     (let* ((spec (read-string p-string)))
453       (list spec))))
454
455
456 ;;*;; Utility functions
457
458 (defun ess-get-help-files-list ()
459   "Return a list of files which have help available."
460   (mapcar 'list
461           (apply 'append
462                  (mapcar '(lambda (dirname)
463                             (if (file-directory-p dirname)
464                                 (directory-files dirname)))
465                          (mapcar '(lambda (str) (concat str "/.Help"))
466                                  (ess-search-list))))))
467
468 (defun ess-nuke-help-bs ()
469   (interactive "*")
470 ;;; This function is a modification of nuke-nroff-bs in man.el from the
471 ;;; standard emacs 18 lisp library.
472   ;; Nuke underlining and overstriking (only by the same letter)
473   (goto-char (point-min))
474   (while (search-forward "\b" nil t)
475     (let* ((preceding (char-after (- (point) 2)))
476            (following (following-char)))
477       (cond ((= preceding following)
478              ;; x\bx
479              (delete-char -2))
480             ((= preceding ?\_)
481              ;; _\b
482              (delete-char -2))
483             ((= following ?\_)
484              ;; \b_
485              (delete-region (1- (point)) (1+ (point)))))))
486   ;; Crunch blank lines
487   (goto-char (point-min))
488   (while (re-search-forward "\n\n\n\n*" nil t)
489     (replace-match "\n\n"))
490   ;; Nuke blanks lines at start.
491   (goto-char (point-min))
492   (skip-chars-forward "\n")
493   (delete-region (point-min) (point)))
494
495 (defun ess-help-underline ()
496   "Replace ^_H codes with underline face."
497   (save-excursion
498     (goto-char (point-min))
499     (while (search-forward "_\b" nil t)
500     (backward-delete-char 2)
501     (put-text-property (point) (1+ (point)) 'face 'underline))))
502
503 ;;*;; Link to Info
504
505 (defun ess-goto-info (node)
506   "Display node NODE from ess-mode info."
507   (require 'info)
508   (split-window)
509   ;;(other-window 1)
510   (Info-goto-node (concat "(ess)" node)))
511
512 \f ; Bug Reporting
513
514 (defun ess-submit-bug-report ()
515   "Submit a bug report on the ess-mode package."
516   (interactive)
517   (require 'ess-mode)
518   (require 'reporter)
519   (let ((reporter-prompt-for-summary-p 't))
520     (reporter-submit-bug-report
521      "ess-bugs@stat.math.ethz.ch"
522      (concat "ess-mode " ess-version)
523      (list 'ess-language
524            'ess-dialect
525            'ess-ask-for-ess-directory
526            'ess-ask-about-transfile
527            'ess-directory
528            'ess-keep-dump-files
529            'ess-source-directory)
530      nil
531      (lambda () (goto-char (point-max)) (insert-buffer "*ESS*")))))
532
533
534 ;;; Provide
535
536 (provide 'ess-help)
537
538 \f ; Local variables section
539
540 ;;; This file is automatically placed in Outline minor mode.
541 ;;; The file is structured as follows:
542 ;;; Chapters:     ^L ;
543 ;;; Sections:    ;;*;;
544 ;;; Subsections: ;;;*;;;
545 ;;; Components:  defuns, defvars, defconsts
546 ;;;              Random code beginning with a ;;;;* comment
547
548 ;;; Local variables:
549 ;;; mode: emacs-lisp
550 ;;; mode: outline-minor
551 ;;; outline-regexp: "\^L\\|\\`;\\|;;\\*\\|;;;\\*\\|(def[cvu]\\|(setq\\|;;;;\\*"
552 ;;; End:
553
554 ;;; ess-help.el ends here