RStudio提供了一种一键式方法,可以使用Knitr从LaTeX + R源生成PDF文件。看起来很适合进行可重复的研究。我正在尝试将我的Emacs配置为:
- 以Knitr方式在左侧缓冲区LaTeX + R代码;
- 在右侧缓冲区PDF输出预览中;
- 一种组合键进行编译。
如果可能的话:请问我该如何设置?
(ESS可以正常工作,但是我不知道如何设置Knitr-way和一键式编译。)
RStudio提供了一种一键式方法,可以使用Knitr从LaTeX + R源生成PDF文件。看起来很适合进行可重复的研究。我正在尝试将我的Emacs配置为:
如果可能的话:请问我该如何设置?
(ESS可以正常工作,但是我不知道如何设置Knitr-way和一键式编译。)
Answers:
从ESS 19.04开始,ess-noweb
和ess-swv
库已过时:
- 用于识字数据分析的库已过时,默认情况下未加载。这包括“ ess-noweb”,“ ess-swv”以及相关功能,例如“ Rnw-mode”。鼓励用户切换到处理这些模式的其他几个软件包之一。例如,polymode https://github.com/polymode/poly-R/, https://polymode.github.io/,或与编辑间接降价模式 https://jblevins.org/projects/markdown-模式。
因此,我的原始答案(如下)不再适用。这些库提供的功能现在由多模式提供,并且配置更简单。为了获得最少的支持,您所需要做的就是安装和软件包(从MELPA或从源代码开始安装ess
,如果您这样做)。polymode
poly-R
而已!现在,如果您打开一个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-format
和polymode-exporter-output-file-format
。
RMarkdown文件(.Rmd
)的过程与此类似。
多模式手册中提供了完整的详细信息
需要设置三个变量:
ess-swv-pdflatex-commands
,自定义组ess-sweave
中的第一个命令需要使用“ pdflatex”。即,它应该类似于:("pdflatex" "texi2pdf" "make")
ess-swv-processor
定制组中的ess-R
,应为值"knitr"
ess-pdf-viewer-pref
在自定义组ess
中"emacsclient"
。假设您正在运行emacs服务器,或者emacs以--daemon模式运行。您也应该尽可能使用pdf工具,因为它比内置的Emacs pdf查看器更可取。我使用钩子来添加两个绑定来调用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 在针织网站上发布了一个简短的视频,演示了其中的一些内容。
这是一个多合一的解决方案。它将从Rnw创建并显示PDF。
具体来说,它将:
如果上述步骤之一失败,该过程将尝试退出并提供提示性消息。
如有必要,将打开一个R实例,或者使用当前实例显示编织过程。
LaTeX输出将发送到“ TeX-output”缓冲区,如果发生编译错误,该缓冲区也会弹出。
Meta- x knit-me
创建和查看PDF。
Meta- x knit-me-clear
删除中间的LaTeX文件和knit-me
。
参考书目需要“ biblatex”软件包,即:
\usepackage[
options...
backend=ENGINE,
]{biblatex}
\addbibresource{foo.bib}
通过解析关键字bibtex
,biber
获得了bib引擎的名称(例如)backend
。
\addbibresource
解析命令以获取书目文件:如果foo.bib
比TeX文件新,则运行bib引擎。在这方面,\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中进行了测试。
;; (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")))
rmarkdown::render
(通过shell-command
)键绑定buffer-file-name
,这将在另一个窗口中更新pdf。