moving generalized comint functions to new file org-babel-comint.el

This commit is contained in:
Eric Schulte 2009-05-27 19:00:13 -07:00
parent a54c27ec91
commit c5a58ad6aa
2 changed files with 171 additions and 71 deletions

93
lisp/org-babel-comint.el Normal file
View file

@ -0,0 +1,93 @@
;;; org-babel-comint.el --- org-babel functions for interaction with comint buffers
;; Copyright (C) 2009 Eric Schulte
;; Author: Eric Schulte
;; Keywords: literate programming, reproducible research, comint
;; Homepage: http://orgmode.org
;; Version: 0.01
;;; License:
;; 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 3, 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 GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;; This file should provide support for passing commands and results
;; to and from `comint-mode' buffers.
;;; Code:
(require 'org-babel)
(defun org-babel-comint-initiate-buffer (buffer ignite)
"If BUFFER does not currently have a process use IGNITE to
create one."
(unless (and (buffer-live-p buffer) (get-buffer buffer))
(save-excursion
(eval ignite)
(setf buffer (current-buffer))
(org-babel-comint-wait-for-output)
(org-babel-comint-input-command ""))))
(defun org-babel-comint-command-to-string (buffer command)
"Send COMMEND to BUFFER's process, and return the results as a string."
(org-babel-comint-input-command buffer command)
(org-babel-comint-last-output buffer))
(defun org-babel-comint-input-command (buffer command)
"Pass COMMAND to the process running in BUFFER."
(save-excursion
(save-match-data
(set-buffer buffer)
(goto-char (process-mark (get-buffer-process (current-buffer))))
(insert command)
(comint-send-input)
(org-babel-comint-wait-for-output))))
(defun org-babel-comint-wait-for-output (buffer)
"Wait until output arrives"
(save-excursion
(save-match-data
(set-buffer 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 org-babel-comint-last-output (buffer)
"Return BUFFER's the last output as a string"
(save-excursion
(save-match-data
(set-buffer buffer)
(goto-char (process-mark (get-buffer-process (current-buffer))))
(forward-line 0)
(let ((raw (buffer-substring comint-last-input-end (- (point) 1)))
output output-flag)
(mapconcat
(lambda (el)
(if (stringp el)
(format "%s" el)
(format "%S" el)))
(delq nil
(mapcar
(lambda (line)
(unless (string-match "^>" line)
(and (string-match "\\[[[:digit:]]+\\] *\\(.*\\)$" line)
(match-string 1 line))))
;; drop first, because it's the last line of input
(cdr (split-string raw "[\n\r]")))) "\n")))))
;;; org-babel-comint.el ends here

View file

@ -115,6 +115,84 @@ and the results to be collected in the same table.
* Tasks [20/32] * Tasks [20/32]
** TODO Create objects in top level (global) environment in R?
*** initial requirement statement [DED]
At the moment, objects created by computations performed in the
code block are evaluated in the scope of the
code-block-function-body and therefore disappear when the code
block is evaluated {unless you employ some extra trickery like
assign('name', object, env=globalenv()) }. I think it will be
desirable to also allow for a style wherein objects that are
created in one code block persist in the R global environment and
can be re-used in a separate block.
This is what Sweave does, and while I'm not saying we have to be
the same as Sweave, it wouldn't be hard for us to provide the same
behaviour in this case; if we don't, we risk undeservedly being
written off as an oddity by some.
IOW one aspect of org-babel is that of a sort of functional
meta-programming language. This is crazy, in a very good
way. Nevertheless, wrt R I think there's going to be a lot of value
in providing for a working style in which the objects are stored in
the R session, rather than elisp/org buffer. This will be a very
familiar working style to lots of people.
There are no doubt a number of different ways of accomplishing
this, the simplest being a hack like adding
#+begin_src R
for(objname in ls())
assign(objname, get(objname), envir=globalenv())
#+end_src
to the source code block function body. (Maybe wrap it in an on.exit() call).
However this may deserve to be thought about more carefully, perhaps
with a view to having a uniform approach across languages. E.g. shell
code blocks have the same semantics at the moment (no persistence of
variables across code blocks), because the body is evaluated in a new
bash shell process rather than a running shell. And I guess the same
is true for python. However, in both these cases, you could imagine
implementing the alternative in which the body is evaluated in a
persistent interactive session. It's just that it's particularly
natural for R, seeing as both ESS and org-babel evaluate commands in a
single persistent R session.
*** sessions [Eric]
Thanks for bringing this up. I think you are absolutely correct that we
should provide support for a persistent environment (maybe called a
*session*) in which to evaluate code blocks. I think the current setup
demonstrates my personal bias for a functional style of programming
which is certainly not ideal in all contexts.
While the R function you mention does look like an elegant solution, I
think we should choose an implementation that would be the same across
all source code types. Specifically I think we should allow the user to
specify an optional *session* as a header variable (when not present we
assume a default session for each language). The session name could be
used to name a comint buffer (like the *R* buffer) in which all
evaluation would take place (within which variables would retain their
values --at least once I remove some of the functional method wrappings
currently in place-- ).
This would allow multiple environments to be used in the same buffer,
and once this setup was implemented we should be able to fairly easily
implement commands for jumping between source code blocks and the
related session buffers, as well as for dumping the last N commands from
a session into a new or existing source code block.
Please let me know if you foresee any problems with this proposed setup,
or if you think any parts might be confusing for people coming from
Sweave. I'll hopefully find some time to work on this later in the
week.
*** implementation
in [[file:lisp/org-babel-comint.el][org-babel-comint.el]]
Currently I've coppied and begun generalizing the functions for
interacting with R buffers.
** TODO improve the source-block snippet ** TODO improve the source-block snippet
[[file:~/src/emacs-starter-kit/src/snippets/text-mode/rst-mode/chap::name%20Chapter%20title][file:~/src/emacs-starter-kit/src/snippets/text-mode/rst-mode/chap::name Chapter title]] [[file:~/src/emacs-starter-kit/src/snippets/text-mode/rst-mode/chap::name%20Chapter%20title][file:~/src/emacs-starter-kit/src/snippets/text-mode/rst-mode/chap::name Chapter title]]
@ -247,77 +325,6 @@ Another example is in the [[*operations%20in%20on%20tables][grades example]].
** TODO pass mutliple reference arguments into R ** TODO pass mutliple reference arguments into R
Can we do this? I wasn't sure how to supply multiple 'var' header Can we do this? I wasn't sure how to supply multiple 'var' header
args. Just delete this TODO if I'm being dense. args. Just delete this TODO if I'm being dense.
** TODO Create objects in top level (global) environment in R?
At the moment, objects created by computations performed in the
code block are evaluated in the scope of the
code-block-function-body and therefore disappear when the code
block is evaluated {unless you employ some extra trickery like
assign('name', object, env=globalenv()) }. I think it will be
desirable to also allow for a style wherein objects that are
created in one code block persist in the R global environment and
can be re-used in a separate block.
This is what Sweave does, and while I'm not saying we have to be
the same as Sweave, it wouldn't be hard for us to provide the same
behaviour in this case; if we don't, we risk undeservedly being
written off as an oddity by some.
IOW one aspect of org-babel is that of a sort of functional
meta-programming language. This is crazy, in a very good
way. Nevertheless, wrt R I think there's going to be a lot of value
in providing for a working style in which the objects are stored in
the R session, rather than elisp/org buffer. This will be a very
familiar working style to lots of people.
There are no doubt a number of different ways of accomplishing
this, the simplest being a hack like adding
#+begin_src R
for(objname in ls())
assign(objname, get(objname), envir=globalenv())
#+end_src
to the source code block function body. (Maybe wrap it in an on.exit() call).
However this may deserve to be thought about more carefully, perhaps
with a view to having a uniform approach across languages. E.g. shell
code blocks have the same semantics at the moment (no persistence of
variables across code blocks), because the body is evaluated in a new
bash shell process rather than a running shell. And I guess the same
is true for python. However, in both these cases, you could imagine
implementing the alternative in which the body is evaluated in a
persistent interactive session. It's just that it's particularly
natural for R, seeing as both ESS and org-babel evaluate commands in a
single persistent R session.
*** sessions [Eric]
Thanks for bringing this up. I think you are absolutely correct that we
should provide support for a persistent environment (maybe called a
*session*) in which to evaluate code blocks. I think the current setup
demonstrates my personal bias for a functional style of programming
which is certainly not ideal in all contexts.
While the R function you mention does look like an elegant solution, I
think we should choose an implementation that would be the same across
all source code types. Specifically I think we should allow the user to
specify an optional *session* as a header variable (when not present we
assume a default session for each language). The session name could be
used to name a comint buffer (like the *R* buffer) in which all
evaluation would take place (within which variables would retain their
values --at least once I remove some of the functional method wrappings
currently in place-- ).
This would allow multiple environments to be used in the same buffer,
and once this setup was implemented we should be able to fairly easily
implement commands for jumping between source code blocks and the
related session buffers, as well as for dumping the last N commands from
a session into a new or existing source code block.
Please let me know if you foresee any problems with this proposed setup,
or if you think any parts might be confusing for people coming from
Sweave. I'll hopefully find some time to work on this later in the
week.
** PROPOSED support for passing paths to files between source blocks ** PROPOSED support for passing paths to files between source blocks
Maybe this should be it's own result type (in addition to scalars and Maybe this should be it's own result type (in addition to scalars and