วิธีการตั้งค่าเวิร์กโฟลว์ Knitr ใน Emacs?


18

RStudio ให้วิธีการเพียงปุ่มเดียวในการผลิตไฟล์ PDF จากแหล่ง LaTeX + R กับ Knitr มันดูดีสำหรับการทำวิจัยที่ทำซ้ำได้ และฉันกำลังพยายามกำหนดค่า Emac ของฉันสำหรับ:

  • ที่บัฟเฟอร์ด้านซ้าย LaTeX + รหัส R ในทาง Knitr;
  • ที่บัฟเฟอร์การแสดงตัวอย่างเอาต์พุต PDF แบบขวา;
  • การรวมคีย์หนึ่งครั้งสำหรับการรวบรวม

หากเป็นไปได้: ฉันจะติดตั้งสิ่งนี้ได้อย่างไรโปรด?

(ESS ใช้งานได้ดี แต่ฉันไม่รู้วิธีตั้งค่า Knitr-way และปุ่มเดียวสำหรับการคอมไพล์)


ฉันค่อนข้างมั่นใจว่ามีวิธีที่ดีกว่าในการทำเช่นนั้น แต่ฉันมีฟังก์ชั่น Elisp สั้น ๆ พร้อมการเชื่อมโยงคีย์ที่ทำงานอยู่rmarkdown::render(ผ่านshell-command) ในปัจจุบันbuffer-file-nameซึ่งจะอัปเดตไฟล์ pdf ในหน้าต่างอื่น
daroczig

1
@daroczig จะใช้งานได้กับ LaTeX หรือเพียงแค่ markdown?
ไทเลอร์

@daroczig: ในคำตอบของฉันฉันอาจจะลองทำแค่นี้สำหรับไฟล์ Rnw (LaTeX + R) ตามไฟล์ Rmd (Rmd + R) ซึ่งง่ายกว่าโปรดเริ่มโพสต์แยกต่างหาก
อันโตนิโอ

