*** empty log message ***
[ess.git] / lisp / essddr.el
blobca8dbe57a5a7f054dbd68e8d95b1ad15cd0313a5
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" "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 : \code{\link{ . }}
189 (define-key map "\C-c\C-s" 'Rd-mode-insert-section)
190 (define-key map "\C-c\C-n" 'ess-eval-line-and-step)
191 (define-key map "\C-c\C-r" 'ess-eval-region)
192 (define-key map "\C-c\C-v" 'ess-display-help-on-object)
193 (define-key map "\C-c\C-w" 'ess-switch-process); is on C-c C-s in ess-mode..
194 (define-key map "\C-c\C-y" 'ess-switch-to-ESS)
195 (define-key map "\C-c\C-z" 'ess-switch-to-end-of-ESS)
196 (setq Rd-mode-map map)))
198 (defvar Rd-mode-menu
199 (list "Rd"
200 ["Markup [word]" Rd-font t]
201 ["Insert Item" Rd-mode-insert-item t]
202 ["Insert Section" Rd-mode-insert-section t]
203 ["Insert Skeleton" Rd-mode-insert-skeleton t]
205 ["Preview" Rd-preview-help t]
207 ["Eval Line" ess-eval-line-and-step t]
208 ["Eval Region" ess-eval-region t]
209 ["Switch to ESS Process" ess-switch-to-ESS t]
210 ["Switch the ESS Process" ess-switch-process t]
211 ["Switch to end{ESS Pr}" ess-switch-to-end-of-ESS t]
213 ["Toggle Abbrev Mode" abbrev-mode t]
214 ["Toggle Auto-Fill Mode" auto-fill-mode t]
216 ["Submit Bug Report" Rd-submit-bug-report t]
218 ["Describe Rd Mode" Rd-describe-major-mode t])
219 "Menu used in Rd mode.")
221 (defvar Rd-mode-hook nil
222 "*Hook to be run when Rd mode is entered.")
224 (defvar Rd-to-help-command "R CMD Rd2txt"
225 "*Shell command for converting R documentation source to help text.")
228 (defvar Rd-font-list
229 '((?\C-b "\\bold{" "}")
230 (?\C-c "\\code{" "}")
231 (?\C-e "\\emph{" "}")
232 (?\C-l "\\link{" "}")
233 (?l "\\code{\\link{" "}}")
234 (?\C-m "\\email{" "}")
235 (?\C-q "\\eqn{" "}")
236 (?\C-u "\\url{" "}")
238 "List of ``fonts'' used by Rd-font.
240 Each entry is a list.
241 The first element is the key to activate the font.
242 The second element is the string to insert before point, and the third
243 element is the string to insert after point."
247 ;;;###autoload
248 (defun Rd-mode ()
249 "Major mode for editing R documentation source files.
251 This mode makes it easier to write R documentation by helping with
252 indentation, doing some of the typing for you (with Abbrev mode) and by
253 showing keywords, strings, etc. in different faces (with Font Lock mode
254 on terminals that support it).
256 Type \\[list-abbrevs] to display the built-in abbrevs for Rd keywords.
258 Keybindings
259 ===========
261 \\{Rd-mode-map}
263 Variables you can use to customize Rd mode
264 ==========================================
266 `Rd-indent-level'
267 Indentation of Rd code with respect to containing blocks.
268 Default is 2.
270 Turning on Rd mode runs the hook `Rd-mode-hook'.
272 To automatically turn on the abbrev and font-lock features, add the
273 following lines to your `.emacs' file:
275 (add-hook 'Rd-mode-hook
276 (lambda ()
277 (abbrev-mode 1)
278 (if (eq window-system 'x)
279 (font-lock-mode 1))))"
281 (interactive)
282 (text-mode)
283 (kill-all-local-variables)
284 (use-local-map Rd-mode-map)
285 (setq mode-name "Rd")
286 (setq major-mode 'Rd-mode)
287 (setq local-abbrev-table Rd-mode-abbrev-table)
288 (set-syntax-table Rd-mode-syntax-table)
290 (set (make-local-variable 'indent-line-function) 'Rd-mode-indent-line)
291 (set (make-local-variable 'fill-column) 72)
292 (set (make-local-variable 'comment-start-skip) "\\s<+\\s-*")
293 (set (make-local-variable 'comment-start) "% ")
294 (set (make-local-variable 'comment-end) "")
295 (set (make-local-variable 'font-lock-defaults)
296 '(Rd-font-lock-keywords nil nil))
297 ;; (set (make-local-variable 'parse-sexp-ignore-comments) t)
299 (require 'easymenu)
300 (easy-menu-define Rd-mode-menu-map Rd-mode-map
301 "Menu keymap for Rd mode." Rd-mode-menu)
302 (easy-menu-add Rd-mode-menu-map Rd-mode-map)
304 (turn-on-auto-fill)
305 (message "Rd mode version %s" essddr-version)
306 (run-hooks 'Rd-mode-hook))
308 ;; FIXME: The following should be moved to ess-utils.el, no? (MM thinks)
309 (defun ess-point (position)
310 "Returns the value of point at certain positions."
311 (save-excursion
312 (cond
313 ((eq position 'bol) (beginning-of-line))
314 ((eq position 'eol) (end-of-line))
315 ((eq position 'boi) (back-to-indentation))
316 ((eq position 'bonl) (forward-line 1))
317 ((eq position 'bopl) (forward-line -1))
318 (t (error "unknown buffer position requested: %s" position)))
319 (point)))
321 (defun Rd-describe-major-mode ()
322 "Describe the current major mode."
323 (interactive)
324 (describe-function major-mode))
326 (defun Rd-mode-in-verbatim-p ()
327 (let ((pos (point)))
328 (save-excursion
329 (if (and (re-search-backward
330 "\\\\\\(usage\\|examples\\|synopsis\\)" nil t)
331 (re-search-forward "\\s(" nil t))
332 (condition-case ()
333 (progn
334 (up-list 1)
335 (< pos (point)))
336 (error t))
337 nil))))
339 (defun Rd-mode-in-preprocessor-line-p ()
340 (save-excursion
341 (beginning-of-line)
342 (looking-at "[ \t]*#\\(ifdef\\|endif\\)")))
344 (defun Rd-mode-calculate-indent ()
345 "Return appropriate indentation for current line in Rd mode."
346 (interactive)
347 (save-excursion
348 (beginning-of-line)
349 (cond
350 ((Rd-mode-in-verbatim-p)
351 ;; Don't do anything in verbatims
352 nil)
353 ((Rd-mode-in-preprocessor-line-p)
354 ;; Indent to 0
357 (let ((p (progn
358 (re-search-forward "[ \t]*\\s)*" (ess-point 'eol) t)
359 (point))))
360 (if (or (< (forward-line -1) 0)
361 (Rd-mode-in-verbatim-p))
363 (set-syntax-table Rd-mode-parse-syntax-table)
364 (while (and (or (looking-at "[ \t]*$")
365 (Rd-mode-in-preprocessor-line-p))
366 (not (bobp)))
367 (forward-line -1))
368 (re-search-forward "[ \t]*\\s)*" (ess-point 'eol) t)
369 (prog1
370 (+ (current-indentation)
371 (* (car (parse-partial-sexp (point) p))
372 Rd-indent-level))
373 (set-syntax-table Rd-mode-syntax-table))))))))
375 (defun Rd-mode-indent-line ()
376 "Indent current line as Rd source."
377 (interactive)
378 (let ((ic (Rd-mode-calculate-indent))
379 (rp (- (current-column) (current-indentation))))
380 (if ic ; Not inside a verbatim
381 (if (< ic 0)
382 (error "Unmatched parenthesis")
383 (indent-line-to ic)
384 (if (> rp 0)
385 (move-to-column (+ ic rp)))))))
387 (defun Rd-mode-insert-item ()
388 (interactive)
389 (reindent-then-newline-and-indent)
390 (insert "\\item{")
393 (defun Rd-mode-insert-section ()
394 (interactive)
395 (let ((s (completing-read
396 "Insert section: "
397 (mapcar '(lambda (x) (cons x x)) Rd-section-names)
398 nil t)))
399 (if (string= s "")
400 (progn (insert "\\section{}{") (backward-char 2))
401 (insert (format "\\%s{" s)))))
403 (defun Rd-mode-insert-skeleton ()
404 (interactive)
405 (insert "\\name{}\n")
406 (insert "\\alias{}\n")
407 (insert "\\title{}\n")
408 (insert "\\description{\n}\n")
409 (insert "\\usage{\n}\n")
410 (insert "\\arguments{\n}\n")
411 (insert "\\value{\n}\n")
412 (insert "\\details{\n}\n")
413 (insert "\\references{\n}\n")
414 (insert "\\seealso{\n}\n")
415 (insert "\\examples{\n}\n")
416 (insert "\\author{}\n")
417 (insert "\\keyword{}"))
419 ;; This is an `easy' version of (defun TeX-font ..) in AUCtex's tex.el ;
420 ;; see TeX-font-list and also LaTeX-font-list in latex.el
422 (defun Rd-font (what)
423 "Insert template for font command.
424 WHAT determines the font to use, as specified by `Rd-font-list'."
425 (interactive "c")
426 ;;TeX had : (Rd-update-style)
427 (let* ((entry (assoc what Rd-font-list))
428 (before (nth 1 entry))
429 (after (nth 2 entry)))
430 (cond ((null entry) ;; help on possibilities :
431 (let ((help
432 (concat
433 "Rd Markup (available from C-c C-f):\n\n\t"
434 "KEY Rd-Markup\n\n"
435 (mapconcat
436 '(lambda (entry)
437 ;; A textual description of an ENTRY in TeX-font-list.
438 (concat (format "%11s "
439 (key-description
440 (char-to-string (nth 0 entry))))
441 (format "%14s %-3s"
442 (nth 1 entry) (nth 2 entry))))
443 Rd-font-list "\n"))))
444 (with-output-to-temp-buffer "*Help*"
445 (set-buffer "*Help*")
446 (insert help))))
448 ((Rd-active-mark)
449 (save-excursion
450 (cond ((> (mark) (point))
451 (insert before)
452 (goto-char (mark))
453 (insert after))
455 (insert after)
456 (goto-char (mark))
457 (insert before)))))
459 (insert before)
460 (save-excursion
461 (insert after))))))
464 (defun Rd-preview-help ()
465 (interactive)
466 (require 'ess-help)
467 (let ((sbuf buffer-file-name)
468 (pbuf (get-buffer-create "R Help Preview")))
469 (set-buffer pbuf)
470 (erase-buffer)
471 (shell-command (format "%s %s" Rd-to-help-command sbuf) t)
472 (ess-nuke-help-bs)
473 (ess-help-mode)
474 (if (not (get-buffer-window pbuf 'visible))
475 (display-buffer pbuf t))))
477 ;; Bug reporting
478 (defun Rd-submit-bug-report ()
479 "Submit a bug report on Rd mode via mail."
480 (interactive)
481 (require 'reporter)
482 (and
483 (y-or-n-p "Do you want to submit a bug report? ")
484 (reporter-submit-bug-report
485 essddr-maintainer-address
486 (concat "Emacs version " emacs-version)
487 (list
488 'essddr-version
489 'Rd-indent-level))))
491 ;; Provide ourself
492 (provide 'essddr)
494 ;; essddr.el ends here