3
votes

I'm remodeling my toolchain away from R + TeXShop to what I hope is a more efficient Sweave -> LaTeX -> SyncteX -> pdfview (skim) through emacs (Aquamacs, to be specific) AUCTeX-mode (with emacs-speaks-statsitics' sweave minor-mode). Everything works great with SyncteX from the AUCTeX -> pdfview chain, and everything works fine compiling from Sweave to .pdf.

The trouble comes in when I add Sweave as the first step. The .Rnw file now becomes the principal original document, and the .tex file it generates is just an intermediate step. Of course, it's the .tex file that SyncteX locks to.

The only changes I've made to my .emacs file are to register two new commands to build the .pdf from the .Rnw.

Can anyone offer advice on how to get emacs to use SyncteX to sync the output .pdf to the .Rnw, instead of the .tex?

Thanks!

Edit 1: Warning: This ended up being a summary of my fumbling around all afternoon. I get very close to the solution but foiled by an error at the very end that I cannot find documentation for. Warning 2: Today was the first time I've ever looked anywhere near this deep into emacs' inner workings. There's bound to be errors or inefficiencies.

VitoshKa's answer got me most of the way there. Adding his recommendation to my .emacs (well, Preferences.el, but functionally the same) file caused an Symbol’s value as variable is void: noweb-minor-mode-map error on launch because Aquamacs seems to load the .emacs file before the rest of its init files.

I fixed this by adding (require 'ess-site) (require 'ess-eldoc) immediately before VitoshKa's (define-key noweb-minor-mode-map (kbd "\C-c \C-c") 'Rnw-command-master). Unfortunately, Aquamacs ships with the ess-swv.elc file already in binary and forces the M-n P reader to be acroread. So I added the following edits of a portion of the ess-swv.el file to the bottom of my own .emacs:

(defun ess-makePDFandView ()
(interactive)
(setq namestem (substring (buffer-name) 0 (search ".Rnw" (buffer-name))))
(setq tex-filename (concat "\"" namestem "\".tex"))
(setq my-command-string (concat "pdflatex -synctex=1" tex-filename))
(shell-command my-command-string)
(setq my-command-string (concat "/Applications/Skim.app/Contents/MacOS/Skim \"" namestem ".pdf\" &"))
(shell-command my-command-string))

