comment configurer le workflow Knitr dans Emacs?

18

RStudio fournit un moyen à un bouton pour produire un fichier PDF à partir de la source LaTeX + R avec Knitr. Il semble idéal pour faire des recherches reproductibles. Et j'essaye de configurer mes Emacs pour:

  • à gauche tampon Code LaTeX + R à la manière de Knitr;
  • au tampon de droite Aperçu de la sortie PDF;
  • une combinaison de touches pour la compilation.

Si c'est possible: comment dois-je configurer cela, s'il vous plaît?

(ESS fonctionne bien, mais je ne sais pas comment configurer Knitr-way et un bouton pour la compilation.)

drobnbobn
la source
Je suis presque sûr qu'il existe une meilleure façon de le faire, mais j'ai une courte fonction Elisp avec un raccourci clavier en cours d'exécution rmarkdown::render(via shell-command) sur le courant buffer-file-name, qui mettra à jour le pdf dans l'autre fenêtre.
daroczig
1
@daroczig cela fonctionnera-t-il pour LaTeX, ou tout simplement pour le démarque?
Tyler
@daroczig: dans ma réponse, j'ai peut-être essayé de faire exactement cela pour les fichiers Rnw (LaTeX + R). En ce qui concerne les fichiers Rmd (Rmd + R), ce qui est plus simple, veuillez commencer un article séparé.
antonio
1
Je n'ai pas pu obtenir une configuration correcte. Je dois d'abord tricoter avec du tissage polymode. Sélectionnez ensuite l'exportateur et tout pour créer le fichier .tex. De là, je dois faire couler du latex. J'ai essayé d'adapter le script ci-dessus, mais mon elisp n'est tout simplement pas encore là. Ce que je veux, c'est tricoter le fichier .Rnw, créer le pdf (pdflatex) et le visualiser. J'ai configuré mon système, que si je tape "Cc Ca" (Tex-Command-run-all) dans le fichier latex, le pdf est construit et affiché, mais je ne peux pas appeler cette fonction sur le fichier tex à partir de my_knitr () . De l'aide serait appréciée. (defun my_knitr () "Exécutez Knitr en mode R-Poly et créez et affichez le pdf" (interacti
Krisselack
@ Krisselack, il semble que les fonctionnalités que je décris dans ma réponse ci-dessous ont été supprimées d'ESS. Je vais devoir le mettre à jour pour refléter la nouvelle approche qu'ils utilisent
Tyler

Réponses:

12

MISE À JOUR

Depuis ESS 19.04, les bibliothèques ess-nowebet ess-swvsont obsolètes:

