come impostare il flusso di lavoro Knitr in Emacs?


18

RStudio offre un modo con un solo pulsante per produrre file PDF dall'origine LaTeX + R con Knitr. Sembra fantastico per fare ricerche riproducibili. E sto provando a configurare il mio Emacs per:

  • nel buffer sinistro codice LaTeX + R in modo Knitr;
  • all'anteprima dell'output PDF del buffer di destra;
  • una combinazione di tasti per la compilazione.

Se è possibile: come devo installarlo, per favore?

(ESS funziona bene, ma non so come impostare Knitr-way e un pulsante per la compilazione.)


Sono abbastanza sicuro che ci sia un modo migliore per farlo, ma ho una breve funzione Elisp con un key binding in esecuzione rmarkdown::render(via shell-command) sull'attuale buffer-file-name, che aggiornerà il pdf nell'altra finestra.
daroczig,

1
@daroczig funzionerà per LaTeX, o semplicemente markdown?
Tyler,

@daroczig: nella mia risposta forse ho provato a fare proprio questo per i file Rnw (LaTeX + R). Per quanto riguarda i file Rmd (Rmd + R), che è più semplice, si prega di iniziare un post separato.
antonio

1
Non sono riuscito a ottenere una configurazione corretta. Devo prima lavorare a maglia con la trama polimerica. Quindi selezionare esportatore e tutto per creare il file .tex. Da lì devo correre il lattice. Ho provato ad adattare lo script sopra, ma il mio elisp non è ancora lì. Quello che voglio è lavorare a maglia il file .Rnw, creare il pdf (pdflatex) e visualizzarlo. Ho impostato il mio sistema, che se digito "Cc Ca" (Tex-Command-run-all) nel file latex, il pdf viene creato e visualizzato, ma non posso chiamare quella funzione sul file tex da my_knitr () . L'aiuto sarebbe apprezzato. (defun my_knitr () "Esegui Knitr in modalità R-Poly e crea e visualizza pdf" (interacti
Krisselack

@Krisselack sembra che le funzionalità che descrivo nella mia risposta di seguito siano state rimosse da ESS. Dovrò aggiornarlo per riflettere il nuovo approccio che usano
Tyler

Risposte:


12

AGGIORNARE

A partire da ESS 19.04, le librerie ess-nowebe ess-swvsono obsolete:

Di conseguenza, la mia risposta originale (sotto) non si applica più. Le funzionalità utilizzate da queste librerie sono ora fornite da polymode e la configurazione è più semplice. Per ottenere un supporto minimo, tutto ciò che serve è quello di installare il ess, polymodee poly-Rpacchetti (da Melpa, o dalla fonte, se è così che si rotolo).

Questo è tutto! Ora se apri un Rnwfile, dovresti vedere il PM-Rnwflag nella modeline e ci sarà un Polymodemenu in alto. Puoi tessere il tuo file in un .texfile tramite M-n w(o il menu polymode) ed esportarlo in .pdfvia M-n e(o il menu). Ti verrà richiesto un esportatore la prima volta che lo fai; Ho appena scelto knitr.

NOTA: export ( M-n e) esegue automaticamente il codice, genera il pdf e lo visualizza, tutto in una volta. Non sono stato in grado di ottenere quel comportamento "one-click" con la vecchia versione descritta di seguito.

I file generati avranno la parola -wovene -exportedaggiunti. Se non ti piace, puoi personalizzare le opzioni polymode-weaver-output-file-formate polymode-exporter-output-file-format.

Il processo è simile per i file RMarkdown ( .Rmd).

I dettagli completi sono forniti nel manuale del polimero

Risposta originale (obsoleta dopo ESS 19.04)

È necessario impostare tre variabili:

  1. ess-swv-pdflatex-commands, nel gruppo di personalizzazione ess-sweavedeve avere "pdflatex" come primo comando. cioè, dovrebbe assomigliare a:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, nel gruppo di personalizzazione ess-R, dovrebbe essere il valore"knitr"
  3. ess-pdf-viewer-prefnel gruppo di personalizzazione essa "emacsclient". Ciò presuppone che tu stia eseguendo il server emacs o che emacs sia in esecuzione in modalità --daemon. Dovresti anche usare gli strumenti pdf se possibile, dato che è di gran lunga preferibile al visualizzatore di pdf Emacs integrato.

