Как настроить рабочий процесс Knitr в Emacs?

18

RStudio обеспечивает способ создания PDF-файла из источника LaTeX + R с помощью Knitr одним нажатием кнопки. Это отлично подходит для проведения воспроизводимых исследований. И я пытаюсь настроить мой Emacs для:

  • в левом буфере код LaTeX + R способом Knitr;
  • в правом буфере предварительный просмотр PDF;
  • одна комбинация клавиш для компиляции.

Если это возможно: как мне это настроить, пожалуйста?

(ESS работает нормально, но я не знаю, как настроить Knitr-way и одну кнопку для компиляции.)

drobnbobn
источник
Я почти уверен, что есть лучший способ сделать это, но у меня есть короткая функция Elisp с привязкой клавиш, запущенной rmarkdown::render(через shell-command) на текущем buffer-file-name, которая обновит pdf в другом окне.
Дароциг
1
@daroczig это будет работать для LaTeX, или просто уценка?
Тайлер
@daroczig: в своем ответе я, возможно, попытался сделать это для файлов Rnw (LaTeX + R). Что касается файлов Rmd (Rmd + R), что проще, пожалуйста, начните отдельный пост.
Антонио
1
Я не мог получить правильную настройку. Сначала я должен вязать с помощью многомодового переплетения. Затем выберите экспортер и все, чтобы создать .tex-файл. Оттуда я должен бежать латекс. Я пытался адаптировать сценарий выше, но мой elisp просто еще не там. Я хочу связать .Rnw-файл, создать pdf (pdflatex) и просмотреть его. Я настроил свою систему так, что если я наберу "Cc Ca" (Tex-Command-run-all) в латексном файле, pdf будет создан и просмотрен, но я не могу вызвать эту функцию для tex-файла из my_knitr () , Помощь будет оценена. (defun my_knitr () «Запустите Knitr в режиме R-Poly и создайте и просмотрите pdf» (interacti
Krisselack
@ Krisselack, похоже, что функции, которые я опишу в своем ответе ниже, были удалены из ESS. Мне придется обновить его, чтобы отразить новый подход, который они используют
Тайлер

Ответы:

12

ОБНОВИТЬ

В СЗС 19.04, то ess-nowebи ess-swvбиблиотеки устарели:

  • Библиотеки для анализа грамотных данных устарели и по умолчанию не загружаются. Это включает в себя «ess-noweb», «ess-swv» и связанные с ними функции, такие как «Rnw-mode». Пользователям рекомендуется переключиться на один из нескольких других пакетов, которые имеют дело с этими режимами. Например, полимод https://github.com/polymode/poly-R/ , https://polymode.github.io/ или режим уценки с косвенным редактированием https://jblevins.org/projects/markdown- режим .

Следовательно, мой оригинальный ответ (ниже) больше не применяется. Функции, которые эти библиотеки использовали для обеспечения, теперь предоставляются в режиме polymode, а конфигурация стала проще. Чтобы получить минимальную поддержку, все, что вам нужно, это установить пакеты ess, polymodeи poly-R(из MELPA или из исходного кода, если вы так работаете).

Это оно! Теперь, если вы откроете Rnwфайл, вы должны увидеть PM-Rnwфлаг на моделине, и Polymodeвверху будет меню. Вы можете ткать свой файл в .texфайл через M-n w(или в меню ПОЛИМОДЫ), и экспортировать его .pdfчерез M-n e(или в меню). Вам будет предложено для экспортера в первый раз, когда вы это сделаете; Я только что выбрал knitr.

ПРИМЕЧАНИЕ: export ( M-n e) автоматически запускает ваш код, генерирует PDF-файл и отображает его - все сразу. Мне не удалось получить такое поведение «одним щелчком» со старой версией, описанной ниже.

Сгенерированные файлы будут иметь слово -wovenи -exportedдобавлены. Если вам это не нравится, вы можете настроить параметры polymode-weaver-output-file-formatи polymode-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скомпонует его и M-n Pобработает с помощью pdflatex.

Обратите внимание, что у Emacs нет простого способа узнать, когда вязание завершено, поэтому вы не можете настроить его на knit & latex за один шаг; Вы должны вручную запустить pdflatex после того, как увидите, что вязание закончено.

Учитывая несколько шагов здесь - Rnw -> Latex -> PDF, я не думаю, что вы можете заставить Synctex сохранить ваши pdf и Rnw файлы для совместной прокрутки, но я был бы рад, если бы я оказался неправ.

Что касается расположения окон, я никогда не смогу заставить их оставаться там, где я хочу. Поэтому, чтобы компенсировать это, я стал достаточно искусным в перетасовке окон и буферов, когда они мне нужны;)

Yihui разместил короткое видео на сайте knitr, демонстрирующее некоторые из них.

Тайлер
источник
Я приложил все усилия, чтобы извлечь из моего .emacs всю необходимую конфигурацию и только необходимую конфигурацию. Возможно, я что-то пропустил, там немного волосато.
Тайлер
это позволило мне составить документы Knitr. Но все же попробуйте найти комбинацию из одного ключа (для 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-clearудалить промежуточные файлы LaTeX и knit-me.

Библиография требует пакет "biblatex", то есть:

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

Имя 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.

Элисп код

;; (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")))
антонио
источник