4

I would like to start a LaTeX after knitting an .Rnw file. The meta code shoudl be something like:

(defun knit ()
  (interactive)
  (setq rproc (ess-get-process ess-current-process-name))
  (setq c "knit('foo.rnw')\n")
  (process-send-string rproc c)
  ;; Wait for ESS to finish 
  (shell-command-to-string   "cd foo/path & pdflatex foo"))

The main problem is to have Emacs to wait for the inferior buffer to finish knitting and only after to start latexing. I found a couple of interesting functions: (ess-wait-for-process ...) and (inferior-ess-mark-as-busy ...)`, which could possibly work, but I am unable to understand how.

Note that (shell-command-to-string ... is only illustrative. The final choice might be:

(TeX-command "LaTeX" 'rnw-to-tex-ext -1))

knit2pdf() might be a different path to go, but I will lose the benefit of AUCTeX.


PS: My question is considered "subjective and likely to be closed" by the SE robot!?

antonio
  • 10,629
  • 13
  • 68
  • 136

2 Answers2

1

I use texi2pdf and have R do it after the knit, but that's apparently the equivalent of knit2pdf. I wonder though if you could use system to call whatever tex command you want from within R, after the knit command, similar to how I use texi2pdf.

I'm not an emacs expert whatsoever but for what it's worth here's the function I have in my .emacs file.

; use knitr (was Sweave) script as compile function for Rnw files
(defun ess-swv-SweaveSh ()
  "Use knitr script to knit an Rnw file and create a pdf."
  (interactive)
  (let 
    ((compilation-buffer-name-function (function (lambda(ign)(concat "*" (buffer-file-name) "*")))))
    (compile  (concat "Rscript -e \".n <- '" (buffer-file-name) "'; library(knitr); knit(.n); library(tools); texi2pdf(sub('Rnw$','tex',.n))\"" ))
  )
)
Aaron left Stack Overflow
  • 36,704
  • 7
  • 77
  • 142
1

A possible solution is to check the 'busy property of the inferior ESS process. The while loop waiting for it to be nil blocks command echo. This is why you see (redisplay). To avoid stressing your dear one, I set also a refresh delay (sleep-for .5).
Just in case ESS gets stuck the loop exists after 60 seconds. Adjust it, if you have heavy weight code.

Finally I attached latexing to AUCTeX. Therefore you can customise LaTeX-command-style with the help of TeX-expand-list documentation.

The function automatically sets the proper name for files to knit and latex, and sets R working dir to the .Rnw file to be knitted, so you can source other scripts or load data.
So you can run it everywhere with M-x knit or perhaps associate it with a shortcut. The function saves the buffer with last changes before knitting and opens an inferior R buffer if there is none available; otherwise it uses the existing one.

(defun knit ()
  "Save the buffer, knit and latex"
  (interactive) ; You will associate this to you favourite key

  (let* (
    ;; Get names & path
        (cur-dir (file-name-directory (buffer-file-name)))
    (rnw-name (file-name-nondirectory (buffer-file-name)))
    (tex-name (concat (file-name-base (buffer-file-name)) ".tex"))

    ;; Create knit command
    (cmd (format "require(knitr); setwd('%s'); knit('%s')"  cur-dir rnw-name))

    ;; Time the knitting  
    (start-time (float-time))
    (wait  60)   ; Lifeboat to exit loop if smt wrong
    )

    ;; Save rnw buffer... you are lazy, I know)
    (save-buffer)


    ;; Send string to R at low-level 
    ;;(setq rproc (ess-get-process ess-current-process-name))
    ;;(process-send-string rproc c)

    ;; or Send line with the ESS wrapper 
    (ess-eval-linewise cmd)

    ;; While loop to check when 
    (setq start-time (float-time)
      wait  60) ; Lifeboat to exit loop after x secs 

    ;; Wait for 'busy property nil, nut not more than wait seconds
    (setq rproc (ess-get-process ess-current-process-name))
    (while  (< (- (float-time) start-time) wait)
      (sleep-for .5)
      ;; (accept-process-output rproc .5) ;alt. way for  process-send-string
      (redisplay)
      (if (not (process-get rproc  'busy)) 
      (setq wait 0)
    (message "Knitting... ")))
    (message "Knitting finished, starting latexing")

    ;; Set LaTeX your fav options. See TeX-expand-list for % pars 
    (setq LaTeX-command-style '(("" "%(PDF)%(latex) -file-line-error %S%(PDFout)")))
    ;; TeX-command requires a 'file function (anonymous here) returning the filename.
    ;; TeX-command/TeX-expand-list say 'file has one opt arg: extension.
    ;; Actually they are 2, despite the second seems always passed true.
    (TeX-command "LaTeX"  (lambda (&optional ext dummy) tex-name))))
antonio
  • 10,629
  • 13
  • 68
  • 136
  • Great and I wonder why it isn't the marked answer yet. Please do you have any update to this `knit` function since then? How to add this function to the `TeX-command-list`? – doctorate Dec 31 '15 at 16:32