1 ;;; ess-help.el --- Support for viewing ESS help files
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.
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>
14 ;; This file is part of ESS
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)
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.
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.
32 ;; Code for dealing with ESS help files. See README.<LANGUAGE> where
33 ;; <LANGUAGE> is one of `S', `SAS', `Stata'or `XLispStat'.
37 \f ; Requires and autoloads
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)
54 (autoload 'ess-beginning-of-function "ess-mode" "[autoload]" t)
55 (autoload 'ess-end-of-function "ess-mode" "[autoload]" t)
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)
67 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
70 ;;;; * The function ess-display-help-on-object
71 ;;;; * The major mode ess-help-mode
72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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))
82 (save-excursion;; ask for new buffer if old one looks bogus ..
85 (ess-write-to-dribble-buffer
86 (format "(ess-help-bogus-buffer-p %s)" (buffer-name))))
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"
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))
107 (ess-write-to-dribble-buffer
108 (format " |--> %s [searching %s]\n" res searching)))
110 (if (and res return-match searching)
111 (list (match-beginning 0) (match-end 0))
115 ;;*;; Access function for displaying help
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
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))
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 :-).
143 (if (or (not old-hb-p)
145 (ess-help-bogus-buffer-p old-hb-p nil nil 'debug)
148 ;; Ask the corresponding ESS process for the help file:
150 (if buffer-read-only (setq buffer-read-only nil))
151 (delete-region (point-min) (point-max))
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
158 ;; Stata is clean, so we get a big BARF from this.
159 (if (not (string= ess-language "STA"))
162 (goto-char (point-min))))
165 (let ((PM (point-min))
166 (nodocs (ess-help-bogus-buffer-p (current-buffer) nil 'give-match))
170 ess-help-kill-bogus-buffers)
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.}
183 (kill-buffer tbuffer))
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)
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))))))
206 (defvar ess-help-frame nil
207 "Stores the frame used for displaying R help buffers.")
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)
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)
221 (let ((window (special-display-popup-frame buffer)))
222 (set-window-dedicated-p window nil)
223 (setq ess-help-frame (window-frame window))
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
237 ;;(w3-fetch-other-window ess-help-url)
241 ;;*;; Major mode definition
244 (defvar ess-help-sec-map nil "Sub-keymap for ESS help mode.")
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)
257 (defvar ess-help-mode-map nil "Keymap for ESS help mode.")
258 (if ess-help-mode-map
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))
298 ;; One reason for the following menu is to <TEACH> the user about key strokes
299 (defvar ess-help-mode-menu
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]
308 ["Help on ..." ess-display-help-on-object t]
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]
314 ["Describe ESS-help Mode" ess-describe-help-mode t]
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]
320 "Menu used in ess-help mode.")
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}"
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)
340 ;;; Keep <tabs> out of the code.
341 (make-local-variable 'indent-tabs-mode)
342 (setq indent-tabs-mode nil)
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)
349 (run-hooks 'ess-help-mode-hook))
351 ;;*;; User commands defined in ESS help mode
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."
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"
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))))))
370 (defun ess-skip-to-next-section nil
371 "Jump to next section in ESS help buffer."
373 (let ((case-fold-search nil))
374 (if (re-search-forward ess-help-sec-regex nil 'no-error) nil
375 (message "No more sections."))))
377 (defun ess-skip-to-previous-section nil
378 "Jump to previous section in ESS help buffer."
380 (let ((case-fold-search nil))
381 (if (re-search-backward ess-help-sec-regex nil 'no-error) nil
382 (message "No previous section."))))
384 (defun ess-describe-help-mode nil
385 "Display help for `ess-mode'."
387 (describe-function 'ess-help-mode))
389 (defun ess-kill-buffer-and-go nil
390 "Kill the current buffer and switch back to the ESS process."
392 (kill-buffer (current-buffer))
393 (ess-switch-to-ESS nil))
395 (defun ess-describe-sec-map nil
396 "Display help for the `s' key."
398 (describe-function 'ess-skip-to-help-section)
400 (set-buffer "*Help*")
401 (toggle-read-only nil)
402 (goto-char (point-max))
403 (insert "\n\nCurrently defined keys are:
406 --------- -------\n")
407 (mapcar '(lambda (cs) (insert " "
411 ess-help-sec-keys-alist)
412 (insert "\nFull list of key definitions:\n"
413 (substitute-command-keys
414 "\\{ess-help-sec-map}"))))
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))
424 (narrow-to-region (max (point-min) (- (point) 1000))
428 (car (assoc (ess-read-object-name-default) olist))))
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)
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)
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)
448 (spec (completing-read prompt-string help-files-list)))
450 ((string= spec "") default)
452 (let* ((spec (read-string p-string)))
456 ;;*;; Utility functions
458 (defun ess-get-help-files-list ()
459 "Return a list of files which have help available."
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))))))
468 (defun ess-nuke-help-bs ()
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)
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)))
495 (defun ess-help-underline ()
496 "Replace ^_H codes with underline face."
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))))
505 (defun ess-goto-info (node)
506 "Display node NODE from ess-mode info."
510 (Info-goto-node (concat "(ess)" node)))
514 (defun ess-submit-bug-report ()
515 "Submit a bug report on the ess-mode package."
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)
525 'ess-ask-for-ess-directory
526 'ess-ask-about-transfile
529 'ess-source-directory)
531 (lambda () (goto-char (point-max)) (insert-buffer "*ESS*")))))
538 \f ; Local variables section
540 ;;; This file is automatically placed in Outline minor mode.
541 ;;; The file is structured as follows:
544 ;;; Subsections: ;;;*;;;
545 ;;; Components: defuns, defvars, defconsts
546 ;;; Random code beginning with a ;;;;* comment
550 ;;; mode: outline-minor
551 ;;; outline-regexp: "\^L\\|\\`;\\|;;\\*\\|;;;\\*\\|(def[cvu]\\|(setq\\|;;;;\\*"
554 ;;; ess-help.el ends here