cara mengatur alur kerja Knitr di Emacs?

18

RStudio menyediakan cara satu tombol untuk menghasilkan file PDF dari sumber LaTeX + R dengan Knitr. Ini terlihat bagus untuk melakukan penelitian yang dapat direproduksi. Dan saya mencoba mengkonfigurasi Emacs saya untuk:

  • di buffer kiri kode LaTeX + R dengan cara Knitr;
  • pada pratinjau output PDF buffer kanan;
  • satu kombinasi tombol untuk kompilasi.

Jika memungkinkan: bagaimana saya harus mengatur ini?

(ESS berfungsi dengan baik, tapi saya tidak tahu cara mengatur Knitr-way dan satu-tombol-untuk-kompilasi.)

drobnbobn
sumber
Saya cukup yakin ada cara yang lebih baik untuk melakukan itu, tetapi saya memiliki fungsi Elisp pendek dengan pengikatan kunci berjalan rmarkdown::render(via shell-command) pada saat ini buffer-file-name, yang akan memperbarui pdf di jendela lain.
daroczig
1
@daroczig apakah itu akan berfungsi untuk LaTeX, atau hanya penurunan harga?
Tyler
@ dococzig: dalam jawaban saya, saya mungkin mencoba melakukan ini untuk file Rnw (LaTeX + R). Mengenai file Rmd (Rmd + R), yang lebih sederhana, silakan mulai posting terpisah.
antonio
1
Saya tidak bisa mendapatkan pengaturan yang tepat. Saya harus merajut dulu dengan polymode-weave. Kemudian pilih eksportir dan semuanya untuk membuat file .tex. Dari sana saya harus menjalankan lateks. Saya mencoba untuk mengadaptasi Script di atas, tetapi elisp saya belum ada di sana. Yang saya inginkan adalah merajut file .Rnw, membuat pdf (pdflatex) dan melihatnya. Saya mengatur sistem saya, bahwa jika saya mengetik "Cc Ca" (Tex-Command-run-all) dalam file lateks, pdf dibuat dan dilihat, tetapi saya tidak dapat memanggil fungsi itu pada file-tex dari my_knitr () . Bantuan akan dihargai. (defun my_knitr () "Jalankan Knitr dalam mode R-Poly dan buat dan lihat pdf" (interaksinya
Krisselack
@ Krisselack sepertinya fitur yang saya jelaskan dalam jawaban saya di bawah ini telah dihapus dari ESS. Saya harus memperbaruinya untuk mencerminkan pendekatan baru yang mereka gunakan
Tyler

Jawaban:

12

MEMPERBARUI

Pada ESS 19.04, perpustakaan ess-nowebdan ess-swvsudah usang:

Akibatnya, jawaban asli saya (di bawah) tidak berlaku lagi. Fitur-fitur yang digunakan oleh perpustakaan-perpustakaan ini sekarang disediakan oleh polymode, dan konfigurasinya lebih sederhana. Untuk mendapatkan dukungan minimal, yang Anda butuhkan adalah menginstal ess, polymodedan poly-Rpaket (dari MELPA, atau dari sumber jika itu cara Anda menggulung).

Itu dia! Sekarang jika Anda membuka Rnwfile, Anda akan melihat PM-Rnwbendera di modeline, dan akan ada Polymodemenu di atas. Anda dapat menenun file Anda menjadi .texfile melalui M-n w(atau menu polymode), dan mengekspornya ke .pdfmelalui M-n e(atau menu). Anda akan diminta untuk eksportir saat pertama kali melakukan ini; Saya baru saja memilih knitr.

CATATAN: mengekspor ( M-n e) secara otomatis menjalankan kode Anda, menghasilkan pdf dan menampilkannya, semuanya sekaligus. Saya tidak bisa mendapatkan perilaku "satu klik" dengan versi lama yang dijelaskan di bawah ini.

File yang dihasilkan akan memiliki kata -wovendan -exportedditambahkan. Jika Anda tidak suka ini, Anda dapat menyesuaikan opsi polymode-weaver-output-file-formatdan polymode-exporter-output-file-format.

Prosesnya mirip untuk file RMarkdown ( .Rmd).

Rincian lengkap disediakan di manual polymode

Jawaban Asli (usang setelah ESS 19.04)

Tiga variabel perlu diatur:

  1. ess-swv-pdflatex-commands, dalam grup kustomisasi ess-sweaveharus memiliki "pdflatex" sebagai perintah pertama. yaitu, harus terlihat seperti:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, dalam grup penyesuaian ess-R, harus menjadi nilainya"knitr"
  3. ess-pdf-viewer-prefdalam grup penyesuaian essuntuk "emacsclient". Ini mengasumsikan Anda menjalankan server emacs, atau emacs berjalan dalam mode --daemon. Anda juga harus menggunakan alat pdf jika memungkinkan, karena jauh lebih disukai daripada penampil pdf Emacs bawaan.

Saya menggunakan hook untuk menambahkan dua keybindings untuk memanggil BibTeX dan 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"))))

Setelah ini selesai, M-n sakan merajut dokumen Anda, M-n bakan bibtex, dan M-n Pakan memprosesnya dengan pdflatex.

Perhatikan bahwa tidak ada cara sederhana bagi Emacs untuk mengetahui kapan merajut selesai, jadi Anda tidak dapat mengatur ini untuk merajut & lateks dalam satu langkah; Anda harus secara manual memicu pdflatex setelah Anda melihat rajutan telah selesai.

Diberikan beberapa langkah di sini - Rnw -> Lateks -> PDF, saya tidak berpikir Anda bisa mendapatkan Synctex untuk menyimpan file pdf dan Rnw Anda untuk digabungkan, tetapi saya akan senang terbukti salah.

Adapun pengaturan jendela, saya tidak pernah bisa membuat mereka tetap di tempat yang saya inginkan. Jadi untuk mengkompensasi saya telah menjadi sangat mahir mengocok jendela dan buffer sesuai kebutuhan saya;)

Yihui memposting video pendek di situs rajutan yang menunjukkan sebagian dari ini.

Tyler
sumber
Saya telah melakukan yang terbaik untuk mengekstrak semua konfigurasi yang diperlukan, dan hanya konfigurasi yang diperlukan, dari .emacs saya. Saya mungkin telah melewatkan sesuatu, itu agak berbulu di sana.
Tyler
itu memungkinkan saya untuk menyusun dokumen rajutan. Tetapi masih mencoba untuk menemukan kombo satu tombol (untuk Rnw -> PDF). Semoga itu mungkin, karena Rstudio memilikinya.
drobnbobn
@ Tyler: Terima kasih, solusi Anda berfungsi seperti ootb pesona (bahkan tanpa mengedit konfigurasi)! Paket: ess, polymode, poly-r
Krisselack
5

Ini adalah solusi menyeluruh. Ini akan membuat dan menampilkan PDF dari Rnw .
Secara khusus itu akan:

  1. Simpan buffer Rnw dan rajut,
  2. Terapkan mesin LaTeX yang diberikan ke file TeX yang dihasilkan,
  3. Identifikasi mesin BibTeX yang dapat dieksekusi (mis. Biber, bibtex8),
  4. Jalankan mesin BibTeX pada file TeX jika file bib lebih baru dari file TeX,
  5. Jalankan LaTeX lagi, 6 Buka menghasilkan PDF di penampil yang ditunjuk.

Prosedur mencoba untuk keluar dengan pesan informatif jika salah satu langkah di atas gagal.
Sebuah instance R akan dibuka, jika perlu, atau yang sekarang digunakan untuk menunjukkan proses merajut.
Output LaTeX dikirim ke buffer "TeX-output", yang juga muncul jika terjadi kesalahan kompilasi.

Pemakaian

Meta- x knit-meuntuk membuat dan melihat PDF.
Meta- x knit-me-clearuntuk menghapus file LaTeX menengah dan knit-me.

Bibliografi membutuhkan paket "biblatex", yaitu:

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

Nama mesin bib (misalnya bibtex, biber) diperoleh dengan menguraikan backendkata kunci.
\addbibresourceperintah diuraikan untuk mendapatkan file bibliografi: jika foo.biblebih baru dari file TeX mesin bib dijalankan. Dalam hal ini, hanya \addbibresourceperintah pertama yang diperhitungkan jika ada banyak.

Sesuaikan

Untuk benar-benar melihat PDF, tetapkan jalur yang dapat dieksekusi penampil dengan:

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

Mungkin menggunakan penampil seperti SumatraPDF , yang secara otomatis memperbarui PDF ketika dikompilasi ulang dan tidak memblokir file yang dibuka mencegah kompilasi baru.

Mesin LaTeX default adalah pdflatex(diasumsikan di jalur saat ini). Sesuaikan dengan:

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

Tentu saja Anda mungkin ingin mengikat knit-medan knit-me-clearbeberapa tombol yang mudah digunakan.

Catatan

Diuji pada Windows MiKTeX, dengan biberdan bibtex8backends dan GNU Emacs 25.1.1.

Kode 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
sumber