(define-key noweb-minor-mode-map "\M-nv" 'ess-makePDFandView)

Now emacs will launch skim with the correct pdf name from emacs with either M-n v or C-c C-c View RET. There's still one more snag to get through...

Command-Shift-Click in emacs tries to sync to filename.Rnw.pdf (but discarding the error message at least still causes a correct sync in the pdfviewer). Command-Shift-Click in the pdfviewer to search back to the source forces emacs to open the filename.tex file and syncs to that. Fortunately, Cameron Bracken has a fix for that. He just doesn't quite go all the way to emacs integration.

So I added this further hack of ess-swv.el to my .emacs:

(defun ess-swv-latex-rnw-sync ()
  "Run LaTeX on the product of Sweave()ing the current file."
  (interactive)
  (save-excursion
    (let* ((namestem (file-name-sans-extension (buffer-file-name)))
           (latex-filename (concat namestem ".tex"))
       (synctex-filename (concat namestem ".synctex.gz"))
           (tex-buf (get-buffer-create " *ESS-tex-output*"))
       (patchDVI-command-string (concat "Rscript -e \"library('patchDVI');patchSynctex('" synctex-filename "')\"")))
    (message "synctex string: %s" patchDVI-command-string) 
    (message "Running LaTeX on '%s' ..." latex-filename)
    (switch-to-buffer tex-buf)
    (call-process "latex" nil tex-buf 1 "-synctex=1 " latex-filename)
    (switch-to-buffer (buffer-name))
    (display-buffer tex-buf)
    (message "Finished running LaTeX" )
    (message "Running patchDVI...")
    (shell-command patchDVI-command-string))))

(define-key noweb-minor-mode-map "\M-nq" 'ess-swv-latex-rnw-sync)

With the thinking that it was the lack patching the DVI and syncing it in... but no! Same behaviour on Command-Shift-Click as above... Back to hacking up ess-swv. I removed the ess-makePDFandView function, and instead added the following to my .emacs:

(defun ess-swv-PDF-rnw-sync (&optional pdflatex-cmd)
"From LaTeX file, create a PDF (via 'texi2pdf' or 'pdflatex', ...), by
default using the first entry of `ess-swv-pdflatex-commands' and display it."
(interactive
  (list
    (let ((def (elt ess-swv-pdflatex-commands 0)))
    (completing-read (format "pdf latex command (%s): " def)
                   ess-swv-pdflatex-commands ; <- collection to choose from
                   nil 'confirm ; or 'confirm-after-completion
                   nil nil def))))
(let* ((buf (buffer-name))
     (namestem (file-name-sans-extension (buffer-file-name)))
     (latex-filename (concat namestem ".tex"))
     (tex-buf (get-buffer-create " *ESS-tex-output*"))
     (pdfviewer (ess-get-pdf-viewer))
     (pdf-status)
     (cmdstr-win (format "start \"%s\" \"%s.pdf\""
                         pdfviewer namestem))
     (cmdstr (format "\"%s\" \"%s.pdf\" &" pdfviewer namestem)))
;;(shell-command (concat "pdflatex " latex-filename))
(message "Running '%s' on '%s' ..." pdflatex-cmd latex-filename)
(switch-to-buffer tex-buf)
(setq pdf-status
      (call-process pdflatex-cmd nil tex-buf 1 "-synctex=1 " 
                    (if (string= "texi2" (substring pdflatex-cmd 0 5))
                        ;; workaround (bug?): texi2pdf or texi2dvi *fail* to work with      full path:
                        (file-name-nondirectory latex-filename)
                      latex-filename)))
(if (not (= 0 pdf-status))
    (message "** OOPS: error in '%s' (%d)!" pdflatex-cmd pdf-status)
  ;; else: pdflatex probably ok
  (shell-command
   (concat (if (and ess-microsoft-p (w32-shell-dos-semantics))
               cmdstr-win
             cmdstr))))
(switch-to-buffer buf)
(display-buffer tex-buf)))

(define-key noweb-minor-mode-map "\M-nv" 'ess-swv-PDF-rnw-sync)

Getting closer and closer, but we still need to tell auctex what filenames to pass through to skim. So I bit the bullet and edited auctex-config.el. I commented out the existing aquamacs-call-viewer function definition and replaced it with my own:

(defun aquamacs-call-viewer (line source)
"Display current output file as PDF at LINE (as in file SOURCE).
Calls `aquamacs-tex-pdf-viewer' to display the PDF file using the
Skim AppleScript protocol."
  (cond 
    ((string= (file-name-extension (TeX-active-master)) "Rnw") 
      (let* ((full-file-name 
     (expand-file-name
      ;; as in TeX-view
      ;; C-c C-c view uses %o (from TeX-expand-list), which
      ;; is the same.
      (concat (file-name-sans-extension (TeX-active-master)) "." (TeX-output-extension))
      default-directory))
    (full-source-name
     (expand-file-name 
      (concat (file-name-sans-extension (source)) ".Rnw") ;; this is relative to the master 
      (file-name-directory (full-file-name)))))
   (message "full pdf name: %s" full-file-name)
   (message "full rnw sournce name: %s" full-source-name)
   (do-applescript
(format 
 "
 set theSink to POSIX file \"%s\" 
 set theSource to POSIX file \"%s\" 
 tell application \"%s\" 
 activate 
 open theSink 
 tell front document to go to TeX line %d from theSource%s
 end tell
 " 
 full-file-name full-source-name aquamacs-tex-pdf-viewer line
 ;; do not say "showing reading bar false" so users can override in future
 (cond ((eq t aquamacs-skim-show-reading-bar)
    " showing reading bar true")
       ((eq nil aquamacs-skim-show-reading-bar)
    " showing reading bar false")
       (t ""))))))
(t
 (let* ((full-file-name 
     (expand-file-name
      ;; as in TeX-view
      ;; C-c C-c view uses %o (from TeX-expand-list), which
      ;; is the same.
      (TeX-active-master (TeX-output-extension))
      default-directory))
    (full-source-name
     (expand-file-name 
      source ;; this is relative to the master 
      (file-name-directory full-file-name))))
   (message "IN OTHERWISE!! WAT: %s" (file-name-extension (TeX-active-master)))
   (do-applescript
(format 
 "
 set theSink to POSIX file \"%s\" 
 set theSource to POSIX file \"%s\" 
 tell application \"%s\" 
 activate 
 open theSink 
 tell front document to go to TeX line %d from theSource%s
 end tell
 " 
 full-file-name full-source-name aquamacs-tex-pdf-viewer line
 ;; do not say "showing reading bar false" so users can override in future
 (cond ((eq t aquamacs-skim-show-reading-bar)
    " showing reading bar true")
       ((eq nil aquamacs-skim-show-reading-bar)
    " showing reading bar false")
       (t ""))))))))

And one more final test (notice I changed the keybindings of the toolchain to: M-n s -> M-n q -> M-n v in that order for sweave -> latex -> view [note that for this last step you have to use pdflatex and not texi2pdf]), everything builds, no errors, launching viewer...

"Skim" "/Users/myname/text/testing/myfile.pdf": exited abnormally with code 127.

And now I'm completely lost again. Anyone sufficiently familiar with Skim?

Edit 2: Well, error 127 is simply file-not-found on the binary. Somewhere in there I must have clobbered the path. So I added (setenv "PATH" (concat (getenv "PATH") ":" "/Applications/Skim.app/Contents/MacOS")) near the top of my auctex-config.el. Now everything compiles and launches with no errors ... but Command-Shift-Click on emacs does not get Skim to respond (at least it gives no errors). Command-Shift-Click on Skim still forces emacs to open the .tex file.

I guess this has now become a very Aquamacs + Skim specific question, since most of the rest of the interaction is in applescript, unless I missed something obvious in the key lines of lisp pointed out above.

1

1 Answers

3
votes

To clarify. You want C-c C-c from Rnw files to run AUCTeX commands on the output of Sweave (M-n s).

The problem is that Auctex doesn't know that it has to act on a different file. This is how to make it aware of that:

(defun Rnw-command-master (&optional override-confirm)
  "Run command on the current document.

If a prefix argument OVERRIDE-CONFIRM is given, confirmation will
depend on it being positive instead of the entry in `TeX-command-list'."
  (interactive "P")
  (let ((TeX-transient-master (concat
                               (file-name-sans-extension (buffer-file-name))
                               ".tex"))
        (TeX-master nil))
    (TeX-command (TeX-command-query (TeX-master-file)) 'TeX-master-file
                 override-confirm)))


(define-key noweb-minor-mode-map (kbd "\C-c \C-c") 'Rnw-command-master)

To make Auctex run sweave first it would require a bit of additional work. Noweb support is now actively rewritten in ESS and by next version you will have a completely new system with AucTeX integration and many other useful things activated by default.