X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2311d8e5a62b8b6dd7dde45b4b8059d443e2abac..c1bb5ba7e161411850f704e5a2fcdc39d098948b:/lisp/org/ob-R.el diff --git a/lisp/org/ob-R.el b/lisp/org/ob-R.el index 49a8a85cf6..5bae9130c0 100644 --- a/lisp/org/ob-R.el +++ b/lisp/org/ob-R.el @@ -1,6 +1,6 @@ ;;; ob-R.el --- org-babel functions for R code evaluation -;; Copyright (C) 2009-2012 Free Software Foundation, Inc. +;; Copyright (C) 2009-2015 Free Software Foundation, Inc. ;; Author: Eric Schulte ;; Dan Davison @@ -28,9 +28,6 @@ ;;; Code: (require 'ob) -(require 'ob-ref) -(require 'ob-comint) -(require 'ob-eval) (eval-when-compile (require 'cl)) (declare-function orgtbl-to-tsv "org-table" (table params)) @@ -39,24 +36,48 @@ (declare-function ess-make-buffer-current "ext:ess-inf" ()) (declare-function ess-eval-buffer "ext:ess-inf" (vis)) (declare-function org-number-sequence "org-compat" (from &optional to inc)) - -(defconst org-babel-header-arg-names:R - '(width height bg units pointsize antialias quality compression - res type family title fonts version paper encoding - pagecentre colormodel useDingbats horizontal) +(declare-function org-remove-if-not "org" (predicate seq)) + +(defconst org-babel-header-args:R + '((width . :any) + (height . :any) + (bg . :any) + (units . :any) + (pointsize . :any) + (antialias . :any) + (quality . :any) + (compression . :any) + (res . :any) + (type . :any) + (family . :any) + (title . :any) + (fonts . :any) + (version . :any) + (paper . :any) + (encoding . :any) + (pagecentre . :any) + (colormodel . :any) + (useDingbats . :any) + (horizontal . :any) + (results . ((file list vector table scalar verbatim) + (raw org html latex code pp wrap) + (replace silent append prepend) + (output value graphics)))) "R-specific header arguments.") (defvar org-babel-default-header-args:R '()) -(defvar org-babel-R-command "R --slave --no-save" - "Name of command to use for executing R code.") +(defcustom org-babel-R-command "R --slave --no-save" + "Name of command to use for executing R code." + :group 'org-babel + :version "24.1" + :type 'string) -(defvar ess-local-process-name) +(defvar ess-local-process-name) ; dynamically scoped (defun org-babel-edit-prep:R (info) (let ((session (cdr (assoc :session (nth 2 info))))) (when (and session (string-match "^\\*\\(.+?\\)\\*$" session)) - (save-match-data (org-babel-R-initiate-session session nil)) - (setq ess-local-process-name (match-string 1 session))))) + (save-match-data (org-babel-R-initiate-session session nil))))) (defun org-babel-expand-body:R (body params &optional graphics-file) "Expand BODY according to PARAMS, return the expanded body." @@ -64,16 +85,22 @@ (or graphics-file (org-babel-R-graphical-output-file params)))) (mapconcat #'identity - ((lambda (inside) - (if graphics-file - (append - (list (org-babel-R-construct-graphics-device-call - graphics-file params)) - inside - (list "dev.off()")) - inside)) - (append (org-babel-variable-assignments:R params) - (list body))) "\n"))) + (let ((inside + (append + (when (cdr (assoc :prologue params)) + (list (cdr (assoc :prologue params)))) + (org-babel-variable-assignments:R params) + (list body) + (when (cdr (assoc :epilogue params)) + (list (cdr (assoc :epilogue params))))))) + (if graphics-file + (append + (list (org-babel-R-construct-graphics-device-call + graphics-file params)) + inside + (list "dev.off()")) + inside)) + "\n"))) (defun org-babel-execute:R (body params) "Execute a block of R code. @@ -120,7 +147,7 @@ This function is called by `org-babel-execute-src-block'." ;; helper functions (defun org-babel-variable-assignments:R (params) - "Return list of R statements assigning the block's variables" + "Return list of R statements assigning the block's variables." (let ((vars (mapcar #'cdr (org-babel-get-header params :var)))) (mapcar (lambda (pair) @@ -146,28 +173,50 @@ This function is called by `org-babel-execute-src-block'." (defun org-babel-R-assign-elisp (name value colnames-p rownames-p) "Construct R code assigning the elisp VALUE to a variable named NAME." (if (listp value) - (let ((transition-file (org-babel-temp-file "R-import-"))) - ;; ensure VALUE has an orgtbl structure (depth of at least 2) + (let* ((lengths (mapcar 'length (org-remove-if-not 'sequencep value))) + (max (if lengths (apply 'max lengths) 0)) + (min (if lengths (apply 'min lengths) 0)) + (transition-file (org-babel-temp-file "R-import-"))) + ;; Ensure VALUE has an orgtbl structure (depth of at least 2). (unless (listp (car value)) (setq value (list value))) (with-temp-file transition-file - (insert (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field))) - (insert "\n")) - (format "%s <- read.table(\"%s\", header=%s, row.names=%s, sep=\"\\t\", as.is=TRUE)" - name (org-babel-process-file-name transition-file 'noquote) - (if (or (eq (nth 1 value) 'hline) colnames-p) "TRUE" "FALSE") - (if rownames-p "1" "NULL"))) + (insert + (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field)) + "\n")) + (let ((file (org-babel-process-file-name transition-file 'noquote)) + (header (if (or (eq (nth 1 value) 'hline) colnames-p) + "TRUE" "FALSE")) + (row-names (if rownames-p "1" "NULL"))) + (if (= max min) + (format "%s <- read.table(\"%s\", + header=%s, + row.names=%s, + sep=\"\\t\", + as.is=TRUE)" name file header row-names) + (format "%s <- read.table(\"%s\", + header=%s, + row.names=%s, + sep=\"\\t\", + as.is=TRUE, + fill=TRUE, + col.names = paste(\"V\", seq_len(%d), sep =\"\"))" + name file header row-names max)))) (format "%s <- %s" name (org-babel-R-quote-tsv-field value)))) -(defvar ess-ask-for-ess-directory nil) +(defvar ess-ask-for-ess-directory) ; dynamically scoped (defun org-babel-R-initiate-session (session params) "If there is not a current R process then create one." (unless (string= session "none") (let ((session (or session "*R*")) (ess-ask-for-ess-directory - (and ess-ask-for-ess-directory (not (cdr (assoc :dir params)))))) + (and (and (boundp 'ess-ask-for-ess-directory) ess-ask-for-ess-directory) + (not (cdr (assoc :dir params)))))) (if (org-babel-comint-buffer-livep session) session (save-window-excursion + (when (get-buffer session) + ;; Session buffer exists, but with dead process + (set-buffer session)) (require 'ess) (R) (rename-buffer (if (bufferp session) @@ -177,7 +226,6 @@ This function is called by `org-babel-execute-src-block'." (buffer-name)))) (current-buffer)))))) -(defvar ess-local-process-name nil) (defun org-babel-R-associate-session (session) "Associate R code buffer with an R session. Make SESSION be the inferior ESS process associated with the @@ -191,35 +239,44 @@ current code buffer." (and (member "graphics" (cdr (assq :result-params params))) (cdr (assq :file params)))) +(defvar org-babel-R-graphics-devices + '((:bmp "bmp" "filename") + (:jpg "jpeg" "filename") + (:jpeg "jpeg" "filename") + (:tikz "tikz" "file") + (:tiff "tiff" "filename") + (:png "png" "filename") + (:svg "svg" "file") + (:pdf "pdf" "file") + (:ps "postscript" "file") + (:postscript "postscript" "file")) + "An alist mapping graphics file types to R functions. + +Each member of this list is a list with three members: +1. the file extension of the graphics file, as an elisp :keyword +2. the R graphics device function to call to generate such a file +3. the name of the argument to this function which specifies the + file to write to (typically \"file\" or \"filename\")") + (defun org-babel-R-construct-graphics-device-call (out-file params) "Construct the call to the graphics device." - (let ((devices - '((:bmp . "bmp") - (:jpg . "jpeg") - (:jpeg . "jpeg") - (:tex . "tikz") - (:tiff . "tiff") - (:png . "png") - (:svg . "svg") - (:pdf . "pdf") - (:ps . "postscript") - (:postscript . "postscript"))) - (allowed-args '(:width :height :bg :units :pointsize - :antialias :quality :compression :res - :type :family :title :fonts :version - :paper :encoding :pagecentre :colormodel - :useDingbats :horizontal)) - (device (and (string-match ".+\\.\\([^.]+\\)" out-file) - (match-string 1 out-file))) - (extra-args (cdr (assq :R-dev-args params))) filearg args) - (setq device (or (and device (cdr (assq (intern (concat ":" device)) - devices))) "png")) - (setq filearg - (if (member device '("pdf" "postscript" "svg" "tikz")) "file" "filename")) + (let* ((allowed-args '(:width :height :bg :units :pointsize + :antialias :quality :compression :res + :type :family :title :fonts :version + :paper :encoding :pagecentre :colormodel + :useDingbats :horizontal)) + (device (and (string-match ".+\\.\\([^.]+\\)" out-file) + (match-string 1 out-file))) + (device-info (or (assq (intern (concat ":" device)) + org-babel-R-graphics-devices) + (assq :png org-babel-R-graphics-devices))) + (extra-args (cdr (assq :R-dev-args params))) filearg args) + (setq device (nth 1 device-info)) + (setq filearg (nth 2 device-info)) (setq args (mapconcat (lambda (pair) (if (member (car pair) allowed-args) - (format ",%s=%s" + (format ",%s=%S" (substring (symbol-name (car pair)) 1) (cdr pair)) "")) params "")) @@ -245,7 +302,7 @@ current code buffer." (body result-type result-params column-names-p row-names-p) "Evaluate BODY in external R process. If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +string. If RESULT-TYPE equals 'value then return the value of the last statement in BODY, as elisp." (case result-type (value @@ -259,20 +316,21 @@ last statement in BODY, as elisp." (format "{function ()\n{\n%s\n}}()" body) (org-babel-process-file-name tmp-file 'noquote))) (org-babel-R-process-value-result - (if (or (member "scalar" result-params) - (member "verbatim" result-params)) - (with-temp-buffer - (insert-file-contents tmp-file) - (buffer-string)) + (org-babel-result-cond result-params + (with-temp-buffer + (insert-file-contents tmp-file) + (buffer-string)) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output (org-babel-eval org-babel-R-command body)))) +(defvar ess-eval-visibly-p) + (defun org-babel-R-evaluate-session (session body result-type result-params column-names-p row-names-p) "Evaluate BODY in SESSION. If RESULT-TYPE equals 'output then return standard output as a -string. If RESULT-TYPE equals 'value then return the value of the +string. If RESULT-TYPE equals 'value then return the value of the last statement in BODY, as elisp." (case result-type (value @@ -292,11 +350,10 @@ last statement in BODY, as elisp." "FALSE") ".Last.value" (org-babel-process-file-name tmp-file 'noquote))) (org-babel-R-process-value-result - (if (or (member "scalar" result-params) - (member "verbatim" result-params)) - (with-temp-buffer - (insert-file-contents tmp-file) - (buffer-string)) + (org-babel-result-cond result-params + (with-temp-buffer + (insert-file-contents tmp-file) + (buffer-string)) (org-babel-import-elisp-from-file tmp-file '(16))) column-names-p))) (output