initial commit

This commit is contained in:
Eric Schulte 2009-02-05 15:04:09 -08:00
commit a3cf89b5b2
9 changed files with 1713 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

9
README.markdown Normal file
View File

@ -0,0 +1,9 @@
rorg --- R and org-mode
======================
* for information on R see the [R website](http://www.r-project.org/)
* for information on org see the [Org-Mode website](http://org-mode.org)
Probably would be good to place the main objectives here, once they
are ironed out.

View File

@ -0,0 +1,358 @@
require(utils)
RweaveOrg = function () {
list(setup = RweaveOrgSetup, runcode = RweaveOrgRuncode,
writedoc = RweaveOrgWritedoc, finish = RweaveOrgFinish,
checkopts = RweaveOrgOptions)
}
RweaveOrgSetup <-
function (file, syntax, output = NULL, quiet = FALSE, debug = FALSE,
echo = TRUE, eval = TRUE, keep.source = FALSE, split = FALSE,
stylepath, pdf = TRUE, eps = TRUE) {
if (is.null(output)) {
prefix.string <- basename(sub(syntax$extension, "", file))
output <- paste(prefix.string, "org", sep = ".")
}
else prefix.string <- basename(sub("\\.org$", "", output))
if (!quiet)
cat("Writing to file ", output, "\n", "Processing code chunks ...\n",
sep = "")
output <- file(output, open = "w+")
if (missing(stylepath)) {
p <- as.vector(Sys.getenv("SWEAVE_STYLEPATH_DEFAULT"))
stylepath <- if (length(p) >= 1 && nzchar(p[1]))
identical(p, "TRUE")
else TRUE
}
if (stylepath) {
styfile <- file.path(R.home("share"), "texmf", "Sweave")
if (.Platform$OS.type == "windows")
styfile <- gsub("\\\\", "/", styfile)
if (length(grep(" ", styfile)))
warning(gettextf("path to '%s' contains spaces,\n",
styfile), gettext("this may cause problems when running LaTeX"),
domain = NA)
}
else styfile <- "Sweave"
options <- list(prefix = TRUE, prefix.string = prefix.string,
engine = "R", print = FALSE, eval = eval, fig = FALSE,
pdf = pdf, eps = eps, width = 6, height = 6, term = TRUE,
echo = echo, keep.source = keep.source, results = "verbatim",
split = split, strip.white = "true", include = TRUE,
pdf.version = "1.1", pdf.encoding = "default", concordance = FALSE,
expand = TRUE)
options <- RweaveOrgOptions(options)
list(output = output, styfile = styfile, havesty = FALSE,
haveconcordance = FALSE, debug = debug, quiet = quiet,
syntax = syntax, options = options, chunkout = list(),
srclines = integer(0), srcfile = srcfile(file))
}
makeRweaveOrgCodeRunner <-
function (evalFunc = RweaveEvalWithOpt)
{
RweaveOrgRuncode <- function(object, chunk, options) {
if (!(options$engine %in% c("R", "S"))) {
return(object)
}
if (!object$quiet) {
cat(formatC(options$chunknr, width = 2), ":")
if (options$echo)
cat(" echo")
if (options$keep.source)
cat(" keep.source")
if (options$eval) {
if (options$print)
cat(" print")
if (options$term)
cat(" term")
cat("", options$results)
if (options$fig) {
if (options$eps)
cat(" eps")
if (options$pdf)
cat(" pdf")
}
}
if (!is.null(options$label))
cat(" (label=", options$label, ")", sep = "")
cat("\n")
}
chunkprefix <- RweaveChunkPrefix(options)
if (options$split) {
chunkout <- object$chunkout[chunkprefix][[1]]
if (is.null(chunkout)) {
chunkout <- file(paste(chunkprefix, "tex", sep = "."),
"w")
if (!is.null(options$label))
object$chunkout[[chunkprefix]] <- chunkout
}
}
else chunkout <- object$output
saveopts <- options(keep.source = options$keep.source)
on.exit(options(saveopts))
SweaveHooks(options, run = TRUE)
chunkexps <- try(parse(text = chunk), silent = TRUE)
RweaveTryStop(chunkexps, options)
openSinput <- FALSE
openSchunk <- FALSE
if (length(chunkexps) == 0)
return(object)
srclines <- attr(chunk, "srclines")
linesout <- integer(0)
srcline <- srclines[1]
srcrefs <- attr(chunkexps, "srcref")
if (options$expand)
lastshown <- 0
else lastshown <- srcline - 1
thisline <- 0
for (nce in 1:length(chunkexps)) {
ce <- chunkexps[[nce]]
if (nce <= length(srcrefs) && !is.null(srcref <- srcrefs[[nce]])) {
if (options$expand) {
srcfile <- attr(srcref, "srcfile")
showfrom <- srcref[1]
showto <- srcref[3]
}
else {
srcfile <- object$srcfile
showfrom <- srclines[srcref[1]]
showto <- srclines[srcref[3]]
}
dce <- getSrcLines(srcfile, lastshown + 1, showto)
leading <- showfrom - lastshown
lastshown <- showto
srcline <- srclines[srcref[3]]
while (length(dce) && length(grep("^[[:blank:]]*$",
dce[1]))) {
dce <- dce[-1]
leading <- leading - 1
}
}
else {
dce <- deparse(ce, width.cutoff = 0.75 * getOption("width"))
leading <- 1
}
if (object$debug)
cat("\nRnw> ", paste(dce, collapse = "\n+ "),
"\n")
if (options$echo && length(dce)) {
if (!openSinput) {
if (!openSchunk) {
cat("#+BEGIN_LaTeX\n", file = chunkout, append = TRUE)
cat("\\begin{Schunk}\n", file = chunkout, append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
openSchunk <- TRUE
}
cat("\\begin{Sinput}", file = chunkout, append = TRUE)
openSinput <- TRUE
}
cat("\n", paste(getOption("prompt"), dce[1:leading],
sep = "", collapse = "\n"), file = chunkout,
append = TRUE, sep = "")
if (length(dce) > leading)
cat("\n", paste(getOption("continue"), dce[-(1:leading)],
sep = "", collapse = "\n"), file = chunkout,
append = TRUE, sep = "")
linesout[thisline + 1:length(dce)] <- srcline
thisline <- thisline + length(dce)
}
tmpcon <- file()
sink(file = tmpcon)
err <- NULL
if (options$eval)
err <- evalFunc(ce, options)
cat("\n")
sink()
output <- readLines(tmpcon)
close(tmpcon)
if (length(output) == 1 & output[1] == "")
output <- NULL
RweaveTryStop(err, options)
if (object$debug)
cat(paste(output, collapse = "\n"))
if (length(output) > 0 & (options$results != "hide")) {
if (openSinput) {
cat("\n\\end{Sinput}\n", file = chunkout, append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
openSinput <- FALSE
}
if (options$results == "verbatim") {
if (!openSchunk) {
cat("#+BEGIN_LaTeX\n", file = chunkout, append = TRUE)
cat("\\begin{Schunk}\n", file = chunkout,
append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
openSchunk <- TRUE
}
cat("\\begin{Soutput}\n", file = chunkout,
append = TRUE)
linesout[thisline + 1] <- srcline
thisline <- thisline + 1
}
output <- paste(output, collapse = "\n")
if (options$strip.white %in% c("all", "true")) {
output <- sub("^[[:space:]]*\n", "", output)
output <- sub("\n[[:space:]]*$", "", output)
if (options$strip.white == "all")
output <- sub("\n[[:space:]]*\n", "\n", output)
}
cat(output, file = chunkout, append = TRUE)
count <- sum(strsplit(output, NULL)[[1]] == "\n")
if (count > 0) {
linesout[thisline + 1:count] <- srcline
thisline <- thisline + count
}
remove(output)
if (options$results == "verbatim") {
cat("\n\\end{Soutput}\n", file = chunkout,
append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
}
}
}
if (openSinput) {
cat("\n\\end{Sinput}\n", file = chunkout, append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
}
if (openSchunk) {
cat("\\end{Schunk}\n", file = chunkout, append = TRUE)
cat("#+END_LaTeX\n", file = chunkout, append = TRUE)
linesout[thisline + 1:2] <- srcline
thisline <- thisline + 2
}
if (is.null(options$label) & options$split)
close(chunkout)
if (options$split & options$include) {
cat("#+LaTeX: \\input{", chunkprefix, "}\n", sep = "", file = object$output,
append = TRUE)
linesout[thisline + 1] <- srcline
thisline <- thisline + 1
}
if (options$fig && options$eval) {
if (options$eps) {
grDevices::postscript(file = paste(chunkprefix,
"eps", sep = "."), width = options$width, height = options$height,
paper = "special", horizontal = FALSE)
err <- try({
SweaveHooks(options, run = TRUE)
eval(chunkexps, envir = .GlobalEnv)
})
grDevices::dev.off()
if (inherits(err, "try-error"))
stop(err)
}
if (options$pdf) {
grDevices::pdf(file = paste(chunkprefix, "pdf",
sep = "."), width = options$width, height = options$height,
version = options$pdf.version, encoding = options$pdf.encoding)
err <- try({
SweaveHooks(options, run = TRUE)
eval(chunkexps, envir = .GlobalEnv)
})
grDevices::dev.off()
if (inherits(err, "try-error"))
stop(err)
}
if (options$include) {
chunksuffix <- ifelse(options$eps, "eps", "pdf")
cat("[[./", paste(chunkprefix, chunksuffix, sep = "."), "]]\n",
sep = "", file = object$output, append = TRUE)
linesout[thisline + 1] <- srcline
thisline <- thisline + 1
}
}
object$linesout <- c(object$linesout, linesout)
return(object)
}
RweaveOrgRuncode
}
RweaveOrgRuncode <- makeRweaveOrgCodeRunner()
RweaveOrgWritedoc <-
function (object, chunk) {
linesout <- attr(chunk, "srclines")
## This part of the function adds the appropriate \usepackage
## directive and begins the document. Skip this for now, but
## eventually add in detection for #+LATEX_PREAMBLE
### if (length(grep("\\usepackage[^\\}]*Sweave.*\\}", chunk)))
### object$havesty <- TRUE
### if (!object$havesty) {
### begindoc <- "^[[:space:]]*\\\\begin\\{document\\}"
### which <- grep(begindoc, chunk)
### if (length(which)) {
### chunk[which] <- sub(begindoc, paste("\\\\usepackage{",
### object$styfile, "}\n\\\\begin{document}", sep = ""),
### chunk[which])
### linesout <- linesout[c(1:which, which, seq(from = which +
### 1, length.out = length(linesout) - which))]
### object$havesty <- TRUE
### }
### }
while (length(pos <- grep(object$syntax$docexpr, chunk))) {
cmdloc <- regexpr(object$syntax$docexpr, chunk[pos[1]])
cmd <- substr(chunk[pos[1]], cmdloc, cmdloc + attr(cmdloc,
"match.length") - 1)
cmd <- sub(object$syntax$docexpr, "\\1", cmd)
if (object$options$eval) {
val <- as.character(eval(parse(text = cmd), envir = .GlobalEnv))
if (length(val) == 0)
val <- ""
}
else val <- paste("\\\\verb{<<", cmd, ">>{", sep = "")
chunk[pos[1]] <- sub(object$syntax$docexpr, val, chunk[pos[1]])
}
while (length(pos <- grep(object$syntax$docopt, chunk))) {
opts <- sub(paste(".*", object$syntax$docopt, ".*", sep = ""),
"\\1", chunk[pos[1]])
object$options <- SweaveParseOptions(opts, object$options,
RweaveOrgOptions)
if (isTRUE(object$options$concordance) && !object$haveconcordance) {
savelabel <- object$options$label
object$options$label <- "concordance"
prefix <- RweaveChunkPrefix(object$options)
object$options$label <- savelabel
object$concordfile <- paste(prefix, "org", sep = ".")
chunk[pos[1]] <- sub(object$syntax$docopt, paste("\\\\input{",
prefix, "}", sep = ""), chunk[pos[1]])
object$haveconcordance <- TRUE
}
else chunk[pos[1]] <- sub(object$syntax$docopt, "", chunk[pos[1]])
}
cat(chunk, sep = "\n", file = object$output, append = TRUE)
object$linesout <- c(object$linesout, linesout)
return(object)
}
RweaveOrgFinish <-
function (object, error = FALSE) {
outputname <- summary(object$output)$description
inputname <- object$srcfile$filename
if (!object$quiet && !error)
cat("\n", gettextf("You can now run org-export-as-latex on '%s'", outputname),
"\n", sep = "")
close(object$output)
if (length(object$chunkout) > 0)
for (con in object$chunkout) close(con)
if (object$haveconcordance) {
linesout <- object$linesout
vals <- rle(diff(linesout))
vals <- c(linesout[1], as.numeric(rbind(vals$lengths,
vals$values)))
concordance <- paste(strwrap(paste(vals, collapse = " ")),
collapse = " %\n")
special <- paste("\\special{concordance:", outputname,
":", inputname, ":%\n", concordance, "}\n", sep = "")
cat(special, file = object$concordfile)
}
invisible(outputname)
}
RweaveOrgOptions <- RweaveLatexOptions

View File

@ -0,0 +1,19 @@
SweaveSyntaxOrg <- list()
SweaveSyntaxOrg$doc <- "^#\\+END_[SR]"
SweaveSyntaxOrg$code <- "^#\\+BEGIN_[SR]:?[[:space:]]*(.*)$"
SweaveSyntaxOrg$coderef <- "^#\\+[SR]_CODEREF:?[[:space:]]*(.*)$"
SweaveSyntaxOrg$docopt <- "^#\\+[SR]_OPTS:?[[:space:]]*(.*)$"
SweaveSyntaxOrg$docexpr <- "\\\\[SR]\\{([^\\}]*)\\}"
SweaveSyntaxOrg$extension <- "\\.[SRsr]org$"
SweaveSyntaxOrg$syntaxname <- "^#\\+[SR]WEAVE_SYNTAX:?[[:space:]]*(.*)$"
SweaveSyntaxOrg$input <- "^#\\+[SR]_FILE:?[[:space:]]*(.*)$"
SweaveSyntaxOrg$trans <- list()
SweaveSyntaxOrg$trans$doc <- "\\\\end{Scode}"
SweaveSyntaxOrg$trans$code <- "\\\\begin{Scode}{\\1}"
SweaveSyntaxOrg$trans$coderef <- "\\\\Scoderef{\\1}"
SweaveSyntaxOrg$trans$docopt <- "\\\\SweaveOpts{\\1}"
SweaveSyntaxOrg$trans$docexpr <- "\\\\Sexpr{\\1}"
SweaveSyntaxOrg$trans$extension <- ".[SR]org"
SweaveSyntaxOrg$trans$syntaxname <- "\\\\SweaveSyntax{SweaveSyntaxLatex}"
SweaveSyntaxOrg$trans$input <- "\\\\SweaveInput{\\1}"
attr(SweaveSyntaxOrg, "class") <- "SweaveSyntax"

View File

@ -0,0 +1,5 @@
source("SweaveSyntaxOrg.R")
source("RweaveOrg.R")
Sweave("testing.Rorg", driver=RweaveOrg, syntax=SweaveSyntaxOrg)
Stangle("testing.Rorg", driver=Rtangle(), syntax=SweaveSyntaxOrg)

View File

@ -0,0 +1,73 @@
# -*- mode: org -*-
#+OPTIONS: LaTeX:t
* Sweave and org-mode
If you're reading a PDF version of this document, you should also
look at [[file:testing.Rorg][testing.Rorg]] (the source file) and [[file:testing.org][testing.org]] (the output
of the Sweave process).
Keep in mind that one of the advantages of a block-based approach is
using \texttt{C-'} to edit code in its native mode.
** Use the Sweave package for latex formatting
Org allows us to issue commands to be included in \{LaTeX} export.
#+LATEX_HEADER: \usepackage{Sweave}
** R blocks
The first argument to an R block when using Sweave is the label for
that block.
Not all R blocks are printed. Sweave options allow the printing of
the evaluated code, the output of the code, both, or neither.
*** R code that is not printed
#+BEGIN_R: hidden_block, echo=FALSE, results=HIDE
a <- 3
b <- 6
#+END_R
*** R code that is printed
#+BEGIN_R: visible_block
c <- 4
#+END_R
We can use block labels to embed blocks by reference (even if they
weren't printed before).
*** R code that references other blocks
#+BEGIN_R: combined_block
#+R_CODEREF: hidden_block
#+R_CODEREF: visible_block
a + b +c
#+END_R
** Inline references to R data
We can evaluate R code inline.
*** Used in text
The value of =a= is \R{a}.
*** Used in a table
| a | b | c | TOTAL |
|-------+-------+-------+---------------|
| \R{a} | \R{b} | \R{c} | \R{a + b + c} |
** Single-line R commands
If we want a line of R code to be evaluated but not printed,
there's a convenient shorthand. This only works for single lines
of R code, but you can have more than one in a row.
#+R: library(lattice)
#+R: data(cars)
** Graphics
We use values defined elsewhere in the buffer to produce this
graph. The new CAPTION and LABEL arguments work just fine.
#+CAPTION: speed by distance
#+LABEL: fig:speed_by_distance
#+BEGIN_R fig=TRUE, eps=FALSE, pdf=TRUE
print(xyplot(speed ~ dist, cars,
panel = function (x, y, ...) {
panel.xyplot(x, y, ...)
panel.abline(h=a)
panel.abline(v=b)
}))
#+END_R

846
existing_tools/org-R.el Normal file
View File

@ -0,0 +1,846 @@
;;; org-R.el --- Numerical computation and data visualisation for org-mode using R
;; Copyright (C) 2009
;; Free Software Foundation, Inc.
;; Author: Dan Davison <davison@stats.ox.ac.uk>
;; Keywords: org, R, ESS, tables, graphics
;; Homepage: http://www.stats.ox.ac.uk/~davison/software/org-R
;; Version: 0.05 2009-02-05
;;
;; This file is not part of GNU Emacs.
;;
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;; This file allows R (http://www.r-project.org) code to be applied to
;; emacs org-mode (http://orgmode.org) tables. When the
;; result of the analysis is a vector or matrix, it is output back
;; into the org-mode buffer as a new org table. Alternatively the R
;; code may be used to plot the data in the org table. It requires R to be
;; running in an inferior-ess-mode buffer (install Emacs Speaks
;; Statistics http://ess.r-project.org and issue M-x R).
;;
;;
;; The user interface is via two different options lines in the org
;; buffer. As is conventional in org-mode, these are lines starting
;; with `#+'. Lines starting with #+R: specify options in the
;; standard org style (option:value) and are used to specify certain
;; off-the-shelf transformations and plots of the table data. The
;; #+R: line is also used to specify the data to be analysed
;; (either an org table or a csv file), and to restrict the analysis
;; to certain columns etc. In lines starting #+RR: you can supply
;; literal R code, giving you full control over what you do with the
;; table. With point in the first #+R line, M-x org-R-apply
;; makes happen whatever has been specified in those lines.
;; The best documentation is currently the Worg tutorial:
;;
;; http://orgmode.org/worg/org-tutorials/org-R/org-R.php
(defconst org-R-skeleton-funcall-1-arg
"%s(x[%s]%s)"
"Skeleton of a call to an R function.
E.g. barplot(x[,3:5], names.arg=rownames(x))")
(defconst org-R-skeleton-funcall-2-args
"%s(x[,%s], x[,%s]%s)"
"Skeleton of a call to an R function which can take x and y
args.")
(defconst org-R-write-org-table-def
"write.org.table <- function (x, write.rownames = TRUE)
{
if(!is.null(dim(x)) && length(dim(x)) > 2)
stop(\"Object must be 1- or 2-dimensional\") ;
if(is.vector(x) || is.table(x) || is.factor(x) || is.array(x))
x <- as.matrix(x) ;
if(!(is.matrix(x) || inherits(x, c('matrix', 'data.frame')))) {
invisible() ;
print(x) ;
stop(\"Object not recognised as 1- or 2-dimensional\") ;
} ;
if(is.null(colnames(x)))
colnames(x) <- rep('', ncol(x)) ;
if(write.rownames)
x <- cbind(rownames(x), x) ;
cat('|', paste(colnames(x), collapse = ' | '), '|\\n') ;
cat('|', paste(rep('----', ncol(x)), collapse = '+'), '|\\n', sep = '') ;
invisible(apply(x, 1, function(row) cat('|', paste(row, collapse = ' | '), '|\\n'))) ;
}"
"Definition of R function to write org table representation of R objects.
To see a more human-readable version of this, look at the code,
or type dput(write.org.table) RET at the R (inferior-ess-mode
buffer) prompt.")
(defun org-R-apply ()
"Construct and evaluate an R function call.
Construct an R function corresponding to the #+R: and #+RR:
lines. R must be currently running in an inferior-ess-mode
buffer. The function evaluates any user-supplied R code in the
#+RR: line before the off-the-shelf actions specified in the #+R:
line. The user-supplied R code can operate on a variable called x
that is the org table represented as a data frame in R. Text
output from the R process may be inserted into the org buffer, as
an org table where appropriate."
(interactive)
(require 'ess)
(save-excursion
(beginning-of-line)
(unless (looking-at "#\\+RR?:") (error "Point must be in a #+R or #+RR line"))
(while (looking-at "#\\+RR?:") (forward-line -1))
(forward-line)
;; For the rest of the code in this file we are based at the
;; beginning of the first #+R line
;; FIXME: if point is at the beginning of the #+RR? lines when
;; this function is called, then tabular output gets inserted,
;; leaving point up at the top of the tabular output.
(let* ((options (org-R-get-options))
(code (org-R-construct-code options))
(infile (plist-get options :infile))
(ext (if infile (file-name-extension infile)))
csv-file)
(if (string-equal ext "csv")
(setq csv-file infile)
(setq csv-file
(org-R-export-to-csv
(make-temp-file "org-R-tmp" nil ".csv") options)))
(org-R-eval code csv-file options)
(delete-other-windows) ;; FIXME
(if (plist-get options :showcode) (org-R-showcode code)))))
(defun org-R-apply-to-subtree ()
"Call org-R-apply in every org-R block in current subtree."
;; This currently relies on re-search-forward leaving point after
;; the #+RR?: If point were at the beginning of the line, then
;; tabular input would get inserted leaving point above the #+RR?:,
;; and this would loop infinitely. Same for org-R-apply-to-buffer.
(interactive)
(save-excursion
(org-back-to-heading)
(while (re-search-forward
"^#\\+RR?:"
(save-excursion (org-end-of-subtree)) t)
(org-R-apply)
(forward-line)
(while (looking-at "#\\+RR?")
(forward-line)))))
(defun org-R-apply-to-buffer ()
"Call org-R-apply in every org-R block in the buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^#\\+RR?:" nil t)
(org-R-apply)
(forward-line)
(while (looking-at "#\\+RR?")
(forward-line)))))
(defun org-R-construct-code (options)
"Construct the R function that implements the requested
behaviour. The body of this function derives from two sources:
1. Explicit R code which is read from lines starting with
#+RR: by org-R-get-user-code, and
2. Off-the-shelf code corresponding to options specified in the
#+R: line. This code is constructed by
org-R-off-the-shelf-code."
(let ((user-code (org-R-get-user-code))
(action (plist-get options :action)))
(if (or (eq action 'tabulate) (eq action 'transpose))
(setq options (plist-put options :output-to-buffer t)))
(format "function(x){%sx}"
(concat
(when user-code (concat user-code ";"))
(when action (concat (org-R-off-the-shelf-code options) ";"))))))
(defun org-R-get-user-code (&optional R)
"Read user-supplied R code from #+RR: lines"
(let ((case-fold-search t))
(save-excursion
(while (looking-at "^#\\+\\(RR?:\\) *\\(.*\\)")
(if (string= "RR:" (match-string 1))
(setq R (concat R (when R ";") (match-string 2))))
(forward-line))))
R)
(defun org-R-off-the-shelf-code (options)
"Return R code implementing the actions requested in the
#+R: lines."
;; This is a somewhat long function as it deals with several
;; different cases, corresponding to all the off-the-shelf actions
;; that have been implemented.
(let* ((action (plist-get options :action))
(cols (plist-get options :columns))
(ncols (org-R-number-of-columns cols))
(nxcols (nth 0 ncols))
(nycols (nth 1 ncols))
(cols-R (org-R-make-index-vectors cols))
(xcols-R (nth 0 cols-R))
(ycols-R (nth 1 cols-R))
seq args largs extra-code title colour matrix-index)
;; I want this to affect options outside this function. Will it
;; necessarily do so? (not if plist-put adds to head of the
;; plist?)
(setq options (plist-put options :nxcols nxcols))
(cond ((eq action 'points)
(setq action 'plot)
(setq options (plist-put options :lines nil)))
((eq action 'lines)
(setq action 'plot)
(setq options (plist-put options :lines t))))
(if (and (setq title (plist-get options :title)) (symbolp title))
(setq title symbol-name title))
(setq args (plist-put args :main (concat "\"" title "\"")))
(if (setq colour (or (plist-get options :colour)
(plist-get options :color)
(plist-get options :col)))
(setq args
(plist-put args :col
(concat "\"" (if (symbolp colour) (symbol-name colour) colour) "\""))))
(setq largs
(if (setq legend (plist-get options :legend))
(plist-put largs :x
(concat "\"" (if (symbolp legend) (symbol-name legend) legend) "\""))
(plist-put largs :x "\"topright\"")))
(cond
((null ycols-R)
;; single set of columns; implicit x values
(if (null xcols-R)
(setq xcols-R "" matrix-index "")
(setq matrix-index (concat "," xcols-R)))
(cond
;;----------------------------------------------------------------------
((eq action 'barplot)
(if (eq nxcols 1)
(progn
(setq args (plist-put args :names.arg "rownames(x)"))
(setq args (org-R-set-user-supplied-args args (plist-get options :args)))
(format org-R-skeleton-funcall-1-arg
"barplot" xcols-R
(concat ", " (org-R-plist-to-R-args args))))
(setq args (plist-put args :names.arg "colnames(x)"))
(setq args (plist-put args :col "seq(nrow(x))"))
(setq args (plist-put args :beside "TRUE"))
(setq largs (plist-put largs :bty "\"n\""))
;; (setq largs (plist-put largs :lwd 10))
(setq largs (plist-put largs :col "seq(nrow(x))"))
(setq largs (plist-put largs :legend "rownames(x)"))
(setq args (org-R-set-user-supplied-args args (plist-get options :args)))
(concat (format org-R-skeleton-funcall-1-arg
"barplot(as.matrix" matrix-index
(concat "), " (org-R-plist-to-R-args args)))
"; legend(" (org-R-plist-to-R-args largs) ")")))
;;----------------------------------------------------------------------
((eq action 'density)
(if (and nxcols (> nxcols 1))
(error "Multiple columns not implemented for action:%s" action))
(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))
(setq args (org-R-set-user-supplied-args args (plist-get options :args)))
(format org-R-skeleton-funcall-1-arg
"plot(density" matrix-index
(concat "), " (org-R-plist-to-R-args args))))
;;----------------------------------------------------------------------
((eq action 'hist)
(if (and nxcols (> nxcols 1))
(error "Multiple columns not implemented for action:%s" action))
(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))
(setq args (org-R-set-user-supplied-args args (plist-get options :args)))
(setq args (concat ", " (org-R-plist-to-R-args args)))
(format org-R-skeleton-funcall-1-arg "hist" matrix-index args))
;;----------------------------------------------------------------------
((eq action 'image)
(format org-R-skeleton-funcall-1-arg "image(as.matrix" matrix-index ")"))
;;----------------------------------------------------------------------
((eq action 'plot)
(setq seq (concat "seq_along("xcols-R")"))
(setq args (plist-put args :type (if (plist-get options :lines) "\"l\"" "\"p\"")))
(setq args (plist-put args :ylab (concat "colnames(x)["xcols-R"]")))
(setq args (concat ", " (org-R-plist-to-R-args args)))
(concat (format org-R-skeleton-funcall-1-arg
(if (eq nxcols 1) "plot" "matplot") matrix-index args)
extra-code))
;;----------------------------------------------------------------------
((eq action 'tabulate)
(concat
(if (plist-get options :sort)
(format org-R-skeleton-funcall-1-arg
"x <- sort(table" xcols-R "), decreasing=TRUE")
(format org-R-skeleton-funcall-1-arg "x <- table" matrix-index ""))
(if (eq nxcols 1) "; x <- data.frame(value=names(x), count=x[])")))
;;----------------------------------------------------------------------
((eq action 'transpose)
(format org-R-skeleton-funcall-1-arg "x <- t" matrix-index ""))
;;----------------------------------------------------------------------
;; Don't recognise action: option, try applying it as the name of an R function.
(t (format org-R-skeleton-funcall-1-arg
(concat "x <- " (symbol-name action)) matrix-index ""))))
;;----------------------------------------------------------------------
(ycols-R
;; x and y columns specified
(cond
;;----------------------------------------------------------------------
((eq action 'plot)
(unless (eq nxcols 1) (error "Multiple x-columns not implemented for action:plot"))
(setq args
(plist-put
args :ylab
(concat "if(length("ycols-R") == 1) colnames(x)["ycols-R"] else ''")))
(setq args (plist-put args :xlab (concat "colnames(x)["xcols-R"]")))
(setq args (plist-put args :type (if (plist-get options :lines) "\"l\"" "\"p\"")))
(setq args (concat ", " (org-R-plist-to-R-args args)))
(setq seq (concat "seq_along("ycols-R")"))
(setq largs (plist-put largs :col seq))
(setq largs (plist-put largs :lty seq))
(setq largs (plist-put largs :bty "\"n\""))
(setq largs (plist-put largs :legend (concat "colnames(x)["ycols-R"]")))
(setq extra-code
(concat "; "
"if(length("ycols-R") > 1) "
"legend(" (org-R-plist-to-R-args largs) ")"))
(concat (format org-R-skeleton-funcall-2-args
(if (and (eq nxcols 1) (eq nycols 1)) "plot" "matplot")
xcols-R ycols-R args)
extra-code))
;;----------------------------------------------------------------------
(t (error "action:%s requires a single set of columns" (symbol-name action))))))))
(defun org-R-set-user-supplied-args (args user-args)
"Set user-supplied values in arguments plist."
(while (setq prop (pop user-args))
(setq args (plist-put args prop (pop user-args))))
args)
(defun org-R-plist-to-R-args (plist)
"Convert a plist into a string of R arguments."
(let (arg-string arg)
(while (setq arg (pop plist))
(string-match ":\\(\.*\\)" (symbol-name arg))
(setq arg (match-string 1 (symbol-name arg)))
(setq arg-string
(concat
(if arg-string (concat arg-string ", "))
(format "%s=%s" arg (pop plist)))))
arg-string))
(defun org-R-alist-to-R-args (alist)
"Convert an alist of (argument . val) pairs into a string of R arguments.
The alist is something like
'((arg1 . 1)
(arg2 . a))
This isn't used, but it seems much nicer than
my plist equivalent. Is there a better way to write the plist
version?
"
(mapconcat
'identity
(mapcar (lambda(pair) (format "%s = %s" (car pair) (cdr pair))) alist)
", "))
(defun org-R-make-index-vectors (cols)
"COLS is the lisp form given by the `columns:' option. It may
take the following forms:
1. integer atom - the number of the column
2. symbol/string atom - the name of the column
3. list of length 1 - same as 1 or 2 above
4. list of length > 1 - specification of multiple columns as 1 or 2 above, unless it is
5. list of 2 lists - each list specifies (possibly multiple) columns
In cases 1-4 this function returns a list of length 1, containing
the R index vector as a string. In case 5 this function returns a
list of two such index vectors.
In cases 1 - 4, when a bivariate plot is requested such as by
`action:lines', the x values are implicit, i.e
1,2,...,number-of-rows.
In case 4, an attempt is made to do something sensible with the
multiple columns, e.g. for `action:lines' they will be plotted
together on the same graph against the implicit x values, and for
`action:barplot' the bars corresponding to a single row will be
stacked on top of each other, or placed side by side, depending
on the value of the `beside' option.
For `action:tabulate', if 2 columns are selected, a
two-dimensional table is created. If more than 2, then the
appropriately dimensioned table is computed and inserted using
the standard text representation of multi-dimensional arrays used
by R (as org does not currently have tables of dimension > 2).
The straightforward case of case 5 is that both lists are of
length 1. For `action:plot' and `action:lines' these specify the
y and x coordinates of the points to be plotted or joined by
lines.
The intention is that `org-R-apply' does something
corresponding to what would happen if you did the following in R:
fun(x=tab[,xcols], y=tab[,ycols])
where fun is the R function implementing the desired
action (plotting/computation), tab is the org table, xcols are
the columns specified in cases 1-4 above, and ycols are the
second set of columns which might have been specified under case
5 above. For relevant R documentation see the help page
associated with the function xy.coords, e.g. by typing ?xy.coords
at the R prompt.
The following won't work with case 5: `tabulate'
"
(defun org-R-make-index-vector (cols)
"Return the R indexing vector (as a string) corresponding to
the lisp form COLS. In this function, COLS is a either a list of
atoms, or an atom, i.e. in the form of cases 1-4"
(when cols
(let (to-stringf)
(unless (listp cols) (setq cols (list cols)))
(setq to-stringf
(cond ((car (mapcar 'symbolp cols))
(lambda (symbol) (concat "\"" (symbol-name symbol) "\"")))
((car (mapcar 'integerp cols))
'int-to-string)
((car (mapcar 'stringp cols))
(lambda (string) (concat "\"" string "\"")))
(t (error "Column selection should be symbol, integer or string: %S" cols))))
(concat (when (> (length cols) 1) "c(")
(mapconcat to-stringf cols ",")
(when (> (length cols) 1) ")")))))
(if (and (listp cols) (listp (car cols)))
(mapcar 'org-R-make-index-vector cols) ;; case 5
(list (org-R-make-index-vector cols)))) ;; other cases
(defun org-R-number-of-columns (cols)
(defun f (c) (if (listp c) (length c) 1))
(if (and (listp cols) (listp (car cols)))
(mapcar 'f cols)
(list (f cols))))
(defun org-R-eval (R-function csv-file options)
"Apply an R function to tabular data and receive output as an org table.
R-FUNCTION is a string; it may be simply the name of an
appropriate R function (e.g. \"summary\", \"plot\"), or a
user-defined anonymous function of the form
\"(function(data.frame) {...})\". It will receive as its first
argument the org table as an R 'data frame' -- a table-like
structure which can have columns containing different types of
data -- numeric, character etc.
The R function may produce graphical and/or text output. If it
produces text output, and the replace:t is specified, and if
there is a table immediately above the #+R lines, then it is
replaced by the text output. Otherwise the text output is
inserted above the #+R lines.
"
(let ((transit-buffer "org-R-transit")
(infile (plist-get options :infile))
(output-file (plist-get options :outfile))
(title (plist-get options :title))
output-format graphics-output-file width height)
(unless (not output-file)
;; We are writing output to file. Determine file format and
;; location, and open graphics device if necessary.
(if (string-match
"\\(.*\.\\)?\\(org\\|png\\|jpg\\|jpeg\\|pdf\\|ps\\|bmp\\|tiff\\)$"
output-file)
(setq output-format (match-string 2 output-file))
(error "Did not recognise file name suffix %s as available output format"
(match-string 2 output-file)))
(unless (match-string 1 output-file)
;; only suffix provided: store in org-attach dir
(require 'org-attach)
(let ((temporary-file-directory (org-attach-dir t)))
(setq output-file
(make-temp-file
"org-R-output-" nil (concat "." output-format)))))
(if (eq output-format "jpg") (setq output-format "jpeg"))
(setq graphics-output-file (not (string-equal output-format "org")))
(if graphics-output-file ;; open the graphics device
(ess-execute
(concat output-format "(file=\"" output-file "\""
(if (setq width (plist-get options :width))
(format ", width=%d" width))
(if (setq height (plist-get options :height))
(format ", height=%d" height)) ")"))))
;; Apply R code to table (which is now stored as a csv file)
;; does it matter whether this uses ess-command or ess-execute?
;; First evaluate function definition for R -> org table conversion
(ess-execute (replace-regexp-in-string "\n" " " org-R-write-org-table-def)
nil transit-buffer)
;; FIXME: why not eval the function def together with the function call
;; as in the commented out line below (it didn't work for some reason)
(ess-execute
(concat
;; (replace-regexp-in-string "\n" " " org-R-write-org-table-def) ";"
(org-R-make-expr R-function csv-file options)) nil transit-buffer)
(save-excursion
(set-buffer (concat "*" transit-buffer "*"))
(unless (or (looking-at "$")
(string-equal (buffer-substring-no-properties 1 2) "|"))
(error "Error in R evaluation:\n%s" (buffer-string))))
(if csv-file
(unless (and infile
(string-equal (file-name-extension infile) "csv"))
(delete-file csv-file)))
(if graphics-output-file (ess-execute "dev.off()")) ;; Close graphics device
(unless (or graphics-output-file
(not (plist-get options :output-to-buffer)))
;; Send tabular output to a org buffer as new org
;; table. Recall that we are currently at the beginning of the
;; first #+R line
(if (and output-file graphics-output-file)
(error "output-to-buffer and graphics-output-file both t"))
(save-excursion
(if output-file
(progn (set-buffer (find-file-noselect output-file))
(delete-region (point-min) (point-max)))
(if (plist-get options :replace)
(progn ;; kill a table iff in one or one ends on the previous line
(delete-region (org-table-begin) (org-table-end))
(save-excursion
(forward-line -1)
(if (looking-at "#\\+TBLNAME")
(delete-region (point) (1+ (point-at-eol))))))))
(if title (insert "#+TBLNAME:" title "\n"))
(insert-buffer-substring (concat "*" transit-buffer "*"))
(org-table-align)
(if output-file (save-buffer))))
;; We might be linking to graphical output, or to org output in
;; another file. Either way, point is still at the beginning of
;; the first #+R line.
(unless (not output-file)
(save-excursion
(forward-line -1)
(if (looking-at "\\[\\[file:")
(delete-region (point) (1+ (point-at-eol)))))
(insert (org-make-link-string
(concat "file:" output-file)
(unless (plist-get options :inline)
(or title (concat output-format " output")))) "\n"))
(kill-buffer (concat "*" transit-buffer "*"))))
(defun org-R-export-to-csv (csv-file options)
"Find and export org table to csv.
If the intable: option has not been supplied, then the table must
end on the line immediately above the #+R lines. Otherwise,
the remote table referenced by the intable: option is found using
org-R-find-table. If options:infile has been set then this is the
org file containing the table. See the docstring of
org-R-find-table for details."
(let ((tbl-name-or-id (plist-get options :intable))
(org-file (plist-get options :infile)) tbl-marker)
(if (and org-file
(not (string-equal (file-name-extension org-file) "org")))
(error "File %s extension is not .csv so should be .org"))
(save-excursion
(if tbl-name-or-id
;; a remote table has been specified -- move into it
(progn
(if org-file (set-buffer (find-file-noselect org-file)))
(setq tbl-marker (org-R-find-table tbl-name-or-id 'marker))
(set-buffer (marker-buffer tbl-marker))
(goto-char (marker-position tbl-marker)))
(forward-line -1)) ;; move into table above
(if (looking-at "[ \t]*|")
(progn (org-table-export csv-file "orgtbl-to-csv") csv-file)
nil))))
(defun org-R-find-table (name-or-id &optional markerp)
"Return location of a table.
NAME-OR-ID may be the name of a
table in the current file as set by a \"#+TBLNAME:\" directive.
The first table following this line will then be used.
Alternatively, it may be an ID referring to any entry, perhaps in
a different file. In this case, the first table in that entry
will be referenced. The location is returned as a marker pointing
to the beginning of the first line of the table.
This is taken from the first part of org-table-get-remote-range
in org-table.el.
"
(cond
((symbolp name-or-id) (setq name-or-id (symbol-name name-or-id)))
((numberp name-or-id) (setq name-or-id (number-to-string name-or-id))))
(save-match-data
(let ((id-loc nil) (case-fold-search t) buffer loc)
(save-excursion
(save-restriction
(widen)
(save-excursion
(goto-char (point-min))
(if (re-search-forward
(concat "^#\\+TBLNAME:[ \t]*" (regexp-quote name-or-id) "[ \t]*$")
nil t)
;; OK, we've found a matching table name in this buffer.
(setq buffer (current-buffer) loc (match-beginning 0))
;; It's not a table name in this buffer. It must be an entry id.
;; obtain a marker pointing to it.
(setq id-loc (org-id-find name-or-id 'marker)
buffer (marker-buffer id-loc)
loc (marker-position id-loc))
(move-marker id-loc nil))) ;; disable the marker
;; (switch-to-buffer buffer)
(set-buffer buffer)
;; OK, so now we're in the right buffer, and loc is either
;; the beginning of the #+TBLNAME line, or the location of the entry
;; either way we need to search forward to get to the beginning of the table
(save-excursion
(save-restriction
(widen)
(goto-char loc)
(forward-char 1)
;; The following regexp search finds the beginning of
;; the next table in this entry. If it gets to the next
;; entry before the next table, then it signals failure.
(unless (and (re-search-forward "^\\(\\*+ \\)\\|[ \t]*|" nil t)
(not (match-beginning 1)))
(error "Cannot find a table at NAME or ID %s" name-or-id))
(if markerp
(move-marker (make-marker) (point-at-bol) (current-buffer))
(error "Option to return cons cell not implemented.
It should return (file-name . position) to be
consistent with functions in org-id.el")))))))))
(defun org-R-make-expr (R-function csv-file options)
"Construct R code to read data, analyse it and write output."
(let ((rownames (plist-get options :rownames))
(colnames (plist-get options :colnames))
(action (plist-get options :action))
(replace (plist-get options :replace)))
(if (and csv-file (symbolp csv-file))
(setq csv-file (symbol-name csv-file)))
(format "write.org.table((%s)(%s), write.rownames=%s)"
R-function
(if csv-file
(format
"read.csv(\"%s\", header=%s, row.names=%s)"
csv-file
;; Do we treat first row as colnames? Yes by default
;; FIXME: should really check for hline
(if colnames "TRUE" "FALSE")
;; Do we use a column as rownames? Not unless rownames: is specified
(if rownames "1" "NULL"))
"NULL")
;; Do we write rownames into org table?
(cond ((eq action 'tabulate)
(if (eq (plist-get options :nxcols) 1) "FALSE" "TRUE"))
((eq action 'transpose) (if colnames "TRUE" "FALSE"))
(rownames "TRUE")
(t "TRUE")))))
(defun org-R-get-options ()
"Parse the #+R: lines and return the options and values as a p-list."
(let ((opts '(
(:infile . "infile")
(:intable . "intable")
(:rownames . "rownames")
(:colnames . "colnames")
(:columns . "columns")
(:action . "action")
(:args . "args")
(:outfile . "outfile")
(:replace . "replace")
(:title . "title")
(:legend . "legend")
(:colour . "colour")
(:color . "color")
(:col . "col")
(:height . "height")
(:width . "width")
(:lines . "lines")
(:sort . "sort")
(:inline . "inline")
(:output-to-buffer . "output-to-buffer")
(:showcode . "showcode")))
(regexp ":\\(\"[^\"]*\"\\|(([^)]*) *([^)]*))\\|([^)]*)\\|[^ \t\n\r;,.]*\\)")
(case-fold-search t) p)
;; FIXME: set default options properly
(setq p (plist-put p :output-to-buffer t)) ;; FIXME: hack: null options plist is bad news
(setq p (plist-put p :replace t))
(setq p (plist-put p :rownames nil))
(setq p (plist-put p :colnames t))
(setq p (plist-put p :inline nil))
(save-excursion
(while (looking-at "^#\\+\\(RR?:+\\) *\\(.*\\)")
(if (string= "R:" (match-string 1))
(setq p (org-R-add-options-to-plist p (match-string 2) opts regexp)))
(forward-line)))
p))
(defun org-R-add-options-to-plist (p opt-string op regexp)
"Parse a #+R: line and set values in the property list
p. This function is adapted from similar functions in org-exp.el
and org-plot.el. It might be a good idea to have a single
function serving these three files' needs."
;; Adapted from org-exp.el and org-plot.el
(let (o)
(when opt-string
(while (setq o (pop op))
(if (string-match
(concat (regexp-quote (cdr o)) regexp)
opt-string)
(setq p (plist-put p (car o)
(car (read-from-string
(match-string 1 opt-string)))))))))
p)
(defun org-R-sanitise-options (options)
(error "not used yet")
(let (should-be-strings '(title legend colour color col csv)))
)
(defun org-R-showcode (R)
"Display R function constructed by org-R in a new R-mode
buffer"
(split-window-vertically)
(switch-to-buffer "*org-table.R*")
(kill-region (point-min) (point-max))
(R-mode)
(insert (replace-regexp-in-string
";" "\n" (replace-regexp-in-string "\\([{}]\\)" "\n\\1\n" R)))
;; (mark-whole-buffer)
;; (indent-region)
;; why doesn't that do what I hoped?
)
(defun org-R-get-remote-range (name-or-id form)
"This is a refactoring of Carsten's original version. I have
extracted the first bit of his function and named it
org-R-find-table (which would presumably be called
something like org-table-find-table or org-id-find-table if this
were accepted).
Get a field value or a list of values in a range from table at ID.
NAME-OR-ID may be the name of a table in the current file as set by
a \"#+TBLNAME:\" directive. The first table following this line
will then be used. Alternatively, it may be an ID referring to
any entry, possibly in a different file. In this case, the first table
in that entry will be referenced.
FORM is a field or range descriptor like \"@2$3\" or or \"B3\" or
\"@I$2..@II$2\". All the references must be absolute, not relative.
The return value is either a single string for a single field, or a
list of the fields in the rectangle."
(let ((tbl-marker (org-R-find-table name-or-id 'marker))
org-table-column-names org-table-column-name-regexp
org-table-local-parameters org-table-named-field-locations
org-table-current-line-types org-table-current-begin-line
org-table-current-begin-pos org-table-dlines
org-table-hlines org-table-last-alignment
org-table-last-column-widths org-table-last-alignment
org-table-last-column-widths tbeg)
(save-excursion
(set-buffer (marker-buffer tbl-marker))
(goto-char (marker-position tbl-marker))
(org-table-get-specials)
(setq form (org-table-formula-substitute-names form))
(if (and (string-match org-table-range-regexp form)
(> (length (match-string 0 form)) 1))
(save-match-data
(org-table-get-range (match-string 0 form) (point) 1))
form))))
(provide 'org-R)

View File

@ -0,0 +1,380 @@
;;; org-exp-blocks.el --- pre-process blocks when exporting org files
;; Copyright (C) 2008 Eric Schulte
;; Author: Eric Schulte
;; This file is not currently part of GNU Emacs.
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2, or (at
;; your option) any later version.
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program ; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;;
;; This is a utility for pre-processing blocks in org files before
;; export using the `org-export-preprocess-hook'. It can be used for
;; exporting new types of blocks from org-mode files and also for
;; changing the default export behavior of existing org-mode blocks.
;; The `org-export-blocks' and `org-export-interblocks' alist can be
;; used to control how blocks and the spaces between blocks
;; respectively are processed upon export.
;;
;; The type of a block is defined as the string following =#+begin_=,
;; so for example the following block would be of type ditaa. Note
;; that both upper or lower case are allowed in =#+BEGIN_= and
;; =#+END_=.
;;
;; #+begin_ditaa blue.png -r -S
;; +---------+
;; | cBLU |
;; | |
;; | +----+
;; | |cPNK|
;; | | |
;; +----+----+
;; #+end_ditaa
;;
;;; Currently Implemented Block Types
;;
;; ditaa :: Convert ascii pictures to actual images using ditaa
;; http://ditaa.sourceforge.net/. To use this set
;; `org-ditaa-jar-path' to the path to ditaa.jar on your
;; system (should be set automatically in most cases) .
;;
;; dot :: Convert graphs defined using the dot graphing language to
;; images using the dot utility. For information on dot see
;; http://www.graphviz.org/
;;
;; comment :: Wrap comments with titles and author information, in
;; their own divs with author-specific ids allowing for css
;; coloring of comments based on the author.
;;
;; R :: Implements Sweave type exporting, evaluates blocks of R code,
;; and also replaces \R{} chunks in the file with their result
;; when passed to R. This require the `R' command which is
;; provided by ESS (Emacs Speaks Statistics).
(defcustom org-export-blocks
'((comment org-export-blocks-format-comment)
(ditaa org-export-blocks-format-ditaa)
(dot org-export-blocks-format-dot)
(r org-export-blocks-format-R)
(R org-export-blocks-format-R))
"Use this a-list to associate block types with block exporting
functions. The type of a block is determined by the text
immediately following the '#+BEGIN_' portion of the block header.
Each block export function should accept three argumets..."
:group 'org-export-general
:type 'alist)
(defcustom org-export-interblocks
'((r org-export-interblocks-format-R)
(R org-export-interblocks-format-R))
"Use this a-list to associate block types with block exporting
functions. The type of a block is determined by the text
immediately following the '#+BEGIN_' portion of the block header.
Each block export function should accept three argumets..."
:group 'org-export-general
:type 'alist)
(defcustom org-export-blocks-witheld
'(hidden)
"List of block types (see `org-export-blocks') which should not
be exported."
:group 'org-export-general
:type 'list)
(defvar org-export-blocks-postblock-hooks nil "")
(defun org-export-blocks-html-quote (body &optional open close)
"Protext BODY from org html export. The optional OPEN and
CLOSE tags will be inserted around BODY."
(concat
"\n#+BEGIN_HTML\n"
(or open "")
body (if (string-match "\n$" body) "" "\n")
(or close "")
"#+END_HTML\n"))
(defun org-export-blocks-latex-quote (body &optional open close)
"Protext BODY from org latex export. The optional OPEN and
CLOSE tags will be inserted around BODY."
(concat
"\n#+BEGIN_LaTeX\n"
(or open "")
body (if (string-match "\n$" body) "" "\n")
(or close "")
"#+END_LaTeX\n"))
(defun org-export-blocks-preprocess ()
"Export all blocks acording to the `org-export-blocks' block
exportation alist. Does not export block types specified in
specified in BLOCKS which default to the value of
`org-export-blocks-witheld'."
(interactive)
(save-window-excursion
(let ((count 0)
(blocks org-export-blocks-witheld)
(case-fold-search t)
(types '())
type func start end)
(flet ((interblock (start end type)
(save-match-data
(when (setf func (cadr (assoc type org-export-interblocks)))
(funcall func start end)))))
(goto-char (point-min))
(setf start (point))
(while (re-search-forward
"^#\\+begin_\\(\\S-+\\)[ \t]*\\(.*\\)?[\r\n]\\([^\000]*?\\)#\\+end_\\S-*[\r\n]" nil t)
(save-match-data (setf type (intern (match-string 1))))
(unless (memq type types) (setf types (cons type types)))
(setf end (save-match-data (match-beginning 0)))
(interblock start end type)
(if (setf func (cadr (assoc type org-export-blocks)))
(replace-match (save-match-data
(if (memq type blocks)
""
(apply func (match-string 3) (split-string (match-string 2) " ")))) t t))
(setf start (save-match-data (match-end 0))))
(mapcar (lambda (type)
(interblock start (point-max) type))
types)))))
(add-hook 'org-export-preprocess-hook 'org-export-blocks-preprocess)
;;================================================================================
;; type specific functions
;;--------------------------------------------------------------------------------
;; ditaa: create images from ASCII art using the ditaa utility
(defvar org-ditaa-jar-path (expand-file-name
"ditaa.jar"
(file-name-as-directory
(expand-file-name
"scripts"
(file-name-as-directory
(expand-file-name
".."
(file-name-directory (or load-file-name buffer-file-name)))))))
"Path to the ditaa jar executable")
(defun org-export-blocks-format-ditaa (body &rest headers)
"Pass block BODY to the ditaa utility creating an image.
Specify the path at which the image should be saved as the first
element of headers, any additional elements of headers will be
passed to the ditaa utility as command line arguments."
(message "ditaa-formatting...")
(let ((out-file (if headers (car headers)))
(args (if (cdr headers) (mapconcat 'identity (cdr headers) " ")))
(data-file (make-temp-file "org-ditaa")))
(unless (file-exists-p org-ditaa-jar-path)
(error (format "Could not find ditaa.jar at %s" org-ditaa-jar-path)))
(setq body (if (string-match "^\\([^:\\|:[^ ]\\)" body)
body
(mapconcat (lambda (x) (substring x (if (> (length x) 1) 2 1)))
(org-split-string body "\n")
"\n")))
(cond
((or htmlp latexp)
(with-temp-file data-file (insert body))
(message (concat "java -jar " org-ditaa-jar-path " " args " " data-file " " out-file))
(shell-command (concat "java -jar " org-ditaa-jar-path " " args " " data-file " " out-file))
(format "\n[[file:%s]]\n" out-file))
(t (concat
"\n#+BEGIN_EXAMPLE\n"
body (if (string-match "\n$" body) "" "\n")
"#+END_EXAMPLE\n")))))
;;--------------------------------------------------------------------------------
;; dot: create graphs using the dot graphing language
;; (require the dot executable to be in your path)
(defun org-export-blocks-format-dot (body &rest headers)
"Pass block BODY to the dot graphing utility creating an image.
Specify the path at which the image should be saved as the first
element of headers, any additional elements of headers will be
passed to the dot utility as command line arguments. Don't
forget to specify the output type for the dot command, so if you
are exporting to a file with a name like 'image.png' you should
include a '-Tpng' argument, and your block should look like the
following.
#+begin_dot models.png -Tpng
digraph data_relationships {
\"data_requirement\" [shape=Mrecord, label=\"{DataRequirement|description\lformat\l}\"]
\"data_product\" [shape=Mrecord, label=\"{DataProduct|name\lversion\lpoc\lformat\l}\"]
\"data_requirement\" -> \"data_product\"
}
#+end_dot"
(message "dot-formatting...")
(let ((out-file (if headers (car headers)))
(args (if (cdr headers) (mapconcat 'identity (cdr headers) " ")))
(data-file (make-temp-file "org-ditaa")))
(cond
((or htmlp latexp)
(with-temp-file data-file (insert body))
(message (concat "dot " data-file " " args " -o " out-file))
(shell-command (concat "dot " data-file " " args " -o " out-file))
(format "\n[[file:%s]]\n" out-file))
(t (concat
"\n#+BEGIN_EXAMPLE\n"
body (if (string-match "\n$" body) "" "\n")
"#+END_EXAMPLE\n")))))
;;--------------------------------------------------------------------------------
;; comment: export comments in author-specific css-stylable divs
(defun org-export-blocks-format-comment (body &rest headers)
"Format comment BODY by OWNER and return it formatted for export.
Currently, this only does something for HTML export, for all
other backends, it converts the comment into an EXAMPLE segment."
(let ((owner (if headers (car headers)))
(title (if (cdr headers) (mapconcat 'identity (cdr headers) " "))))
(cond
(htmlp ;; We are exporting to HTML
(concat "#+BEGIN_HTML\n"
"<div class=\"org-comment\""
(if owner (format " id=\"org-comment-%s\" " owner))
">\n"
(if owner (concat "<b>" owner "</b> ") "")
(if (and title (> (length title) 0)) (concat " -- " title "</br>\n") "</br>\n")
"<p>\n"
"#+END_HTML\n"
body
"#+BEGIN_HTML\n"
"</p>\n"
"</div>\n"
"#+END_HTML\n"))
(t ;; This is not HTML, so just make it an example.
(concat "#+BEGIN_EXAMPLE\n"
(if title (concat "Title:" title "\n") "")
(if owner (concat "By:" owner "\n") "")
body
(if (string-match "\n\\'" body) "" "\n")
"#+END_EXAMPLE\n")))))
;;--------------------------------------------------------------------------------
;; R: Sweave-type functionality
(defvar interblock-R-buffer nil
"Holds the buffer for the current R process")
(defun org-export-blocks-format-R (body &rest headers)
"Process R blocks and replace \R{} forms outside the blocks
with their values as determined by R."
(interactive)
(message "R processing...")
(let ((image-path (or (and (car headers)
(string-match "\\(.?\\)\.\\(EPS\\|eps\\)" (car headers))
(match-string 1 (car headers)))
(and (> (length (car headers)) 0)
(car headers))
;; create the default filename
(format "Rplot-%03d" count)))
(plot (string-match "plot" body))
R-proc)
(setf count (+ count 1))
(interblock-initiate-R-buffer)
(setf R-proc (get-buffer-process interblock-R-buffer))
;; send strings to the ESS process using `comint-send-string'
(setf body (mapconcat (lambda (line)
(interblock-R-input-command line) (concat "> " line))
(butlast (split-string body "[\r\n]"))
"\n"))
;; if there is a plot command, then create the images
(when plot
(interblock-R-input-command (format "dev.copy2eps(file=\"%s.eps\")" image-path)))
(concat (cond
(htmlp (org-export-blocks-html-quote body
(format "<div id=\"R-%d\">\n<pre>\n" count)
"</pre>\n</div>\n"))
(latexp (org-export-blocks-latex-quote body
"\\begin{Schunk}\n\\begin{Sinput}\n"
"\\end{Sinput}\n\\end{Schunk}\n"))
(t (insert ;; default export
"#+begin_R " (mapconcat 'identity headers " ") "\n"
body (if (string-match "\n$" body) "" "\n")
"#+end_R\n")))
(if plot
(format "[[file:%s.eps]]\n" image-path)
""))))
(defun org-export-interblocks-format-R (start end)
"This is run over parts of the org-file which are between R
blocks. It's main use is to expand the \R{stuff} chunks for
export."
(save-excursion
(goto-char start)
(interblock-initiate-R-buffer)
(let (code replacement)
(while (and (< (point) end) (re-search-forward "\\\\R{\\(.*\\)}" end t))
(save-match-data (setf code (match-string 1)))
(setf replacement (interblock-R-command-to-string code))
(setf replacement (cond
(htmlp replacement)
(latexp replacement)
(t replacement)))
(setf end (+ end (- (length replacement) (length code))))
(replace-match replacement t t)))))
(defun interblock-initiate-R-buffer ()
"If there is not a current R process then create one."
(unless (and (buffer-live-p interblock-R-buffer) (get-buffer interblock-R-buffer))
(save-excursion
(R)
(setf interblock-R-buffer (current-buffer))
(interblock-R-wait-for-output)
(interblock-R-input-command ""))))
(defun interblock-R-command-to-string (command)
"Send a command to R, and return the results as a string."
(interblock-R-input-command command)
(interblock-R-last-output))
(defun interblock-R-input-command (command)
"Pass COMMAND to the R process running in `interblock-R-buffer'."
(save-excursion
(save-match-data
(set-buffer interblock-R-buffer)
(goto-char (process-mark (get-buffer-process (current-buffer))))
(insert command)
(comint-send-input)
(interblock-R-wait-for-output))))
(defun interblock-R-wait-for-output ()
"Wait until output arrives"
(save-excursion
(save-match-data
(set-buffer interblock-R-buffer)
(while (progn
(goto-char comint-last-input-end)
(not (re-search-forward comint-prompt-regexp nil t)))
(accept-process-output (get-buffer-process (current-buffer)))))))
(defun interblock-R-last-output ()
"Return the last R output as a string"
(save-excursion
(save-match-data
(set-buffer interblock-R-buffer)
(goto-char (process-mark (get-buffer-process (current-buffer))))
(forward-line 0)
(let ((raw (buffer-substring comint-last-input-end (- (point) 1))))
(if (string-match "\n" raw)
raw
(and (string-match "\\[[[:digit:]+]\\] *\\(.*\\)$" raw)
(message raw)
(message (match-string 1 raw))
(match-string 1 raw)))))))
(provide 'org-exp-blocks)
;;; org-exp-blocks.el ends here

22
rorg.org Normal file
View File

@ -0,0 +1,22 @@
#+TITLE: rorg --- R and org-mode
Please feel free to change the layout of this file, I'm just putting
this here to get things started.
* objectives
What are these?
I'll pre-populate with a quick list from the email to get started
Just to get this out there, there seem to be four kinds of
functionality we're trying to get here:
1. import data into R from org
2. easy editing of R code using r-mode from an org buffer
3. evaluate R code and make the output available for processing in an
org buffer
4. evaluate R code and format the output for export
* tasks