Wie richte ich den Knitr-Workflow in Emacs ein?

18

RStudio bietet eine Ein-Knopf-Methode zum Erstellen von PDF-Dateien aus LaTeX + R-Quellen mit Knitr. Es eignet sich hervorragend für reproduzierbare Untersuchungen. Und ich versuche, meinen Emacs für Folgendes zu konfigurieren:

  • am linken Puffer LaTeX + R-Code in Knitr-Weise;
  • am rechten Puffer Vorschau der PDF-Ausgabe;
  • eine Tastenkombination zum Kompilieren.

Wenn es möglich ist: Wie soll ich das einrichten, bitte?

(ESS funktioniert einwandfrei, aber ich kann Knitr-way und One-Button-for-Compiling nicht einrichten.)

drobnbobn
quelle
Ich bin mir ziemlich sicher, dass es einen besseren Weg gibt, aber ich habe eine kurze Elisp-Funktion mit einer Tastenzuordnung rmarkdown::render(via shell-command) auf dem aktuellen buffer-file-name, die das PDF im anderen Fenster aktualisiert.
Daroczig
1
@daroczig Funktioniert das für LaTeX oder nur für Markdown?
Tyler
@daroczig: in meiner antwort habe ich möglicherweise versucht, genau dies für rnw-dateien (latex + r) zu tun. Für Rmd-Dateien (Rmd + R), die einfacher sind, starten Sie bitte einen separaten Beitrag.
Antonio
1
Ich konnte mich nicht richtig einrichten. Ich muss zuerst mit Polymode-Weben stricken. Wählen Sie dann exporter und alles aus, um die .tex-Datei zu erstellen. Von dort muss ich Latex laufen lassen. Ich habe versucht, das obige Skript anzupassen, aber mein elisp ist einfach noch nicht da. Ich möchte die .Rnw-Datei stricken, das PDF (pdflatex) erstellen und es ansehen. Ich richte mein System so ein, dass, wenn ich "Cc Ca" (Tex-Command-run-all) in die Latex-Datei eingebe, das PDF erstellt und angezeigt wird, aber ich kann diese Funktion in der Tex-Datei nicht von my_knitr () aus aufrufen . Hilfe wäre dankbar. (defun my_knitr () "Führe Knitr im R-Poly Modus aus und erstelle und betrachte pdf" (interacti
Krisselack
@Krisselack, es sieht so aus, als ob die Funktionen, die ich in meiner Antwort unten beschreibe, aus ESS entfernt wurden. Ich muss es aktualisieren, um den neuen Ansatz widerzuspiegeln, den sie verwenden
Tyler

Antworten:

12

AKTUALISIEREN

Ab ESS 19.04 sind die Bibliotheken ess-nowebund ess-swvveraltet:

Folglich gilt meine ursprüngliche Antwort (unten) nicht mehr. Die Funktionen, die diese Bibliotheken zur Verfügung stellen, werden jetzt im Polymode bereitgestellt, und die Konfiguration ist einfacher. Um minimale Unterstützung zu erhalten, alles , was Sie brauchen , ist die Installation ess, polymodeund poly-RPakete (von Melpa, oder von der Quelle , wenn das ist , wie Sie rollen).

Das ist es! Wenn Sie nun eine RnwDatei öffnen , sollten Sie PM-Rnwdie Markierung in der Modeline sehen und es wird ein PolymodeMenü oben angezeigt . Sie können Ihre Datei .texüber M-n w(oder das Polymode-Menü) in eine Datei verweben und .pdfüber M-n e(oder das Menü) in eine Datei exportieren . Wenn Sie dies zum ersten Mal tun, werden Sie aufgefordert, einen Exporteur anzugeben. Ich habe gerade gepflückt knitr.

HINWEIS: exporting ( M-n e) führt Ihren Code automatisch aus, generiert das PDF und zeigt es auf einmal an. Ich konnte dieses "Ein-Klick" -Verhalten mit der alten, unten beschriebenen Version nicht erzielen.

Die erzeugten Dateien werden das Wort haben -wovenund -exportedangehängt. Wenn Ihnen das nicht gefällt, können Sie die Optionen polymode-weaver-output-file-formatund anpassen polymode-exporter-output-file-format.

Der Vorgang ist für RMarkdown-Dateien ( .Rmd) ähnlich .

Ausführliche Informationen finden Sie im Polymode-Handbuch

Ursprüngliche Antwort (veraltet nach ESS 19.04)

Drei Variablen müssen gesetzt werden:

  1. ess-swv-pdflatex-commandsIn der Anpassungsgruppe ess-sweavemuss "pdflatex" als erster Befehl angegeben werden. Das heißt, es sollte ungefähr so ​​aussehen:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processorsollte in der Anpassungsgruppe ess-Rder Wert sein"knitr"
  3. ess-pdf-viewer-prefin der Anpassungsgruppe esszu "emacsclient". Dies setzt voraus, dass Sie den Emacs-Server ausführen oder dass der Emacs im --daemon-Modus ausgeführt wird. Sie sollten nach Möglichkeit auch PDF-Tools verwenden , da dies dem integrierten Emacs-PDF-Viewer weit vorzuziehen ist.

Ich benutze einen Haken, um zwei Tastenkombinationen zum Aufrufen von BibTeX und texi2pdf hinzuzufügen:

(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"))))

Sobald dies erledigt ist, M-n swird Ihr Dokument gestrickt, M-n bbibtex und M-n Pmit pdflatex verarbeitet.

Beachten Sie, dass es für Emacs keine einfache Möglichkeit gibt, festzustellen, wann das Stricken abgeschlossen ist. Sie können dies also nicht in einem Schritt auf Stricken und Latex einstellen. Sie müssen pdflatex manuell auslösen, nachdem Sie gesehen haben, dass das Stricken beendet ist.

Angesichts der vielen Schritte hier - Rnw -> Latex -> PDF, glaube ich nicht, dass Sie Synctex dazu bringen können, Ihre PDF- und Rnw-Dateien zusammen zu scrollen, aber ich würde mich freuen, wenn ich mich als falsch erweisen würde.

Was die Fensteranordnung betrifft, kann ich sie niemals dazu bringen, dort zu bleiben, wo ich sie haben möchte. Um das zu kompensieren, bin ich ziemlich geschickt darin geworden, Fenster und Puffer so zu mischen, wie ich sie brauche;)

Yihui hat ein kurzes Video auf der Knitr-Site gepostet, das einige davon demonstriert.

Tyler
quelle
Ich habe mein Bestes getan, um die gesamte erforderliche Konfiguration und nur die erforderliche Konfiguration aus meinen .emacs-Dateien zu extrahieren. Ich habe vielleicht etwas verpasst, es ist irgendwie haarig da drin.
Tyler
es hat mir ermöglicht, knitr-dokumente zusammenzustellen. Versuchen Sie dennoch, eine Ein-Tasten-Kombination zu finden (für Rnw -> PDF). Hoffe es ist möglich, da Rstudio dies hat.
Drobnbobn
@ Tyler: Danke, deine Lösung funktioniert wie ein Zauber ootb (auch ohne die Konfiguration zu bearbeiten)! Pakete: ess, polymode, poly-r
Krisselack
5

Dies ist eine Komplettlösung. Es wird ein PDF aus einem Rnw erstellt und angezeigt .
Insbesondere wird es:

  1. Speichern Sie den Rnw-Puffer und stricken Sie es,
  2. Wenden Sie eine bestimmte LaTeX-Engine auf die resultierende TeX-Datei an.
  3. Identifizieren Sie die ausführbare Datei der BibTeX-Engine (z. B. biber, bibtex8).
  4. Führen Sie die BibTeX-Engine für die TeX-Datei aus, wenn die Bib-Datei neuer als die TeX-Datei ist.
  5. Führen Sie LaTeX erneut aus. 6 Öffnen Sie die resultierende PDF-Datei im festgelegten Viewer.

Die Prozedur versucht, mit informativen Meldungen zu beenden, wenn einer der obigen Schritte fehlschlägt.
Bei Bedarf wird eine R-Instanz geöffnet oder die aktuelle Instanz wird zum Anzeigen des Strickprozesses verwendet.
Die LaTeX-Ausgabe wird an den "TeX-Ausgabe" -Puffer gesendet, der im Falle eines Kompilierungsfehlers ebenfalls abgerufen wird.

Verwendung

Meta- x knit-meum das PDF zu erstellen und anzuzeigen.
Meta- x knit-me-clearum zwischengeschaltete LaTeX-Dateien zu entfernen und knit-me.

Die Bibliographie erfordert das "biblatex" -Paket, dh:

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

Der Name des bib-Motors (z bibtex, biber) erhalten wird das Parsen backendSchlüsselwort.
\addbibresourceDer Befehl wird analysiert, um die Bibliographiedatei abzurufen. Wenn sie foo.bibneuer als die TeX-Datei ist, wird die Bib-Engine ausgeführt. In dieser Hinsicht wird nur der erste \addbibresourceBefehl berücksichtigt, wenn es viele gibt.

Anpassen

Um die PDF-Datei tatsächlich anzuzeigen, legen Sie den ausführbaren Pfad des Viewers wie folgt fest:

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

Verwenden Sie möglicherweise einen Viewer wie SumatraPDF , der die PDF-Datei beim erneuten Kompilieren automatisch aktualisiert und die geöffnete Datei nicht blockiert, wodurch neue Kompilierungen verhindert werden.

Die Standard-LaTeX-Engine ist pdflatex(im aktuellen Pfad angenommen). Anpassen mit:

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

Natürlich möchten Sie möglicherweise knit-meund knit-me-clearan einige bequeme Schlüssel binden .

Anmerkungen

Getestet in Windows MiKTeX, mit biberund bibtex8Backends und GNU Emacs 25.1.1.

Elisp-Code

;; (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
quelle