1
ฉันไม่สามารถรับการตั้งค่าที่เหมาะสม ฉันต้องถักก่อนด้วย polymode-woven จากนั้นเลือกผู้ส่งออกและทุกอย่างเพื่อสร้างไฟล์. tex จากนั้นฉันต้องวิ่งเล่นน้ำยาง ฉันพยายามที่จะปรับสคริปต์ข้างต้น แต่สิ่งที่ฉันทำยังไม่ได้อยู่ที่นั่น สิ่งที่ฉันต้องการคือการถัก. Rw-file สร้าง pdf (pdflatex) และดู ฉันตั้งค่าระบบของฉันว่าถ้าฉันพิมพ์ "Cc Ca" (Tex-Command-run-all) ในไฟล์ลาเท็กซ์ไฟล์ pdf จะถูกสร้างและดู แต่ฉันไม่สามารถเรียกใช้ฟังก์ชันนั้นในไฟล์เท็กซัสจาก my_knitr () . ความช่วยเหลือจะได้รับการชื่นชม (กำหนด my_knitr () "เรียกใช้ Knitr ในโหมด R-Poly และสร้างและดู pdf" (การโต้ตอบ
Krisselack

@Krisselack ดูเหมือนว่าคุณสมบัติที่ฉันอธิบายในคำตอบของฉันด้านล่างถูกลบออกจาก ESS ฉันจะต้องอัปเดตเพื่อให้สอดคล้องกับวิธีการใหม่ที่พวกเขาใช้
ไทเลอร์

คำตอบ:


12

UPDATE

ตั้งแต่ ESS 19.04 ห้องสมุดess-nowebและess-swvล้าสมัยแล้ว:

  • ไลบรารีสำหรับการวิเคราะห์ข้อมูลที่รู้แจ้งจะล้าสมัยและไม่ได้โหลดตามค่าเริ่มต้น ซึ่งรวมถึง 'ess-noweb', 'ess-swv' และฟังก์ชันที่เกี่ยวข้องเช่น 'Rnw-mode' ผู้ใช้ควรเปลี่ยนไปใช้แพ็คเกจอื่น ๆ ที่เกี่ยวข้องกับโหมดเหล่านี้ ตัวอย่างเช่น polymode https://github.com/polymode/poly-R/ , https://polymode.github.io/หรือโหมด markdown- ด้วยhttps://jblevins.org/projects/markdown-ทางอ้อม โหมด

ดังนั้นคำตอบดั้งเดิมของฉัน (ด้านล่าง) จึงไม่มีผลอีกต่อไป ฟีเจอร์ที่ไลบรารีเหล่านี้ใช้เพื่อจัดเตรียมมีให้ในขณะนี้โดย polymode และการกำหนดค่านั้นง่ายกว่า ได้รับการสนับสนุนน้อยที่สุดสิ่งที่คุณต้องการคือการติดตั้งess, polymodeและpoly-Rแพคเกจ (จาก MELPA หรือจากแหล่งที่มาว่าเป็นวิธีที่คุณม้วน)

แค่นั้นแหละ! ตอนนี้ถ้าคุณเปิดRnwไฟล์คุณควรเห็นการPM-Rnwตั้งค่าสถานะใน modeline และจะมีPolymodeเมนูที่ด้านบน คุณสามารถสานไฟล์เป็น.texไฟล์ผ่านM-n w(หรือเมนู polymode) และส่งออกเป็น.pdfผ่านM-n e(หรือเมนู) คุณจะได้รับพร้อมท์สำหรับผู้ส่งออกในครั้งแรกที่คุณทำเช่นนี้ knitrฉันเพียงแค่หยิบ

หมายเหตุ: การส่งออก ( M-n e) จะเรียกใช้รหัสของคุณโดยอัตโนมัติสร้างไฟล์ pdf และแสดงมันทั้งหมดในครั้งเดียว ฉันไม่สามารถรับพฤติกรรม "คลิกเดียว" ที่มีรุ่นเก่าที่อธิบายไว้ด้านล่าง

ไฟล์ที่สร้างขึ้นจะมีคำ-wovenและ-exportedผนวกเข้าด้วยกัน ถ้าคุณไม่ทำเช่นนี้คุณสามารถกำหนดตัวเลือกและpolymode-weaver-output-file-formatpolymode-exporter-output-file-format

กระบวนการคล้ายกับไฟล์ RMarkdown ( .Rmd)

รายละเอียดทั้งหมดมีอยู่ในคู่มือโพลี

คำตอบเดิม (ล้าสมัยหลังจาก ESS 19.04)

ต้องตั้งค่าตัวแปรสามตัว:

  1. ess-swv-pdflatex-commandsในกลุ่มการปรับแต่งess-sweaveจะต้องมี "pdflatex" เป็นคำสั่งแรก เช่นควรมีลักษณะดังนี้:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processorในกลุ่มการกำหนดเองess-Rควรเป็นค่า"knitr"
  3. ess-pdf-viewer-prefในกลุ่มการปรับแต่งการess "emacsclient"สิ่งนี้ถือว่าคุณกำลังใช้เซิร์ฟเวอร์ emacs หรือ emacs กำลังทำงานในโหมด - daemon คุณควรใช้เครื่องมือ pdfหากเป็นไปได้เนื่องจากเป็นวิธีที่ดีกว่าโปรแกรมดูไฟล์ PDF ในเครื่องของ Emacs

ฉันใช้ตะขอเพื่อเพิ่มการโยงสองปุ่มสำหรับเรียกใช้ BibTeX และ 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"))))

เมื่อเสร็จแล้วM-n sจะถักเอกสารของคุณM-n bจะ bibtex และM-n Pจะประมวลผลด้วย pdflatex

โปรดทราบว่าไม่มีวิธีง่ายๆที่ Emacs จะทราบเมื่อถักเสร็จแล้วดังนั้นคุณไม่สามารถตั้งค่านี้ให้ถักและยางในขั้นตอนเดียว คุณต้องเรียก pdflatex ด้วยตนเองหลังจากที่คุณเห็นการถักเสร็จแล้ว

ด้วยขั้นตอนหลายขั้นตอนที่นี่ - Rnw -> Latex -> PDF ฉันไม่คิดว่าคุณจะได้รับ Synctex เพื่อให้ไฟล์ PDF และ Rnw ของคุณเลื่อนไปด้วยกัน แต่ฉันตื่นเต้นที่จะพิสูจน์ว่าผิด

สำหรับการจัดเรียงหน้าต่างฉันไม่สามารถทำให้พวกเขาอยู่ที่ที่ฉันต้องการได้ ดังนั้นเพื่อชดเชยฉันได้กลายเป็นผู้เชี่ยวชาญที่หน้าต่างสับและบัฟเฟอร์ตามที่ฉันต้องการ;)

Yihui โพสต์วิดีโอสั้น ๆบนเว็บไซต์ knitr ซึ่งแสดงให้เห็นบางส่วนของเรื่องนี้


ฉันทำอย่างดีที่สุดเพื่อแยกการกำหนดค่าที่จำเป็นทั้งหมดและเฉพาะการกำหนดค่าที่จำเป็นจาก. emac ของฉัน ฉันอาจจะพลาดบางสิ่งบางอย่างมันมีขนดกอยู่ในนั้น
ไทเลอร์

มันทำให้เป็นไปได้สำหรับฉันที่จะรวบรวมเอกสารถัก แต่ยังคงพยายามค้นหาคำสั่งผสมหนึ่งคีย์ (สำหรับ Rnw -> PDF) หวังว่ามันจะเป็นไปได้เนื่องจาก Rstudio มีสิ่งนี้
drobnbobn

@Tyler: ขอบคุณวิธีแก้ปัญหาของคุณทำงานเหมือนเครื่องราง ootb (แม้จะไม่แก้ไขการตั้งค่า)! แพ็คเกจ: ess, polymode, poly-r
Krisselack

5

นี่คือโซลูชันครบวงจร มันจะสร้างและแสดงรูปแบบไฟล์ PDF จาก RNW
โดยเฉพาะมันจะ:

  1. บันทึกบัฟเฟอร์ Rnw และถักมัน
  2. นำเอ็นจิ้น LaTeX ที่กำหนดไปใช้กับไฟล์ TeX ที่ได้
  3. ระบุเครื่องยนต์ที่ปฏิบัติการได้ของ BibTeX (เช่น biber, bibtex8)
  4. รันโปรแกรม BibTeX บนไฟล์ TeX หากไฟล์ bib นั้นใหม่กว่าไฟล์ TeX
  5. เรียกใช้ LaTeX อีกครั้ง 6 เปิด PDF ที่เป็นผลลัพธ์ในวิวเวอร์ที่กำหนด

ขั้นตอนจะพยายามออกโดยใช้ข้อความที่ให้ข้อมูลหากหนึ่งในขั้นตอนข้างต้นล้มเหลว
อินสแตนซ์ R จะถูกเปิดหากจำเป็นหรือปัจจุบันถูกใช้เพื่อแสดงกระบวนการถัก
การส่งออก LaTeX จะถูกส่งไปยังบัฟเฟอร์ "TeX-output" ซึ่งจะปรากฏในกรณีที่เกิดข้อผิดพลาดในการรวบรวม

การใช้

Meta- x knit-meเพื่อสร้างและดู PDF
Meta- การลบไฟล์น้ำยางกลางและx knit-me-clearknit-me

บรรณานุกรมต้องใช้แพ็คเกจ "biblatex" เช่น:

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

ชื่อของเอี๊ยมเครื่องยนต์ (เช่นbibtex, biber) จะได้รับการแยกวิเคราะห์backendคำหลัก
\addbibresourceคำสั่งจะถูกแยกวิเคราะห์เพื่อให้ได้ไฟล์บรรณานุกรม: ถ้าfoo.bibใหม่กว่าไฟล์ TeX โปรแกรมเอ็นจิ้นจะถูกเรียกใช้ ในเรื่องนี้\addbibresourceคำสั่งแรกเท่านั้นที่จะถูกนำมาพิจารณาหากมีจำนวนมาก

ปรับแต่ง

หากต้องการดู PDF ให้ตั้งค่าพา ธ ที่สามารถเรียกทำงานได้ของโปรแกรมดูด้วย:

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

อาจใช้วิวเวอร์เช่นSumatraPDFซึ่งจะอัปเดต PDF โดยอัตโนมัติเมื่อคอมไพล์ใหม่และไม่บล็อกไฟล์ที่เปิดอยู่เพื่อป้องกันการรวบรวมใหม่

เอ็นจิ้น LaTeX เริ่มต้นคือpdflatex(สันนิษฐานในเส้นทางปัจจุบัน) ปรับแต่งด้วย:

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

แน่นอนคุณอาจต้องการผูกknit-meและknit-me-clearปุ่มบางอย่างที่สะดวกสบาย

หมายเหตุ

ทดสอบใน Windows MiKTeX พร้อมด้วยbiberและbibtex8แบ็กเอนด์และ GNU Emacs 25.1.1

รหัส 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")))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.