(ess-r-args-get): Do not use zap-to-char within defun,
[ess.git] / lisp / essddr.el
blobf661b633f7dfb542238b7fc4754192b5635931e1
1 ;; essddr.el --- Support for editing R documentation (Rd) source
3 ;; Copyright (C) 1997--2005 A.J. Rossini, Rich M. Heiberger, Martin
4 ;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
6 ;; Original Author: KH <Kurt.Hornik@ci.tuwien.ac.at>
7 ;; Created: 25 July 1997
8 ;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>
10 ;; This file is part of ESS (Emacs Speaks Statistics).
12 ;; This file is free software; you may redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by the
14 ;; Free Software Foundation; either version 2, or (at your option) any
15 ;; later version.
17 ;; This is distributed in the hope that it will be useful, but WITHOUT
18 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 ;; for more details.
22 ;; A copy of the GNU General Public License is available on the World
23 ;; Wide Web at http://www.gnu.org/copyleft/gpl.html. You can also
24 ;; obtain it by writing to the Free Software Foundation, Inc., 675 Mass
25 ;; Ave, Cambridge, MA 02139, USA.
27 ;;; Code:
29 ;; To stave off byte compiler errors
30 (eval-when-compile (require 'ess-help))
32 (defvar essddr-version "0.9-1"
33 "Current version of essddr.el.")
35 (defvar essddr-maintainer-address
36 "Kurt Hornik <Kurt.Hornik@R-project.org>"
37 "Current maintainer of essddr.el.")
39 (defun Rd-active-mark () nil) ;silence compiler.
40 (if (featurep 'xemacs)
41 ;; Special support for XEmacs (curtesy of auctex):
42 (defun Rd-active-mark ()
43 (and zmacs-regions (mark)))
45 ;; else: special support for GNU Emacs
46 (defun Rd-active-mark ()
47 (and transient-mark-mode mark-active))
51 (autoload 'ess-eval-region "ess-inf" "[autoload]" t)
52 (autoload 'ess-eval-line-and-step "ess-inf" "[autoload]" t)
53 (autoload 'ess-switch-process "ess-inf" "[autoload]" t)
54 (autoload 'ess-switch-to-ESS "ess-inf" "[autoload]" t)
55 (autoload 'ess-switch-to-end-of-ESS "ess-inf" "[autoload]" t)
57 (autoload 'ess-help-mode "ess-help" "[autoload]" t)
58 (autoload 'ess-nuke-help-bs "ess-help" "[autoload]" t)
60 (defvar Rd-mode-abbrev-table nil
61 "Abbrev table for R documentation keywords.
62 All Rd mode abbrevs start with a grave accent (`).")
63 (if Rd-mode-abbrev-table
65 (define-abbrev-table 'Rd-mode-abbrev-table ())
66 (define-abbrev Rd-mode-abbrev-table "`ag" "\\arguments")
67 (define-abbrev Rd-mode-abbrev-table "`al" "\\alias")
68 (define-abbrev Rd-mode-abbrev-table "`au" "\\author")
69 (define-abbrev Rd-mode-abbrev-table "`bf" "\\bold")
70 (define-abbrev Rd-mode-abbrev-table "`co" "\\code")
71 (define-abbrev Rd-mode-abbrev-table "`de" "\\describe")
72 (define-abbrev Rd-mode-abbrev-table "`dn" "\\description")
73 (define-abbrev Rd-mode-abbrev-table "`dt" "\\details")
74 (define-abbrev Rd-mode-abbrev-table "`em" "\\emph")
75 (define-abbrev Rd-mode-abbrev-table "`en" "\\enumerate")
76 (define-abbrev Rd-mode-abbrev-table "`ex" "\\examples")
77 (define-abbrev Rd-mode-abbrev-table "`fi" "\\file")
78 (define-abbrev Rd-mode-abbrev-table "`fo" "\\format")
79 (define-abbrev Rd-mode-abbrev-table "`it" "\\item")
80 (define-abbrev Rd-mode-abbrev-table "`iz" "\\itemize")
81 (define-abbrev Rd-mode-abbrev-table "`kw" "\\keyword")
82 (define-abbrev Rd-mode-abbrev-table "`li" "\\link")
83 (define-abbrev Rd-mode-abbrev-table "`me" "\\method")
84 (define-abbrev Rd-mode-abbrev-table "`na" "\\name")
85 (define-abbrev Rd-mode-abbrev-table "`no" "\\note")
86 (define-abbrev Rd-mode-abbrev-table "`re" "\\references")
87 (define-abbrev Rd-mode-abbrev-table "`sa" "\\seealso")
88 (define-abbrev Rd-mode-abbrev-table "`se" "\\section")
89 (define-abbrev Rd-mode-abbrev-table "`so" "\\source")
90 (define-abbrev Rd-mode-abbrev-table "`sy" "\\synopsis")
91 (define-abbrev Rd-mode-abbrev-table "`ta" "\\tabular")
92 (define-abbrev Rd-mode-abbrev-table "`ti" "\\title")
93 (define-abbrev Rd-mode-abbrev-table "`us" "\\usage")
94 (define-abbrev Rd-mode-abbrev-table "`va" "\\value"))
96 (defvar Rd-mode-syntax-table nil
97 "Syntax table for Rd mode.")
98 (if Rd-mode-syntax-table
100 (setq Rd-mode-syntax-table (copy-syntax-table text-mode-syntax-table))
101 (modify-syntax-entry ?\\ "\\" Rd-mode-syntax-table)
102 (modify-syntax-entry ?\{ "(}" Rd-mode-syntax-table)
103 (modify-syntax-entry ?\} "){" Rd-mode-syntax-table)
104 ;; Nice for editing, not for parsing ...
105 (modify-syntax-entry ?\( "()" Rd-mode-syntax-table)
106 (modify-syntax-entry ?\) ")(" Rd-mode-syntax-table)
107 (modify-syntax-entry ?\[ "(]" Rd-mode-syntax-table)
108 (modify-syntax-entry ?\] ")[" Rd-mode-syntax-table)
109 ;; To get strings right
110 ;; (modify-syntax-entry ?\' "\"" Rd-mode-syntax-table)
111 (modify-syntax-entry ?\" "\"" Rd-mode-syntax-table)
112 ;; To make abbrevs starting with a grave accent work ...
113 (modify-syntax-entry ?\` "w" Rd-mode-syntax-table)
114 ;; Comments
115 (modify-syntax-entry ?\% "<" Rd-mode-syntax-table)
116 (modify-syntax-entry ?\n ">" Rd-mode-syntax-table))
118 (defvar Rd-mode-parse-syntax-table nil
119 "Syntax table for parsing Rd mode.")
120 (if Rd-mode-parse-syntax-table
122 (setq Rd-mode-parse-syntax-table
123 (copy-syntax-table Rd-mode-syntax-table))
124 ;; To make parse-partial-sexps do the thing we want for computing
125 ;; indentations
126 (modify-syntax-entry ?\( "_" Rd-mode-parse-syntax-table)
127 (modify-syntax-entry ?\) "_" Rd-mode-parse-syntax-table)
128 (modify-syntax-entry ?\[ "_" Rd-mode-parse-syntax-table)
129 (modify-syntax-entry ?\] "_" Rd-mode-parse-syntax-table))
131 (defvar Rd-section-names
132 '("arguments" "alias" "author" "concept" "describe" "description"
133 "details" "docType" "encoding" "enumerate" "examples" "format"
134 "itemize" "keyword" "name" "note" "preformatted" "references"
135 "seealso" "section" "source" "synopsis" "tabular" "title" "usage"
136 "value"))
138 (defvar Rd-keywords
139 '("Alpha" "Gamma" "R" "S3method" "S4method" "acronym" "alpha" "beta"
140 "bold" "cite" "code" "command" "cr" "dQuote" "deqn" "dfn" "dontrun"
141 "dontshow" "dots" "email" "emph" "env" "epsilon" "eqn" "file" "ge"
142 "item" "kbd" "lambda" "ldots" "le" "left" "linkS4class" "link" "method" "mu"
143 "option" "pi" "pkg" "right" "sQuote" "samp" "sigma" "strong" "tab"
144 "url" "var"))
146 ;; Need to fix Rd-bold-face problem.
148 ;; (defvar Rd-bold-face 'bold)
149 ;(defvar Rd-bold-face nil)
150 ;(make-face Rd-bold-face "R documentation bold face")
151 ;(make-face-bold Rd-bold-face
153 (defvar Rd-font-lock-keywords
154 (list
155 (cons
156 (concat "\\\\\\("
157 (mapconcat 'identity Rd-section-names "\\|")
158 "\\>\\)")
159 'font-lock-reference-face) ; Rd-bold-face
160 (cons
161 (concat "\\\\\\("
162 (mapconcat 'identity Rd-keywords "\\|")
163 "\\>\\)")
164 'font-lock-keyword-face)
165 '("^#\\(ifn?def\\)\\s-+\\(\\sw+\\)"
166 (1 font-lock-builtin-face)
167 (2 font-lock-variable-name-face nil t))
168 '("^#\\(endif\\)" 1 font-lock-builtin-face))
169 "Additional Rd expressions to highlight.")
171 (defvar Rd-indent-level 2
172 "*Indentation of Rd code with respect to containing blocks.")
174 (defvar Rd-mode-map nil
175 "Keymap used in Rd mode.")
176 (if Rd-mode-map
178 (let ((map (make-sparse-keymap)))
179 (define-key map "\t" 'indent-according-to-mode)
180 (define-key map "\C-j" 'reindent-then-newline-and-indent)
181 (define-key map "\C-m" 'reindent-then-newline-and-indent)
182 (define-key map "\C-c\C-p" 'Rd-preview-help)
183 (define-key map "\C-c\C-j" 'Rd-mode-insert-item)
184 (define-key map "\C-c\C-e" 'Rd-mode-insert-skeleton)
185 (define-key map "\C-c\C-f" 'Rd-font)
186 ;; ^C^F ^E : \emph{ . }
187 ;; ^C^F ^C : \code{ . }
188 ;; ^C^F ^L : \link{ . }
189 ;; ^C^F L : \code{\link{ . }} etc
190 (define-key map "\C-c\C-s" 'Rd-mode-insert-section)
191 (define-key map "\C-c\C-n" 'ess-eval-line-and-step)
192 (define-key map "\C-c\C-r" 'ess-eval-region)
193 (define-key map "\C-c\C-c" 'ess-eval-function-or-paragraph-and-step)
194 (define-key map "\C-c\C-v" 'ess-display-help-on-object)
195 (define-key map "\C-c\C-w" 'ess-switch-process); is on C-c C-s in ess-mode..
196 (define-key map "\C-c\C-y" 'ess-switch-to-ESS)
197 (define-key map "\C-c\C-z" 'ess-switch-to-end-of-ESS)
198 (setq Rd-mode-map map)))
200 (defvar Rd-mode-menu
201 (list "Rd"
202 ["Markup [word]" Rd-font t]
203 ["Insert Item" Rd-mode-insert-item t]
204 ["Insert Section" Rd-mode-insert-section t]
205 ["Insert Skeleton" Rd-mode-insert-skeleton t]
207 ["Preview" Rd-preview-help t]
209 ["Eval Line" ess-eval-line-and-step t]
210 ["Eval Region" ess-eval-region t]
211 ["Switch to ESS Process" ess-switch-to-ESS t]
212 ["Switch the ESS Process" ess-switch-process t]
213 ["Switch to end{ESS Pr}" ess-switch-to-end-of-ESS t]
215 ["Toggle Abbrev Mode" abbrev-mode t]
216 ["Toggle Auto-Fill Mode" auto-fill-mode t]
218 ["Submit Bug Report" Rd-submit-bug-report t]
220 ["Describe Rd Mode" Rd-describe-major-mode t])
221 "Menu used in Rd mode.")
223 (defvar Rd-mode-hook nil
224 "*Hook to be run when Rd mode is entered.")
226 (defvar Rd-to-help-command "R CMD Rd2txt"
227 "*Shell command for converting R documentation source to help text.")
230 (defvar Rd-font-list
231 '((?\C-b "\\bold{" "}")
232 (?\C-c "\\code{" "}")
233 (?\C-e "\\emph{" "}")
234 (?\C-l "\\link{" "}")
235 (?l "\\code{\\link{" "}}")
236 (?\C-m "\\email{" "}")
237 (?\C-q "\\eqn{" "}")
238 (?\C-u "\\url{" "}")
240 "List of ``fonts'' used by Rd-font.
242 Each entry is a list.
243 The first element is the key to activate the font.
244 The second element is the string to insert before point, and the third
245 element is the string to insert after point."
249 ;;;###autoload
250 (defun Rd-mode ()
251 "Major mode for editing R documentation source files.
253 This mode makes it easier to write R documentation by helping with
254 indentation, doing some of the typing for you (with Abbrev mode) and by
255 showing keywords, strings, etc. in different faces (with Font Lock mode
256 on terminals that support it).
258 Type \\[list-abbrevs] to display the built-in abbrevs for Rd keywords.
260 Keybindings
261 ===========
263 \\{Rd-mode-map}
265 Variables you can use to customize Rd mode
266 ==========================================
268 `Rd-indent-level'
269 Indentation of Rd code with respect to containing blocks.
270 Default is 2.
272 Turning on Rd mode runs the hook `Rd-mode-hook'.
274 To automatically turn on the abbrev and font-lock features, add the
275 following lines to your `.emacs' file:
277 (add-hook 'Rd-mode-hook
278 (lambda ()
279 (abbrev-mode 1)
280 (if (eq window-system 'x)
281 (font-lock-mode 1))))"
283 (interactive)
284 (text-mode)
285 (kill-all-local-variables)
286 (use-local-map Rd-mode-map)
287 (setq mode-name "Rd")
288 (setq major-mode 'Rd-mode)
289 (setq local-abbrev-table Rd-mode-abbrev-table)
290 (set-syntax-table Rd-mode-syntax-table)
292 (set (make-local-variable 'indent-line-function) 'Rd-mode-indent-line)
293 (set (make-local-variable 'fill-column) 72)
294 (set (make-local-variable 'comment-start-skip) "\\s<+\\s-*")
295 (set (make-local-variable 'comment-start) "% ")
296 (set (make-local-variable 'comment-end) "")
297 (set (make-local-variable 'font-lock-defaults)
298 '(Rd-font-lock-keywords nil nil))
299 ;; (set (make-local-variable 'parse-sexp-ignore-comments) t)
301 (require 'easymenu)
302 (easy-menu-define Rd-mode-menu-map Rd-mode-map
303 "Menu keymap for Rd mode." Rd-mode-menu)
304 (easy-menu-add Rd-mode-menu-map Rd-mode-map)
306 (turn-on-auto-fill)
307 (message "Rd mode version %s" essddr-version)
308 (run-hooks 'Rd-mode-hook))
310 ;; FIXME: The following should be moved to ess-utils.el, no? (MM thinks)
311 (defun ess-point (position)
312 "Returns the value of point at certain positions."
313 (save-excursion
314 (cond
315 ((eq position 'bol) (beginning-of-line))
316 ((eq position 'eol) (end-of-line))
317 ((eq position 'boi) (back-to-indentation))
318 ((eq position 'bonl) (forward-line 1))
319 ((eq position 'bopl) (forward-line -1))
320 (t (error "unknown buffer position requested: %s" position)))
321 (point)))
323 (defun Rd-describe-major-mode ()
324 "Describe the current major mode."
325 (interactive)
326 (describe-function major-mode))
328 (defun Rd-mode-in-verbatim-p ()
329 (let ((pos (point)))
330 (save-excursion
331 (if (and (re-search-backward
332 "\\\\\\(usage\\|examples\\|synopsis\\)" nil t)
333 (re-search-forward "\\s(" nil t))
334 (condition-case ()
335 (progn
336 (up-list 1)
337 (< pos (point)))
338 (error t))
339 nil))))
341 (defun Rd-mode-in-preprocessor-line-p ()
342 (save-excursion
343 (beginning-of-line)
344 (looking-at "[ \t]*#\\(ifdef\\|endif\\)")))
346 (defun Rd-mode-calculate-indent ()
347 "Return appropriate indentation for current line in Rd mode."
348 (interactive)
349 (save-excursion
350 (beginning-of-line)
351 (cond
352 ((Rd-mode-in-verbatim-p)
353 ;; Don't do anything in verbatims
354 nil)
355 ((Rd-mode-in-preprocessor-line-p)
356 ;; Indent to 0
359 (let ((p (progn
360 (re-search-forward "[ \t]*\\s)*" (ess-point 'eol) t)
361 (point))))
362 (if (or (< (forward-line -1) 0)
363 (Rd-mode-in-verbatim-p))
365 (set-syntax-table Rd-mode-parse-syntax-table)
366 (while (and (or (looking-at "[ \t]*$")
367 (Rd-mode-in-preprocessor-line-p))
368 (not (bobp)))
369 (forward-line -1))
370 (re-search-forward "[ \t]*\\s)*" (ess-point 'eol) t)
371 (prog1
372 (+ (current-indentation)
373 (* (car (parse-partial-sexp (point) p))
374 Rd-indent-level))
375 (set-syntax-table Rd-mode-syntax-table))))))))
377 (defun Rd-mode-indent-line ()
378 "Indent current line as Rd source."
379 (interactive)
380 (let ((ic (Rd-mode-calculate-indent))
381 (rp (- (current-column) (current-indentation))))
382 (if ic ; Not inside a verbatim
383 (if (< ic 0)
384 (error "Unmatched parenthesis")
385 (indent-line-to ic)
386 (if (> rp 0)
387 (move-to-column (+ ic rp)))))))
389 (defun Rd-mode-insert-item ()
390 (interactive)
391 (reindent-then-newline-and-indent)
392 (insert "\\item{")
395 (defun Rd-mode-insert-section ()
396 (interactive)
397 (let ((s (completing-read
398 "Insert section: "
399 (mapcar '(lambda (x) (cons x x)) Rd-section-names)
400 nil t)))
401 (if (string= s "")
402 (progn (insert "\\section{}{") (backward-char 2))
403 (insert (format "\\%s{" s)))))
405 (defun Rd-mode-insert-skeleton ()
406 (interactive)
407 ;; Hmm: in theory this should be kept in sync with prompt()
408 ;; --- maybe using prompt() [or promptClass()...] would be better anyway?!
409 (insert "\\name{}\n")
410 (insert "\\alias{}\n")
411 (insert "\\title{}\n")
412 (insert "\\description{\n}\n")
413 (insert "\\usage{\n}\n")
414 (insert "\\arguments{\n}\n")
415 (insert "\\value{\n}\n")
416 (insert "\\details{\n}\n")
417 (insert "\\references{\n}\n")
418 (insert "\\seealso{\n}\n")
419 (insert "\\examples{\n}\n")
420 (insert "\\author{}\n")
421 (insert "\\keyword{}\n"))
423 ;; This is an `easy' version of (defun TeX-font ..) in AUCtex's tex.el ;
424 ;; see TeX-font-list and also LaTeX-font-list in latex.el
426 (defun Rd-font (what)
427 "Insert template for font command.
428 WHAT determines the font to use, as specified by `Rd-font-list'."
429 (interactive "c")
430 ;;TeX had : (Rd-update-style)
431 (let* ((entry (assoc what Rd-font-list))
432 (before (nth 1 entry))
433 (after (nth 2 entry)))
434 (cond ((null entry) ;; help on possibilities :
435 (let ((help
436 (concat
437 "Rd Markup (available from C-c C-f):\n\n\t"
438 "KEY Rd-Markup\n\n"
439 (mapconcat
440 '(lambda (entry)
441 ;; A textual description of an ENTRY in TeX-font-list.
442 (concat (format "%11s "
443 (key-description
444 (char-to-string (nth 0 entry))))
445 (format "%14s %-3s"
446 (nth 1 entry) (nth 2 entry))))
447 Rd-font-list "\n"))))
448 (with-output-to-temp-buffer "*Help*"
449 (set-buffer "*Help*")
450 (insert help))))
452 ((Rd-active-mark)
453 (save-excursion
454 (cond ((> (mark) (point))
455 (insert before)
456 (goto-char (mark))
457 (insert after))
459 (insert after)
460 (goto-char (mark))
461 (insert before)))))
463 (insert before)
464 (save-excursion
465 (insert after))))))
468 (defun Rd-preview-help ()
469 (interactive)
470 (require 'ess-help)
471 (let ((sbuf buffer-file-name)
472 (pbuf (get-buffer-create "R Help Preview")))
473 (set-buffer pbuf)
474 (erase-buffer)
475 (shell-command (format "%s %s" Rd-to-help-command sbuf) t)
476 (ess-nuke-help-bs)
477 (ess-help-mode)
478 (if (not (get-buffer-window pbuf 'visible))
479 (display-buffer pbuf t))))
481 ;; Bug reporting
482 (defun Rd-submit-bug-report ()
483 "Submit a bug report on Rd mode via mail."
484 (interactive)
485 (require 'reporter)
486 (and
487 (y-or-n-p "Do you want to submit a bug report? ")
488 (reporter-submit-bug-report
489 essddr-maintainer-address
490 (concat "Emacs version " emacs-version)
491 (list
492 'essddr-version
493 'Rd-indent-level))))
495 ;; Provide ourself
496 (provide 'essddr)
498 ;; essddr.el ends here