jak skonfigurować przepływ pracy Knitr w Emacsie?

18

RStudio zapewnia jeden przycisk do tworzenia pliku PDF ze źródła LaTeX + R za pomocą Knitr. Wygląda doskonale na prowadzenie powtarzalnych badań. Próbuję skonfigurować Emacsa do:

  • w lewym buforze kod LaTeX + R w sposób Knitr;
  • w prawym buforze podgląd wyjścia PDF;
  • jedna kombinacja klawiszy do kompilacji.

Jeśli to możliwe: jak to ustawić, proszę?

(ESS działa dobrze, ale nie wiem, jak skonfigurować Knitr-way i kompilację za pomocą jednego przycisku).

drobnbobn
źródło
Jestem prawie pewien, że jest na to lepszy sposób, ale mam krótką funkcję Elisp z powiązaniem klawiszy działającym rmarkdown::render(przez shell-command) na bieżącym buffer-file-name, który zaktualizuje pdf w drugim oknie.
daroczig
1
@daroczig, czy to zadziała dla LaTeXa, czy może po prostu przecenić?
Tyler
@daroczig: w mojej odpowiedzi prawdopodobnie próbowałem zrobić to tylko dla plików Rnw (LaTeX + R). Jeśli chodzi o pliki Rmd (Rmd + R), co jest prostsze, proszę rozpocząć oddzielny post.
Antonio
1
Nie mogłem uzyskać właściwej konfiguracji. Muszę najpierw wykonać na drutach splot wielomodowy. Następnie wybierz eksportera i wszystko, aby utworzyć plik .tex. Stamtąd muszę uruchomić lateks. Próbowałem dostosować powyższy skrypt, ale mojego elipsa jeszcze nie ma. Chcę zrobić plik .Rnw, utworzyć plik pdf (pdflatex) i wyświetlić go. Skonfigurowałem swój system, aby po wpisaniu „Cc Ca” (Tex-Command-run-all) w pliku lateksowym pdf został zbudowany i przeglądany, ale nie mogę wywołać tej funkcji w pliku tex z my_knitr () . Pomoc będzie mile widziana. (defun my_knitr () "Uruchom Knitr w trybie R-Poly oraz utwórz i wyświetl pdf" (interaktywne
Krisselack
@Krisselack wygląda na to, że funkcje, które opisuję w mojej odpowiedzi poniżej, zostały usunięte z ESS. Będę musiał go zaktualizować, aby odzwierciedlić nowe podejście, którego używają
Tyler

Odpowiedzi:

12

AKTUALIZACJA

Od wersji ESS 19.04 biblioteki ess-nowebi ess-swvsą przestarzałe:

W związku z tym moja pierwotna odpowiedź (poniżej) nie ma już zastosowania. Funkcje, które zapewniały te biblioteki, są teraz obsługiwane przez tryb wielomodowy, a konfiguracja jest prostsza. Aby uzyskać minimalne wsparcie, co potrzebne jest do zainstalowania ess, polymodea poly-Rpakiety (od MELPA lub ze źródła, jeśli to w jaki sposób rolka).

Otóż ​​to! Teraz, jeśli otworzysz Rnwplik, powinieneś zobaczyć PM-Rnwflagę w modeline, a Polymodeu góry będzie menu. Możesz wplatać swój plik do .texpliku za pomocą M-n w(lub menu trybu wielomodowego) i eksportować go do .pdfza pośrednictwem M-n e(lub menu). Za pierwszym razem zostaniesz poproszony o eksportera; Właśnie wybrałem knitr.

UWAGA: export ( M-n e) automatycznie uruchamia kod, generuje plik pdf i wyświetla go za jednym razem. Nie byłem w stanie uzyskać tego zachowania „jednym kliknięciem” w starej wersji opisanej poniżej.

Wygenerowane pliki będą zawierały słowo -woveni -exporteddołączone. Jeśli Ci się nie podoba, możesz dostosować opcje polymode-weaver-output-file-formati polymode-exporter-output-file-format.

Proces jest podobny w przypadku plików RMarkdown ( .Rmd).

Pełne szczegóły znajdują się w instrukcji obsługi trybu wielomodowego

Oryginalna odpowiedź (przestarzała po ESS 19.04)

Należy ustawić trzy zmienne:

  1. ess-swv-pdflatex-commands, w grupie dostosowywania ess-sweavemusi mieć „pdflatex” jako pierwsze polecenie. tzn. powinien wyglądać mniej więcej tak:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, w grupie dostosowywania ess-Rpowinna być wartością"knitr"
  3. ess-pdf-viewer-prefw grupie dostosowywania essdo "emacsclient". Zakłada się, że korzystasz z serwera emacs lub emacs działa w trybie --daemon. Powinieneś także używać narzędzi pdf, jeśli to w ogóle możliwe, ponieważ jest to zdecydowanie lepsze niż wbudowana przeglądarka pdf Emacsa.

Używam haka, aby dodać dwa skróty klawiszowe do wywoływania BibTeX i 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"))))

Gdy to zrobisz, M-n szrobisz na drutach dokument, wykonasz M-n bbibteks i M-n Pprzetworzy go za pomocą pdflatex.

Zauważ, że Emacs nie ma prostego sposobu, aby dowiedzieć się, kiedy dziewiarstwo jest zakończone, więc nie możesz ustawić go na drutach i lateksie w jednym kroku; musisz ręcznie uruchomić pdflatex, gdy zobaczysz, że dziewiarstwo zostało zakończone.

Biorąc pod uwagę wiele kroków tutaj - Rnw -> Lateks -> PDF, nie sądzę, że możesz zmusić Synctex do przechowywania plików pdf i Rnw, aby przewijać się razem, ale byłbym podekscytowany, gdybym udowodnił, że się mylę.

Jeśli chodzi o układ okien, nigdy nie mogę ich zatrzymać tam, gdzie chcę. Aby to zrekompensować, stałem się biegły w tasowaniu okien i buforów, gdy ich potrzebuję;)

Yihui opublikował krótki film na stronie knitr, w którym demonstruje niektóre z nich.

Tyler
źródło
Zrobiłem co w mojej mocy, aby wyodrębnić wszystkie niezbędne konfiguracje i tylko niezbędne konfiguracje z moich .emacs. Mogłem coś przeoczyć, tam jest trochę owłosione.
Tyler,
umożliwiło mi to skompilowanie knitr dokumentów. Ale nadal spróbuj znaleźć kombinację jednego klucza (dla Rnw -> PDF). Mam nadzieję, że jest to możliwe, ponieważ Rstudio ma to.
drobnbobn
@Tyler: Dziękuję, twoje rozwiązanie działa jak ootb uroku (nawet bez edycji konfiguracji)! Pakiety: ess, polimod, poly-r
Krisselack
5

To rozwiązanie typu „wszystko w jednym”. Będzie ona tworzyć i wyświetlać pliki PDF z RNW .
W szczególności:

  1. Zapisz bufor Rnw i zrób go,
  2. Zastosuj dany silnik LaTeX do wynikowego pliku TeX,
  3. Zidentyfikuj plik wykonywalny silnika BibTeX (np. Biber, bibtex8),
  4. Uruchom silnik BibTeX na pliku TeX, jeśli plik BIB jest nowszy niż plik TeX,
  5. Uruchom ponownie LaTeX, 6 Otwórz wynikowy plik PDF w wyznaczonej przeglądarce.

Procedura próbuje wyjść z komunikatami informacyjnymi, jeśli jeden z powyższych kroków nie powiedzie się.
Instancja R zostanie otwarta, jeśli to konieczne, lub bieżąca zostanie wykorzystana do pokazania procesu dziania.
Dane wyjściowe LaTeX są wysyłane do bufora „TeX-output”, który również pojawia się w przypadku błędu kompilacji.

Stosowanie

Meta- w x knit-mecelu utworzenia i przeglądania pliku PDF.
Meta- x knit-me-clearaby usunąć pośrednie pliki LaTeX i knit-me.

Bibliografia wymaga pakietu „biblatex”, tj .:

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

Nazwa Bib-silnik (np bibtex, biber) otrzymuje parsowania backendsłowa kluczowego.
\addbibresourcepolecenie jest analizowane w celu uzyskania pliku bibliografii: jeśli foo.bibjest nowszy niż plik TeX, uruchamiany jest silnik śliniak. Pod tym względem \addbibresourcebrane jest pod uwagę tylko pierwsze polecenie, jeśli jest ich wiele.

Dostosuj

Aby faktycznie wyświetlić plik PDF, ustaw ścieżkę wykonywalną przeglądarki za pomocą:

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

Ewentualnie użyj przeglądarki takiej jak SumatraPDF , która automatycznie aktualizuje plik PDF po ponownej kompilacji i nie blokuje otwartego pliku, uniemożliwiając nowe kompilacje.

Domyślny silnik LaTeX to pdflatex(przyjęty w bieżącej ścieżce). Dostosuj za pomocą:

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

Oczywiście możesz chcieć powiązać knit-mei knit-me-clearkilka wygodnych kluczy.

Notatki

Testowane w Windows MiKTeX, z biberi bibtex8backendami oraz GNU Emacs 25.1.1.

Kod 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
źródło