Uso un hook per aggiungere due combinazioni di tasti per chiamare BibTeX e 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"))))

Fatto ciò, M-n slavorerà a maglia il documento, M-n blo bibtex e M-n Plo elaborerà con pdflatex.

Nota che Emacs non ha modo semplice di sapere quando il lavoro a maglia è completato, quindi non puoi impostarlo per lavorare a maglia e lattice in un solo passaggio; devi attivare manualmente pdflatex dopo aver visto che il lavoro a maglia è terminato.

Dati i molteplici passaggi qui - Rnw -> Latex -> PDF, non credo che tu possa ottenere Synctex per far scorrere insieme i tuoi file pdf e Rnw, ma sarei entusiasta di essere smentito.

Per quanto riguarda la disposizione delle finestre, non riesco mai a farli stare dove li voglio. Quindi per compensare sono diventato abbastanza abile nel mescolare finestre e buffer quando ne ho bisogno;)

Yihui ha pubblicato un breve video sul sito di Knitr a dimostrazione di ciò.


Ho fatto del mio meglio per estrarre dal mio .emacs tutta la configurazione necessaria e solo la configurazione necessaria. Potrei essermi perso qualcosa, è un po 'peloso lì dentro.
Tyler,

mi ha permesso di compilare documenti knitr. Ma prova ancora a trovare una combinazione a una chiave (per Rnw -> PDF). Spero sia possibile, dal momento che Rstudio ha questo.
drobnbobn

@Tyler: Grazie, la tua soluzione funziona come un oggetto di fascino (anche senza modificare la configurazione)! Pacchetti: ess, polymode, poly-r
Krisselack

5

Questa è una soluzione all-in-one. Sarà creare e visualizzare un PDF da un Rnw .
Nello specifico:

  1. Salvare il buffer Rnw e lavorarlo a maglia,
  2. Applicare un determinato motore LaTeX al file TeX risultante,
  3. Identificare l'eseguibile del motore BibTeX (ad esempio biber, bibtex8),
  4. Esegui il motore BibTeX sul file TeX se il file bib è più recente del file TeX,
  5. Eseguire di nuovo LaTeX, 6 Aprire il PDF risultante nel visualizzatore designato.

La procedura tenta di uscire con messaggi informativi se uno dei passaggi precedenti non riesce.
Un'istanza R verrà aperta, se necessario, o quella corrente viene utilizzata per mostrare il processo di lavoro a maglia.
L'output LaTeX viene inviato al buffer "TeX-output", che viene anche visualizzato in caso di errore di compilazione.

uso

Meta- x knit-meper creare e visualizzare il PDF.
Meta- x knit-me-clearper rimuovere i file intermedi LaTeX e knit-me.

La bibliografia richiede il pacchetto "biblatex", ovvero:

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

Il nome del motore bib (ad es bibtex. biber) Si ottiene analizzando la backendparola chiave.
\addbibresourceil comando viene analizzato per ottenere il file bibliografico: se foo.bibè più recente del file TeX, viene eseguito il motore bib. A questo proposito, solo il primo \addbibresourcecomando viene preso in considerazione se ce ne sono molti.

Personalizzare

Per visualizzare effettivamente il PDF, impostare il percorso eseguibile del visualizzatore con:

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

Possibilmente utilizzare un visualizzatore come SumatraPDF , che aggiorna automaticamente il PDF quando ricompilato e non blocca il file aperto impedendo nuove compilazioni.

Il motore LaTeX predefinito è pdflatex(assunto nel percorso corrente). Personalizza con:

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

Ovviamente potresti voler associare knit-mee knit-me-clearad alcune comode chiavi.

Appunti

Testato su Windows MiKTeX, con bibere bibtex8backend e GNU Emacs 25.1.1.

Codice 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")))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.