Par conséquent, ma réponse originale (ci-dessous) ne s'applique plus. Les fonctionnalités que ces bibliothèques fournissaient auparavant sont désormais fournies par polymode, et la configuration est plus simple. Pour obtenir un support minimal, tout ce dont vous avez besoin est d'installer les packages ess, polymodeet poly-R(depuis MELPA, ou depuis la source si c'est comme ça que vous roulez).

C'est ça! Maintenant, si vous ouvrez un Rnwfichier, vous devriez voir le PM-Rnwdrapeau dans la modélisation, et il y aura un Polymodemenu en haut. Vous pouvez tisser votre fichier dans un .texfichier via M-n w(ou le menu polymode) et l'exporter vers .pdfvia M-n e(ou le menu). Vous serez invité à entrer un exportateur la première fois que vous effectuez cette opération; Je viens de choisir knitr.

REMARQUE: l'export ( M-n e) exécute automatiquement votre code, génère le pdf et l'affiche, d'un seul coup. Je n'ai pas pu obtenir ce comportement "en un clic" avec l'ancienne version décrite ci-dessous.

Les fichiers générés auront le mot -wovenet seront -exportedajoutés. Si vous n'aimez pas cela, vous pouvez personnaliser les options polymode-weaver-output-file-formatet polymode-exporter-output-file-format.

Le processus est similaire pour les fichiers RMarkdown ( .Rmd).

Tous les détails sont fournis dans le manuel du polymode

Réponse originale (obsolète après ESS 19.04)

Trois variables doivent être définies:

  1. ess-swv-pdflatex-commands, dans le groupe de personnalisation ess-sweavedoit avoir "pdflatex" comme première commande. c'est-à-dire qu'il devrait ressembler à quelque chose comme:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, dans le groupe de personnalisation ess-R, doit être la valeur"knitr"
  3. ess-pdf-viewer-prefdans le groupe de personnalisation essà "emacsclient". Cela suppose que vous exécutez le serveur emacs ou qu'emacs s'exécute en mode --daemon. Vous devriez également utiliser des outils pdf si possible, car il est de loin préférable à la visionneuse pdf Emacs intégrée.

J'utilise un hook pour ajouter deux raccourcis clavier pour appeler BibTeX et 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"))))

Une fois cela fait, M-n sva tricoter votre document, M-n ble bibtex et M-n Ple traitera avec pdflatex.

Notez qu'il n'y a pas de moyen simple pour Emacs de savoir quand le tricot est terminé, vous ne pouvez donc pas le configurer pour tricoter et latex en une seule étape; vous devez déclencher manuellement pdflatex après avoir vu le tricot terminé.

Étant donné les multiples étapes ici - Rnw -> Latex -> PDF, je ne pense pas que vous puissiez demander à Synctex de conserver vos fichiers pdf et Rnw pour faire défiler ensemble, mais je serais ravi d'avoir tort.

Quant à la disposition des fenêtres, je ne peux jamais les faire rester là où je les veux. Donc, pour compenser, je suis devenu assez habile pour mélanger les fenêtres et les tampons lorsque j'en ai besoin;)

Yihui a posté une courte vidéo sur le site de knitr démontrant cela.

Tyler
la source
J'ai fait de mon mieux pour extraire toutes les config nécessaires, et seulement la config nécessaire, de mes .emacs. J'ai peut-être raté quelque chose, c'est un peu poilu là-dedans.
Tyler
cela m'a permis de compiler des documents knitr. Mais essayez toujours de trouver un combo à une touche (pour Rnw -> PDF). J'espère que c'est possible, car Rstudio en a.
drobnbobn
@Tyler: Merci, votre solution fonctionne comme un ootb de charme (même sans éditer la config)! Paquets: ess, polymode, poly-r
Krisselack
5

Il s'agit d'une solution tout-en-un. Il créera et affichera un PDF à partir d'un Rnw .
Plus précisément, il:

  1. Enregistrez le tampon Rnw et tricotez-le,
  2. Appliquer un moteur LaTeX donné au fichier TeX résultant,
  3. Identifier l'exécutable du moteur BibTeX (par exemple biber, bibtex8),
  4. Exécutez le moteur BibTeX sur le fichier TeX si le fichier bib est plus récent que le fichier TeX,
  5. Exécutez à nouveau LaTeX, 6 Ouvrez le fichier PDF résultant dans la visionneuse désignée.

La procédure tente de quitter avec des messages informatifs si l'une des étapes ci-dessus échoue.
Une instance R sera ouverte, si nécessaire, ou l'instance actuelle est utilisée pour montrer le processus de tricotage.
La sortie LaTeX est envoyée au tampon "TeX-output", qui est également sauté en cas d'erreur de compilation.

Usage

Meta- x knit-mepour créer et visualiser le PDF.
Meta- x knit-me-clearpour supprimer les fichiers intermédiaires LaTeX et knit-me.

La bibliographie nécessite le package "biblatex", c'est-à-dire:

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

Le nom du bib-engine (par exemple bibtex, biber) est obtenu en analysant le backendmot - clé.
\addbibresourceLa commande est analysée pour obtenir le fichier de bibliographie: si elle foo.bibest plus récente que le fichier TeX, le moteur bib est exécuté. A cet égard, seule la première \addbibresourcecommande est prise en compte s'il y en a plusieurs.

Personnaliser

Pour visualiser réellement le PDF, définissez le chemin exécutable du visualiseur avec:

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

Utilisez éventuellement une visionneuse comme SumatraPDF , qui met automatiquement à jour le PDF lors de la recompilation et ne bloque pas le fichier ouvert empêchant de nouvelles compilations.

Le moteur LaTeX par défaut est pdflatex(supposé dans le chemin actuel). Personnalisez avec:

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

Bien sûr, vous voudrez peut-être lier knit-meet knit-me-clearà certaines touches pratiques.

Remarques

Testé sous Windows MiKTeX, avec biberet bibtex8backends et GNU Emacs 25.1.1.

Code 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
la source