[ESS] Combining sweave and pdflatex into one helper function

Vitalie S. spinuvit.list at gmail.com
Thu Feb 10 12:48:37 CET 2011

Paul Johnson <pauljohn32 at gmail.com> writes:

> Hello, I need to revive this thread from December.
> I've got that same problem Dirk had. I'm running Ubuntu linux with
> Emacs 23.2 and ess 5.12.  After Sweaving a document, I get no further,
> ESS refuses, minibuffer says:
> ess-error: ESS process not ready. Finish your command before trying again.
> Did you find an answer to this problem?

Hi everyone!

I will address Dirk and Paul's problem of merging weaving and pdflatex first,
and the more general issue with the mechanism ESS uses to wait for the
inferior process in a sequel.

Here is the concatenation solution what works for me on Ubuntu:

(defun edd-sweave ()
  "Run sweave and pdflatex"
  (ess-swv-run-in-R "Sweave")
  ;; need to wait for the prompt and refresh the emacs winds here:
  (ess-prompt-wait2 (get-process ess-current-process-name))
  (ess-swv-PDF "pdflatex")

(defun ess-prompt-wait2 (proc &optional  start-of-output sleep)
  "Wait for a prompt to appear at BOL of process burffer
PROC is the ESS process. Does not change point"
;; redefined ess-prompt-wait from the ess-inf.el
  (if sleep (sleep-for sleep)); we sleep here, *and* wait below
  (if start-of-output nil (setq start-of-output (point-min)))
  (with-current-buffer (process-buffer proc)
    (while (progn
             (accept-process-output proc 0 500)
             (redisplay t)
             (goto-char (marker-position (process-mark proc)))
             (if (< (point) start-of-output) (goto-char start-of-output))
             (not (looking-at inferior-ess-primary-prompt))))))

Now, the second part.

Personally, I find the mechanism the ESS uses to check for
R process being "ready", very unreliable. The message "ESS process not
ready. Finish your command before trying again." in wide majority of cases pops
when the process is actually ready, but something is wrong with the prompt.

Congruential use of multiple ess-command calls is also prone to be highly
unreliable, and the output often ends up in wrong buffers (I experience it all
the time with the ess-watch functionality for ess-tracebug. Hope to release it
at the end of this week though.)

The solution as I see it is to check for the ending "> " directly in the process
filter and set the per process buffer variable  'ess-process-is-ready' to nil if
that is not found.

Bellow is the experimental patch which I propose to ess-inf.el. It assumes R
prompt "> ", but could be easily modified to adapt the general case.

In a nutshell:

- 'ess-process-is-ready' is defined as a buffer local var

- inferior-ess-output-filter and ordinary-insertion-filter are modified to
  look for "> " at the end of the output string, and set
  'ess-process-is-ready' accordingly.

- new function `ess-wait-for-process' with the same meaning as
  `ess-prompt-wait' but which uses 'ess-process-is-ready' to check for the
  availability of proc

- 'ess-command' is modified in two places, first to give the "process not
  ready" error in case 'ess-process-is-ready' is nil, second to use
  `ess-wait-for-process' instead of `ess-prompt-wait'.

with the above addition Dirk's function would look like:

(defun edd-sweave ()
  "Run sweave and pdflatex"
  (ess-swv-run-in-R "Sweave")
  (ess-wait-for-process (get-process ess-current-process-name) nil t)
  (ess-swv-PDF "pdflatex")


vitoshka at vitoshka:~/SVN/ESS$ svn diff
Index: lisp/ess-inf.el
--- lisp/ess-inf.el	(revision 4459)
+++ lisp/ess-inf.el	(working copy)
@@ -371,11 +371,18 @@
 	  (switch-to-buffer (process-buffer (get-process proc-name)))
 	(pop-to-buffer (process-buffer (get-process proc-name)))))))

+(defvar ess-process-is-ready t
+  "An indicator if the process is ready for input.
+To be used in the process buffers only. ")
+(make-variable-buffer-local 'ess-process-is-ready)
 (defun inferior-ess-output-filter (proc string)
   "Standard output filter for the inferior ESS process.
 Ring Emacs bell if process output starts with an ASCII bell, and pass
 the rest to `comint-output-filter'.
 Taken from octave-mod.el."
+  (with-current-buffer (process-buffer proc)
+    (setq ess-process-is-ready  (string-match "> +\\'" string)))
   (comint-output-filter proc (inferior-ess-strip-ctrl-g string)))

 (defun inferior-ess-strip-ctrl-g (string)
@@ -834,20 +841,32 @@
     (while (progn
 	     ;; get output if there is some ready

-;;	     (if (and ess-microsoft-p ess-ms-slow)
-;;		 (accept-process-output proc 0 1500) ; Microsoft is slow
-	       (accept-process-output proc 0 500)
-;;	       )
+             ;;	     (if (and ess-microsoft-p ess-ms-slow)
+             ;;		 (accept-process-output proc 0 1500) ; Microsoft is slow
+             (accept-process-output proc 0 500)
+             ;;	       )
 	     (goto-char (marker-position (process-mark proc)))
 	     (if (< (point) start-of-output) (goto-char start-of-output))
 	     (not (looking-at inferior-ess-primary-prompt))))))

+(defun ess-wait-for-process (proc &optional sleep force-redisplay)
+  "Wait for the  `ess-process-is-ready' to become t."
+  (if sleep (sleep-for sleep)); we sleep here, *and* wait below
+  (accept-process-output proc 0 500)
+  (with-current-buffer (process-buffer proc)
+    (while (not ess-process-is-ready)
+      (accept-process-output proc 0 500)
+      (if force-redisplay (redisplay t))
+      )))
 (defun ordinary-insertion-filter (proc string)
   (let ((old-buffer (current-buffer)))
 	(let (moving)
 	  (set-buffer (process-buffer proc))
+          (setq ess-process-is-ready  (string-match "> +\\'" string))
 	  (setq moving (= (point) (process-mark proc)))
 	    ;; Insert the text, moving the process-marker.
@@ -896,14 +915,9 @@
 		(and ess-cmd-delay sleep)))
 	(if do-sleep (setq sleep (* sleep ess-cmd-delay)))
 	(ess-if-verbose-write (format "(ess-command %s ..)" com))
-	(save-excursion
-	  (goto-char (marker-position (process-mark sprocess)))
-	  (beginning-of-line)
-	  (unless no-prompt-check
-	    (if (looking-at inferior-ess-primary-prompt)
-		nil
-	      (ess-error
-	       "ESS process not ready. Finish your command before trying again."))))
+        (unless (or ess-process-is-ready no-prompt-check)
+          (ess-error
+           "ESS process not ready. Finish your command before trying again."))
 	(setq oldpf (process-filter sprocess))
 	(setq oldpb (process-buffer sprocess))
 	(setq oldpm (marker-position (process-mark sprocess)))
@@ -920,11 +934,10 @@
 		(process-send-string sprocess com)
 		;; need time for ess-create-object-name-db on PC
 		(if no-prompt-check
-		    (sleep-for 0.020); 0.1 is noticable!
+		    (sleep-for 0.020); 0.1 is noticeable!
 		    ;; else: default
-		  (ess-prompt-wait sprocess nil
-				   (and do-sleep (* 0.4 sleep))));MS: 4
-		;;(if do-sleep (sleep-for (* 0.0 sleep))); microsoft: 0.5
+                  (ess-wait-for-process sprocess
+                                        (and do-sleep (* 0.4 sleep))))

 		;; goto end, making sure final prompt is deleted
 		;; FIXME? do this with much less code

More information about the ESS-help mailing list