¿Cómo configurar el flujo de trabajo Knitr en Emacs?

18

RStudio proporciona una forma de un botón para producir archivos PDF desde la fuente LaTeX + R con Knitr. Se ve muy bien para hacer una investigación reproducible. Y estoy tratando de configurar mi Emacs para:

  • en el código del búfer izquierdo LaTeX + R en forma Knitr;
  • en la vista previa de salida de PDF del búfer derecho;
  • Una combinación de teclas para compilar.

Si es posible: ¿cómo debo configurar esto, por favor?

(ESS funciona bien, pero no sé cómo configurar Knitr-way y un botón para compilar).

drobnbobn
fuente
Estoy bastante seguro de que hay una mejor manera de hacerlo, pero tengo una función Elisp corta con un enlace de teclas ejecutándose rmarkdown::render(vía shell-command) en el actual buffer-file-name, que actualizará el pdf en la otra ventana.
daroczig
1
@daroczig, ¿funcionará para LaTeX, o simplemente rebajará?
Tyler
@daroczig: en mi respuesta, posiblemente intenté hacer esto para archivos Rnw (LaTeX + R). En cuanto a los archivos Rmd (Rmd + R), que es más simple, inicie una publicación por separado.
antonio
1
No pude obtener una configuración adecuada. Primero tengo que tejer con tejido polymode. Luego seleccione exportador y todo para crear el archivo .tex. A partir de ahí tengo que correr látex. Traté de adaptar la secuencia de comandos anterior, pero mi elisp todavía no está allí. Lo que quiero es tejer el archivo .Rnw, crear el pdf (pdflatex) y verlo. Configuré mi sistema, que si escribo "Cc Ca" (Tex-Command-run-all) en el archivo latex, el pdf se construye y se ve, pero no puedo llamar a esa función en el archivo tex desde my_knitr () . La ayuda sería apreciada (defun my_knitr () "Ejecutar Knitr en modo R-Poly y crear y ver pdf" (interacti
Krisselack
@Krisselack parece que las características que describo en mi respuesta a continuación se han eliminado de ESS. Tendré que actualizarlo para reflejar el nuevo enfoque que usan
Tyler el

Respuestas:

12

ACTUALIZAR

A partir de ESS 19.04, las bibliotecas ess-noweby ess-swvson obsoletas:

En consecuencia, mi respuesta original (a continuación) ya no se aplica. Polymode proporciona las características que estas bibliotecas solían proporcionar, y la configuración es más simple. Para conseguir un apoyo mínimo, todo lo que necesita es instalar el ess, polymodey poly-Rlos paquetes (de Melpa, o de origen si así es como se rollo).

¡Eso es! Ahora, si abre un Rnwarchivo, debería ver la PM-Rnwbandera en la línea de modo, y habrá un Polymodemenú en la parte superior. Puede entrelazar su archivo en un .texarchivo vía M-n w(o el menú polimodal) y exportarlo a .pdftravés de M-n e(o el menú). Se le solicitará un exportador la primera vez que haga esto; Acabo de elegir knitr.

NOTA: exporting ( M-n e) ejecuta automáticamente su código, genera el pdf y lo muestra, todo de una vez. No pude obtener ese comportamiento de "un clic" con la versión anterior que se describe a continuación.

Los archivos generados tendrán la palabra -woveny se -exportedagregarán. Si no le gusta esto, puede personalizar las opciones polymode-weaver-output-file-formaty polymode-exporter-output-file-format.

El proceso es similar para los archivos RMarkdown ( .Rmd).

Los detalles completos se proporcionan en el manual de polymode

Respuesta original (obsoleta después de ESS 19.04)

Se deben establecer tres variables:

  1. ess-swv-pdflatex-commands, en el grupo de personalización ess-sweavedebe tener "pdflatex" como primer comando. es decir, debería verse algo así como:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, en el grupo de personalización ess-R, debe ser el valor"knitr"
  3. ess-pdf-viewer-prefen el grupo de personalización esspara "emacsclient". Esto supone que está ejecutando el servidor emacs o que emacs se está ejecutando en modo --daemon. También debe usar herramientas pdf si es posible, ya que es mucho mejor que el visor de pdf Emacs incorporado.

Utilizo un enlace para agregar dos combinaciones de teclas para llamar a BibTeX y texi2pdf:

(add-hook 'ess-noweb-mode-hook 'my-noweb-hook)

(defun my-noweb-hook ()
  (define-key ess-noweb-mode-prefix-map "b"
    #'(lambda () (interactive) (TeX-command "BibTeX" 'TeX-master-file)))
  (define-key ess-noweb-mode-prefix-map "P"
    #'(lambda () (interactive)
        (ess-swv-PDF "texi2pdf"))))

Una vez hecho esto M-n s, M-n btejerá su documento, lo archivará y M-n Plo procesará con pdflatex.

Tenga en cuenta que no hay una manera simple de que Emacs sepa cuándo se completa el tejido, por lo que no puede configurarlo para tejer y látex en un solo paso; debe activar manualmente pdflatex después de haber visto que el tejido ha terminado.

Dados los múltiples pasos aquí: Rnw -> Latex -> PDF, no creo que pueda hacer que Synctex mantenga sus archivos PDF y Rnw para desplazarse juntos, pero me encantaría que me demuestren que están equivocados.

En cuanto a la disposición de las ventanas, nunca puedo hacer que se queden donde las quiero. Entonces, para compensar, me he convertido en un experto en barajar ventanas y buffers cuando los necesito;)

Yihui publicó un video corto en el sitio de knitr que demuestra algo de esto.

Tyler
fuente
He hecho todo lo posible para extraer toda la configuración necesaria, y solo la configuración necesaria, de mis .emacs. Puede que me haya perdido algo, es un poco peludo allí.
Tyler
me permitió compilar documentos de punto. Pero aún así trate de encontrar una combinación de teclas (para Rnw -> PDF). Espero que sea posible, ya que Rstudio tiene esto.
drobnbobn
@Tyler: ¡Gracias, su solución funciona como un encanto ootb (incluso sin editar la configuración)! Paquetes: ess, polymode, poly-r
Krisselack
5

Esta es una solución todo en uno. Será crear y mostrar un PDF a partir de un Rnw .
Específicamente:

  1. Guarde el búfer Rnw y hágalo tejer,
  2. Aplique un motor LaTeX dado al archivo TeX resultante,
  3. Identifique el ejecutable del motor BibTeX (por ejemplo, biber, bibtex8),
  4. Ejecute el motor BibTeX en el archivo TeX si el archivo bib es más nuevo que el archivo TeX,
  5. Ejecute LaTeX nuevamente, 6 Abra el PDF resultante en el visor designado.

El procedimiento intenta salir con mensajes informativos si falla uno de los pasos anteriores.
Se abrirá una instancia R, si es necesario, o la actual se usa para mostrar el proceso de tejido.
La salida de LaTeX se envía al búfer de "salida de TeX", que también aparece en caso de error de compilación.

Uso

Meta- x knit-mepara crear y ver el PDF.
Meta- x knit-me-clearpara eliminar archivos intermedios de LaTeX y knit-me.

La bibliografía requiere el paquete "biblatex", es decir:

\usepackage[
    options...      
    backend=ENGINE,
]{biblatex}
\addbibresource{foo.bib}

El nombre del motor bib (p bibtex. Ej . biber) Se obtiene analizando la backendpalabra clave.
\addbibresourceEl comando se analiza para obtener el archivo de bibliografía: si foo.bibes más reciente que el archivo TeX, se ejecuta el motor bib. En este sentido, solo el primer \addbibresourcecomando se tiene en cuenta si hay muchos.

Personalizar

Para ver realmente el PDF, configure la ruta ejecutable del visor con:

(setq pdf-viewer "path/to/pdf-viewer")

Posiblemente use un visor como SumatraPDF , que actualiza automáticamente el PDF cuando se vuelve a compilar y no bloquea el archivo abierto evitando nuevas compilaciones.

El motor predeterminado de LaTeX es pdflatex(asumido en la ruta actual). Personalizar con:

(setq latex-engine "newengine"
      latex-args   "--option-1 --option-2")

Por supuesto, es posible que desee vincular knit-mey knit-me-clearalgunas teclas convenientes.

Notas

Probado en Windows MiKTeX, con bibery bibtex8backends y GNU Emacs 25.1.1.

Código Elisp

;; (require 'ess-site) ; assumed in your init file

(defun knit-me-clear () 
  "Delete intermediate LaTeX files and run `knkt-me'.
These are based on extensions .aux, .blg, .out, .run.xml, .bbl, .log, -blx.bib"

  (interactive)
  (check-Rnw)
  (let
      ((file)
       (stem (file-name-sans-extension (buffer-file-name))))
    (dolist (elt
         (list ".aux" ".blg" ".out" ".run.xml" ".bbl" ".log" "-blx.bib"))
      (setq file (concat stem elt))
      (if (file-exists-p file) (delete-file file))))  
  (knit-me))


(defun knit-me () 
  "Knit->LaTeX-engine->bibtex-engine->LaTeX-engine->View.
Default LaTeX engine is \"pdflatex\" and can be customised with `latex-engine';
default LaTeX arguments are set to nil and can be customised with `latex-args';
default PDF viewer is set to nil and can be customised with `pdf-viewer'.
Bibliography must be set via \"biblatex\" LaTeX package.
Bibliography engine is obtained from \"backend\" option in \"biblatex\" package.
A reference  LaTeX bib file is obtained from the first LaTeX command \"\addbibresource{foo.bib}\".
The biblatex-engine is run if the bib file is newer of the TeX file. 
If there are multiple \"\addbibresource\" only the first will be used to decide whether to run the biblatex-engine."

  (interactive)

  ;; Default values
  (defvar pdf-viewer nil)
  (defvar latex-engine "pdflatex")
  (defvar latex-args nil)

  (check-Rnw)

  ;;If 1 R-proc found, associate it with buffer;
  ;;if many found, ask to choose one; if none found, launch and associate
  (ess-force-buffer-current "Process to use: ")

  ;;Save Rnw buffer
  (save-buffer)


  (let*
      (;;Set file paths
       (pathstem (file-name-sans-extension (buffer-file-name)))
       (namestem (file-name-nondirectory pathstem))
       (cur-dir     (file-name-directory pathstem))
       (rnw-name    (concat namestem ".Rnw"))
       (tex-name    (concat namestem ".tex"))

       ;;Create LaTeX commmand
       (latex-args (concat latex-args " " namestem))
       (latex-cmd (concat latex-engine " " latex-args))

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

       ;;Get R buffer proc
       (r-proc (ess-get-process))
       (r-buf (ess-get-process-buffer))

       ;;TeX executable process and bibtex engine/file
       (tex-proc)
       (tex-buf)
       (bibfile (bib-getfile))
       (bibengine (bib-getengine))
       (bibfile-updated
    (file-newer-than-file-p
     (concat cur-dir (file-name-nondirectory bibfile) ".bib") (concat pathstem ".tex")))


       ;;Command success
       (success nil)
       (error-msg "")
       )


    (setq default-directory cur-dir)

    ;; Exit on error
    (catch 'exit-func

      ;;Check bibtex file and engine
      (when (not bibfile)
    (setq error-msg (bib-getfile t))
    (throw 'exit-func nil))     
      (when (not bibengine)
    (setq error-msg (bib-getengine t))      
    (throw 'exit-func nil))

      ;; Biber wants .bib
      (let ((fail (and (string= bibengine "biber")
              (string= (file-name-nondirectory bibfile) (file-name-base bibfile)))))
    (setq success (not fail)))
      (when (not success)
    (setq error-msg
          (format "biber wants \\addbibresource{%s%s}" (file-name-base bibfile) ".bib"))
    (throw 'exit-func nil))


      ;; Knitting
      (switch-to-buffer-other-window r-buf)
      (message knit-cmd)
      (ess-eval-linewise knit-cmd nil t nil t) 
      ;; Foll. 3 lines are an alternative to ess-eval
      ;; (inferior-ess-mark-as-busy r-proc)  ; knit immediately after this line       
      ;; (process-send-string r-proc (format "cat(\"%s\");%s\n" knit-cmd knit-cmd)) ; real 
      ;; (ess-wait-for-process r-proc nil)

      ;; Check for knitting results
      (with-current-buffer r-buf
    ;; Parse last 3 lines
    (let ((beg) (end) (out))
      (goto-char (point-max))
      (setq end (point))
      (forward-line -3) 
      (setq beg (point))
      (setq out (buffer-substring-no-properties beg end))

      ;; Knitting successful?
      (setq success "output file: %s\n\n[1] \"%s\"\n> ")
      (setq success (string= (format success tex-name tex-name) out))))

      (when (not success)
    (setq error-msg (concat "Unable to knit " rnw-name))
    (throw 'exit-func nil))

      ;; First LaTeXing
      (setq tex-buf (get-buffer-create "TeX-output")) ; Create output buffer or use existing
      (with-current-buffer tex-buf                   
    (buffer-disable-undo)
    (erase-buffer))
      (message "1st latex ...")
      (send-r-mess (format "Starting LaTeX (see \"%s\")" (buffer-name tex-buf)))      
      (send-r-mess latex-cmd)
      (setq success (= 0 (call-process latex-engine nil tex-buf t latex-args)))
      (goto-char (point-max))

      ;; Check 1st LaTeX results
      (when (not success)
    (setq error-msg (concat "Unable to LaTeX " namestem))
    (switch-to-buffer-other-window tex-buf) 
    (other-window 1)
    (throw 'exit-func nil))

      ;; Run bibtex engine
      (when bibfile-updated  
    (message "biblatex ...")
    (send-r-mess (concat bibengine " "  namestem))
    (setq success (= 0 (call-process bibengine nil tex-buf t namestem)))
    (goto-char (point-max))

    ;; Check bibtex results
    (when (not success)
      (setq error-msg (concat "Unable to " bibengine " " namestem))
      (switch-to-buffer-other-window tex-buf) 
      (other-window 1)
      (throw 'exit-func nil)))

      ;; Second LaTeXing
      (message "2nd latex ...")
      (send-r-mess latex-cmd)
      (setq success (= 0 (call-process latex-engine nil tex-buf t latex-args)))
      (goto-char (point-max))

      ;; Check 2nd LaTeX results
      (when (not success)
    (setq error-msg (concat "Unable to LaTeX " pathstem))
    (switch-to-buffer-other-window tex-buf) 
    (other-window 1)
    (throw 'exit-func nil))

      ;; View   
      (if (not pdf-viewer) (throw 'exit-func nil))
      (send-r-mess  "...and now the viewer")
      (goto-char (point-max))
      (setq success (file-exists-p pdf-viewer))
      (when (not success)
    (setq error-msg (concat "Can\\'t find executable " pdf-viewer))
    (throw 'exit-func nil))

      ;; If you need viewer console output, use "(start-process "pdf-viewer" tex-buf ...";
      ;; but you block tex-buf buffer till the viewer is open
      (start-process "pdf-viewer" nil pdf-viewer (concat namestem ".pdf")))

    (if success
    (if bibfile-updated (message (concat "Updated to "  (file-name-nondirectory bibfile))))
      (message error-msg)
      (send-r-mess error-msg))))

(defun bib-getfile(&optional show-messages)
  "Check if 'addbibresource' command and related file exist. 
If found, return .bib file full path, else:
if SHOW-MESSAGES is nil return nil, if SHOW-MESSAGES is non-nil return related error."
  (save-excursion
    (goto-char (point-min))
    (re-search-forward "\\\\addbibresource{\\(.+\\)}" nil t))
  (let ((fmatch (match-string-no-properties 1)) 
    (success nil)
    mess)    
    (cond 
     ((not fmatch) (setq mess "Missing \\addbibresource command."))
     ((not (file-exists-p (concat (file-name-sans-extension fmatch) ".bib")))
      (setq mess (concat "Missing file: " fmatch ".bib")))
     ;; if no problem, sucess=message=bib-file-path
     (t (setq mess (concat (file-name-directory (buffer-file-name)) fmatch)
          success mess)))

    (if show-messages mess success)))

(defun bib-getengine(&optional show-messages)
  "Find biblatex engine.
If found,  engine name, else:
if SHOW-MESSAGES is nil return nil, if SHOW-MESSAGES is non-nil return related error."
  (save-excursion
    (goto-char (point-min))
    (let ((pack (re-search-forward "\\\\usepackage *\\(\\[[^]]*\\)\\] *{ *biblatex *}" nil t))
      (bend nil)
      mess)

      (when pack (setq pack (match-string-no-properties 1)))
      (when (and pack
         (string-match "[^[:alpha:]]+backend *= *\\([^, \n]+\\)" pack))
    (setq bend (match-string 1 pack)))
      (cond 
       ((not pack) (setq mess "Missing biblatex package command."))
       ((not bend) (setq mess "Missing biblatex backend."))
       ;; if no problem, sucess=message=bib-file-path
       (t (setq mess bend)))
      (if show-messages mess bend))))


(defun send-r-mess (mess)
  "Just send MESS at the end of R console buffer"
  (process-send-string (ess-get-process)
             (format "cat('%s\\n')\n" mess)))

(defun check-Rnw ()
  "Give error if `ess-dialect' is not \"R\""
  (if (not (string= "R" ess-dialect))
      (error "Not an Rnw buffer")))
antonio
fuente