ESS[SAS]: somebody forgot about the SUM statement (probably me)
[ess.git] / lisp / essd-r.el
blob97e88deeca9fb83c3fd4accb7729b0eb806c1c70
1 ;;; essd-r.el --- R customization
3 ;; Copyright (C) 1997--2007 A.J. Rossini, Rich M. Heiberger, Martin
4 ;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
6 ;; Original Author: A.J. Rossini
7 ;; Created: 12 Jun 1997
8 ;; Maintainers: ESS-core <ESS-core@stat.math.ethz.ch>
10 ;; Keywords: start up, configuration.
12 ;; This file is part of ESS.
14 ;; This file is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; any later version.
19 ;; This file is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to
26 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28 ;;; Commentary:
29 ;;; This file defines all the R customizations for ESS. See essl-s.el
30 ;;; for general S language customizations.
32 ;;; Autoloads and Requires
34 (ess-message "[essd-r:] (require 'essl-s)")
35 (require 'essl-s)
37 (require 'essd-r-args); for now
39 ;; modify S Syntax table:
40 (setq R-syntax-table S-syntax-table)
42 ;; In R 2.x, back tick now is a quote character, so lets tell Emacs
43 ;; that it is; the problem below for older R should no longer be a
44 ;; serious issue.
45 ;;R >= 1.8: back tick `string` -- unfortunately no *pair* checking:
46 ;; breaks when things like `..' are used:
47 (modify-syntax-entry ?` "\"" R-syntax-table)
48 (modify-syntax-entry ?_ "_" R-syntax-table) ; foo_bar is symbol in R >=1.9
50 (ess-message "[essd-r:] (autoload ..) & (def** ..)")
52 (autoload 'inferior-ess "ess-inf" "Run an ESS process.")
53 (autoload 'ess-mode "ess-mode" "Edit an ESS process.")
55 ;;; Code:
57 (defvar R-customize-alist
58 (append
59 '((ess-local-customize-alist . 'R-customize-alist)
60 (ess-dialect . "R")
61 (ess-suffix . "R")
62 (ess-dump-filename-template . (ess-replace-regexp-in-string
63 "S$" ess-suffix ; in the one from custom:
64 ess-dump-filename-template-proto))
65 (ess-mode-syntax-table . R-syntax-table)
66 (ess-mode-editing-alist . R-editing-alist)
67 (ess-change-sp-regexp . ess-R-change-sp-regexp)
68 (ess-help-sec-regex . ess-help-R-sec-regex)
69 (ess-help-sec-keys-alist . ess-help-R-sec-keys-alist)
70 (ess-loop-timeout . ess-S-loop-timeout);fixme: dialect spec.
71 (ess-cmd-delay . ess-R-cmd-delay)
72 (ess-function-pattern . ess-R-function-pattern)
73 (ess-object-name-db-file . "ess-r-namedb.el" )
74 (ess-imenu-mode-function . 'ess-imenu-R)
75 (inferior-ess-program . inferior-R-program-name)
76 (inferior-ess-objects-command . inferior-R-objects-command)
77 (inferior-ess-font-lock-keywords . inferior-ess-R-font-lock-keywords)
78 (inferior-ess-search-list-command . "search()\n")
79 (inferior-ess-help-command . "help(\"%s\", htmlhelp=FALSE)\n")
80 (inferior-ess-help-filetype . nil)
81 (inferior-ess-exit-command . "q()")
82 (inferior-ess-exit-prompt . "Save workspace image? [y/n/c]: ")
83 (inferior-ess-primary-prompt . "\\([A-Z][][A-Za-z0-9.]*\\)?> ")
84 (inferior-ess-secondary-prompt . "+ ?")
85 ;;harmful for shell-mode's C-a: -- but "necessary" for ESS-help?
86 (inferior-ess-start-file . nil) ;; "~/.ess-R"
87 (inferior-ess-start-args . "")
88 (ess-STERM . "iESS")
89 (ess-editor . R-editor)
90 (ess-pager . R-pager)
92 S-common-cust-alist)
93 "Variables to customize for R -- set up later than emacs initialization.")
95 (defvar ess-r-versions '("R-1" "R-2" "R-devel" "R-patched")
96 "List of partial strings for versions of R to access within ESS.
97 Each string specifies the start of a filename. If a filename
98 beginning with one of these strings is found on `exec-path', a M-x
99 command for that version of R is made available. For example, if the
100 file \"R-1.8.1\" is found and this variable includes the string
101 \"R-1\", a function called `M-x R-1.8.1' will be available to run that
102 version of R.
103 If duplicate versions of the same program are found (which happens if
104 the same path is listed on `exec-path' more than once), they are
105 ignored by calling `ess-uniq-list'.
106 Set this variable to nil to disable searching for other versions of R.
107 If you set this variable, you need to restart Emacs (and set this variable
108 before ess-site is loaded) for it to take effect.")
110 ;;;### autoload
111 (defun R (&optional start-args)
112 "Call 'R', the 'GNU S' system from the R Foundation.
113 Optional prefix (C-u) allows to set command line arguments, such as
114 --vsize. This should be OS agnostic.
115 If you have certain command line arguments that should always be passed
116 to R, put them in the variable `inferior-R-args'."
117 (interactive "P")
118 (setq ess-customize-alist R-customize-alist)
119 (ess-write-to-dribble-buffer ;; for debugging only
120 (format
121 "\n(R): ess-dialect=%s, buf=%s, start-arg=%s\n current-prefix-arg=%s\n"
122 ess-dialect (current-buffer) start-args current-prefix-arg))
123 (let* ((r-always-arg
124 (if (or ess-microsoft-p (eq system-type 'cygwin))
125 "--ess "
126 "--no-readline "))
127 (r-start-args
128 (concat r-always-arg
129 inferior-R-args " " ; add space just in case
130 (if start-args
131 (read-string
132 (concat "Starting Args [other than `"
133 r-always-arg
134 "'] ? "))
135 nil)))
137 ;;Micro$ ?: default-process-coding-system ;-breaks UTF locales on Unix:
138 (if ess-microsoft-p
139 (setq default-process-coding-system '(undecided-dos . undecided-dos)))
140 (inferior-ess r-start-args) ;; (R)
141 (ess-write-to-dribble-buffer
142 (format "(R): inferior-ess-language-start=%s\n"
143 inferior-ess-language-start))
144 ;; currently rely on baseenv() which is in R only since version 2.2:
145 (ess-eval-linewise
146 "if(!exists(\"baseenv\", mode=\"function\")) baseenv <- function() NULL"
147 nil nil nil 'wait-prompt);; solving "lines running together"
148 (if inferior-ess-language-start
149 (ess-eval-linewise inferior-ess-language-start
150 nil nil nil 'wait-prompt))))
152 ;;;### autoload
153 (defun R-mode (&optional proc-name)
154 "Major mode for editing R source. See `ess-mode' for more help."
155 (interactive)
156 (setq ess-customize-alist R-customize-alist)
157 ;;(setq imenu-generic-expression R-imenu-generic-expression)
158 (ess-mode R-customize-alist proc-name)
159 (if (fboundp 'ess-add-toolbar) (ess-add-toolbar))
160 ;; ECB needs seminatic stuff.
161 ;; (if (featurep 'semantic)
162 ;; (setq semantic-toplevel-bovine-table r-toplevel-bovine-table))
163 (if ess-imenu-use-S
164 (progn (require 'ess-menu)
165 (ess-imenu-R)))
166 ;; MM: ^^^^^^^^^^^ should really use ess-imenu-mode-function from the
167 ;; alist above!
171 (fset 'r-mode 'R-mode)
173 (defun ess-r-versions-create ()
174 "Generate the `M-x R-x.y' functions for starting other versions of R.
175 On MS Windows, this works using `ess-rterm-version-paths'; otherwise,
176 see `ess-r-versions' for strings that determine which functions are created.
178 The result is a list of the new R defuns, if any, that were created. The
179 defuns will normally be placed on the menubar and stored as
180 `ess-r-versions-created' upon ESS initialisation."
182 (if (not ess-r-versions)
183 nil ;nothing to return
184 ;; else, if ess-r-versions is non-nil, let's try to find those R versions.
185 ;; This works by creating a temp buffer where the template function is
186 ;; edited so that X.Y is replaced by the version name
187 (let ((versions)
188 (eval-buf (get-buffer-create "*ess-temp-r-evals*"))
189 (r-versions-created)
190 (template
191 ;; This is the template function used for creating M-x R-X.Y.
192 (concat
193 "(defun R-X.Y (&optional start-args)
194 \"Call R-X.Y, i.e., the R version 'R-X.Y' using ESS.
195 This function was generated by `ess-r-versions-create'.\"
196 (interactive \"P\")
197 (let ((use-dialog-box nil);; Win-only: dialog box won't return a directory
198 (inferior-R-program-name \""
199 (if ess-microsoft-p "Rterm" "R") "-X.Y\"))
200 (R start-args)))
202 ) ))
204 (save-excursion
205 (set-buffer eval-buf)
206 ;; clear the buffer.
207 (delete-region (point-min) (point-max))
209 ;; Find which versions of R we want. Remove the pathname, leaving just
210 ;; the name of the executable.
211 (setq versions
212 (if ess-microsoft-p ess-rterm-version-paths
213 ;; ^^^^^^^^^^^^^^^^^^^^^^^ created in
214 ;; ./ess-site.el at start
215 ;; else (non-MS):
216 (ess-uniq-list
217 (mapcar 'file-name-nondirectory
218 (apply 'nconc
219 (mapcar 'ess-find-exec-completions
220 ess-r-versions))))))
221 (ess-write-to-dribble-buffer
222 (format "(R): ess-r-versions-create making M-x defuns for \n %s\n"
223 (mapconcat 'identity versions "\n ")))
224 (if (not ess-microsoft-p)
225 (setq r-versions-created versions)) ;keep copy for returning at end.
227 ;; Iterate over each string in VERSIONS, creating a new defun each time.
228 (while versions
229 (let* ((version (car versions))
230 (this-version version)
231 (beg (point)))
233 (setq versions (cdr versions))
234 (when ess-microsoft-p
235 (setq
236 version (file-name-nondirectory
237 (ess-chop1 (file-name-directory
238 (ess-chop1 (file-name-directory version)))))
239 r-versions-created (cons version r-versions-created)))
241 (insert template)
242 (goto-char beg)
243 (while (search-forward "R-X.Y" nil t)
244 (replace-match version t t))
245 (when ess-microsoft-p
246 (goto-char beg)
247 (while (search-forward "Rterm-X.Y" nil t)
248 (replace-match this-version t t)))
249 (goto-char (point-max)))
251 ;; buffer has now been created with defuns, so eval them!
252 (eval-buffer)
253 (kill-buffer eval-buf)
255 r-versions-created)))
257 (defvar ess-newest-R nil
258 "Stores the newest version of R that has been found. Used as a cache,
259 within ess-find-newest-R. Do not use this value directly, but
260 instead call the function \\[ess-find-newest-R].")
262 (defun ess-find-newest-R ()
263 "Find the newest version of R on the system. Once the value is found,
264 cache it in the variable `ess-newest-R' for future use as finding the
265 newest version of R can be potentially time-consuming."
266 (or ess-newest-R
267 (progn (message "Finding all versions of R on your system...")
268 ;;(sleep-for 3)
269 nil)
270 (setq ess-newest-R
271 (ess-newest-r
272 (if ess-microsoft-p
273 ess-rterm-version-paths
274 (add-to-list 'ess-r-versions-created
275 inferior-R-program-name))))))
277 (defun ess-check-R-program-name ()
278 "Check if `inferior-R-program-name' points to an executable version of R.
279 If not, try to find the newest version of R elsewhere on the system, and
280 update `inferior-R-program-name' accordingly."
281 (unless (executable-find inferior-R-program-name)
282 ;; need to check if we can find another name.
283 (let ((newest (ess-find-newest-R)))
284 (if newest
285 (setq inferior-R-program-name newest)
286 (message "Sorry, no version of R could be found on your system.")))))
288 (defun R-newest (&optional start-args)
289 "Find the newest version of R available, and run it.
290 Subsequent calls to R-newest will run that version, rather than searching
291 again for the newest version. Providing an optional prefix arg (C-u) will
292 prompt for command line arguments."
293 (interactive "P")
294 (let ((rnewest (ess-find-newest-R)))
295 (if (not rnewest)
296 (error "No version of R could be found.")
297 ;; Else: we have a working version of R.
298 ;; Have to be careful to avoid recursion...
299 (message (concat "Newest version of R is " rnewest))
300 (fset 'R-newest
301 (intern
302 (if ess-microsoft-p
303 (file-name-nondirectory
304 (substring (file-name-directory
305 (substring (file-name-directory rnewest) 0 -1))
306 0 -1))
307 rnewest)))
308 ;;(fset 'R-newest (intern rnewest))
309 (R-newest start-args))))
311 ;; (ess-r-version-date "R-2.5.1") (ess-r-version-date "R-patched")
312 ;; (ess-r-version-date "R-1.2.1") (ess-r-version-date "R-1.8.1")
313 ;; Note that for R-devel, ver-string is something like
314 ;; R version 2.6.0 Under development (unstable) (2007-07-14 r42234)
315 ;; Antique examples are 'R 1.0.1 (April 14, 2000)' or 'R 1.5.1 (2002-06-17).'
316 (defun ess-r-version-date (rver)
317 "Return the date of the version of R named RVER.
318 The date is returned as a date string. If the version of R could
319 not be found from the output of the RVER program, \"-1\" is
320 returned."
321 (let (ver-string
322 (date "-1"))
323 (setq ver-string (shell-command-to-string
324 (concat rver " --version")))
325 (when (string-match
326 "R \\(version \\)?[1-9][^\n]+ (\\(2[0-9-]+\\)\\( r[0-9]+\\)?)"
327 ver-string)
328 (setq date (match-string 2 ver-string)))
329 (cons date rver)))
331 (defun ess-newest-r (rvers)
332 "Check all the versions of RVERS to see which is the newest.
333 Return the name of the newest version of R."
334 (let ((rtimes (mapcar 'ess-r-version-date rvers)))
335 ;; SJE: 2007-07-13 -- following line is a temp var to check that
336 ;; the newest version of R is found correctly.
337 (setq ess-temp-newest rtimes)
338 (ess-find-newest-date rtimes)))
340 ;; Test case for following defun:
341 ;; (setq a '( ("2003-10-04" . "R-1.7")
342 ;; ("2006-11-19" . "R-2.2")
343 ;; ("2007-07-01" . "R-dev")
344 ;; ("-1" . "R-broken")
345 ;; ("2005-12-30" . "R-2.0")))
346 ;; (ess-find-newest-date a)
347 (defun ess-find-newest-date (rvers)
348 "Find the newest version of R given in the a-list RVERS.
349 Each element of RVERS is a dotted pair (date . R-version), where
350 date is given as e.g.\"2007-11-30\" so that we can compare dates
351 as strings. If a date is listed as \"-1\", that version of R
352 could not be found.
354 If the value returned is nil, no valid newest version of R could be found."
355 (let (new-r this-r
356 (new-time "0"))
357 (while rvers
358 (setq this-r (car rvers)
359 rvers (cdr rvers))
360 (when (string< new-time (car this-r))
361 (setq new-time (car this-r)
362 new-r (cdr this-r))))
363 new-r))
366 (defun ess-find-rterm (&optional ess-R-root-dir)
367 "Find the full path of all occurences of Rterm.exe under the ESS-R-ROOT-DIR.
368 If ESS-R-ROOT-DIR is nil, construct it by looking for an occurence of Rterm.exe
369 in the exec-path. If there are no occurences of Rterm.exe in the exec-path,
370 then use `ess-program-files' (which evaluates to something like \"c:/progra~1/R/\"
371 in English locales) which is the default location for the R distribution."
372 (if (not ess-R-root-dir)
373 (let ((Rpath (executable-find "Rterm")))
374 (setq ess-R-root-dir
375 (expand-file-name
376 (if Rpath
377 (concat (file-name-directory Rpath) "../../")
378 (concat ess-program-files "/R/"))))
379 (ess-write-to-dribble-buffer
380 (format "(ess-find-rterm): ess-R-root-dir = '%s'\n" ess-R-root-dir))
383 (when (file-directory-p ess-R-root-dir) ; otherwise file-name-all-.. errors
384 (setq ess-R-root-dir
385 (ess-replace-regexp-in-string "[\\]" "/" ess-R-root-dir))
386 (let ((R-ver
387 (ess-drop-non-directories
388 (ess-flatten-list
389 (mapcar '(lambda (r-prefix)
390 (file-name-all-completions r-prefix ess-R-root-dir))
391 (append '("rw") ess-r-versions))))))
392 (mapcar '(lambda (dir)
393 (concat ess-R-root-dir
394 (ess-replace-regexp-in-string "[\\]" "/" dir)
395 "bin/Rterm.exe"))
396 R-ver))))
399 ;;;### autoload
400 (defun Rnw-mode ()
401 "Major mode for editing Sweave(R) source.
402 See `noweb-mode' and `R-mode' for more help."
403 (interactive)
404 (require 'ess-noweb);; << probably someplace else
405 (noweb-mode 1); turn it on
406 (noweb-set-doc-mode 'latex-mode)
407 (noweb-set-code-mode 'R-mode)
408 (run-hooks 'Rnw-mode-hook))
410 (fset 'Snw-mode 'Rnw-mode); just a synonym (for now or ever)
413 (autoload 'ess-transcript-mode "ess-trns"
414 "Major mode for editing S transcript files." t)
416 (defun R-transcript-mode ()
417 "Does the right thing."
418 (interactive)
419 (ess-transcript-mode R-customize-alist))
421 (fset 'r-transcript-mode 'R-transcript-mode)
423 (defun R-fix-T-F (&optional from quietly)
424 "Fix T/F into TRUE and FALSE *cautiously*, i.e. not in comments and strings;
425 starting from the current position (point)."
426 (interactive "d\nP"); point and prefix (C-u)
427 (save-excursion
428 (goto-char from)
429 (ess-rep-regexp "\\(\\([][=,()]\\|<-\\|_\\) *\\)T\\>" "\\1TRUE"
430 'fixcase nil (not quietly))
431 (goto-char from)
432 (ess-rep-regexp "\\(\\([][=,()]\\|<-\\|_\\\) *\\)F\\>" "\\1FALSE"
433 'fixcase nil (not quietly))))
435 ;; From: Sebastian Luque <spluque@gmail.com>
436 ;; To: ess-help@stat.math.ethz.ch
437 ;; Date: Mon, 01 May 2006 19:17:49 -0500
439 ;; Without knowing how to tell R to use w3m from within Emacs, and after
440 ;; switching to Konqueror's window for the millionth time, I wrote the
441 ;; following function:
443 ;; This emulates some of the functionality of RSiteSearch() and tests ok in
444 ;; my system GNU Emacs 22.0.50.1 (i486-pc-linux-gnu, X toolkit, Xaw3d scroll
445 ;; bars) of 2006-04-27 on pacem, modified by Debian. This has the benefit of
446 ;; displaying results with whatever you've told browse-url to use; in my
447 ;; case, w3m with the emacs-w3m package.
449 ;; My elisp skills are rather poor, so comments and suggestions for
450 ;; improvement are welcome.
451 ;; --
452 ;; Seb
455 ;; MM _FIXME_: This only works correctly for Emacs 22.0.50 (alpha)
456 ;; for 21.x it has problems in the (completing-read-multiple .)
457 ;; at the end
458 (defun R-site-search (string)
459 "Search the R archives for STRING, using default criteria. If
460 called with a prefix, options are available for
461 1) matches per page,
462 2) sections of the archives to search (separated by value of `crm-default-separator'),
463 3) for displaying results in long or short formats, and
464 4) for sorting by any given field.
465 Completion is available for supplying options."
466 (interactive "sSearch string: ")
467 (let ((site "http://search.r-project.org/cgi-bin/namazu.cgi?query=")
468 (okstring (replace-regexp-in-string " +" "+" string)))
469 (if current-prefix-arg
470 (let ((mpp (concat
471 "&max="
472 (completing-read
473 "Matches per page: "
474 '(("20" 1) ("30" 2) ("40" 3) ("50" 4) ("100" 5)))))
475 (format (concat
476 "&result="
477 (completing-read
478 "Format: " '("normal" "short")
479 nil t "normal" nil "normal")))
480 (sortby (concat
481 "&sort="
482 (completing-read
483 "Sort by: "
484 '(("score" 1) ("date:late" 2) ("date:early" 3)
485 ("field:subject:ascending" 4)
486 ("field:subject:decending" 5)
487 ("field:from:ascending" 6) ("field:from:decending" 7)
488 ("field:size:ascending" 8) ("field:size:decending" 9))
489 nil t "score" nil "score")))
490 (restrict (concat
491 "&idxname="
492 (mapconcat
493 'identity
494 (completing-read-multiple
495 "Limit search to: "
496 '(("Rhelp02a" 1) ("functions" 2) ("docs" 3)
497 ("Rhelp01" 4))
498 nil t "Rhelp02a,functions,docs" nil
499 "Rhelp02a,functions,docs") "&idxname="))))
500 (browse-url (concat site okstring mpp format sortby restrict)))
501 ;; else: without prefix use defaults:
502 (browse-url (concat site okstring "&max=20&result=normal&sort=score"
503 "&idxname=Rhelp02a&idxname=functions&idxname=docs")))))
506 \f ; provides
508 (provide 'essd-r)
510 \f ; Local variables section
512 ;;; This file is automatically placed in Outline minor mode.
513 ;;; The file is structured as follows:
514 ;;; Chapters: ^L ;
515 ;;; Sections: ;;*;;
516 ;;; Subsections: ;;;*;;;
517 ;;; Components: defuns, defvars, defconsts
518 ;;; Random code beginning with a ;;;;* comment
520 ;;; Local variables:
521 ;;; mode: emacs-lisp
522 ;;; outline-minor-mode: nil
523 ;;; mode: outline-minor
524 ;;; outline-regexp: "\^L\\|\\`;\\|;;\\*\\|;;;\\*\\|(def[cvu]\\|(setq\\|;;;;\\*"
525 ;;; End:
527 ;;; essd-r.el ends here