como configurar o fluxo de trabalho Knitr no Emacs?

18

O RStudio fornece uma maneira de um botão para produzir arquivos PDF a partir da fonte LaTeX + R com Knitr. Parece ótimo para fazer pesquisas reproduzíveis. E estou tentando configurar meu Emacs para:

  • no buffer esquerdo, código LaTeX + R, no modo Knitr;
  • no buffer direito de visualização da saída PDF;
  • uma combinação de teclas para compilar.

Se for possível: como devo configurar isso, por favor?

(O ESS funciona bem, mas não sei como configurar o Knitr-way e um botão para compilar.)

drobnbobn
fonte
Tenho certeza de que existe uma maneira melhor de fazer isso, mas tenho uma função Elisp curta com uma ligação de chave em execução rmarkdown::render(via shell-command) na corrente buffer-file-name, que atualizará o pdf na outra janela.
daroczig
1
@daroczig isso funcionará para o LaTeX, ou apenas uma remarcação?
Tyler
@ Daroczig: na minha resposta, eu tentei fazer exatamente isso para arquivos Rnw (LaTeX + R). No que diz respeito aos arquivos Rmd (Rmd + R), que é mais simples, inicie uma postagem separada.
antonio
1
Não consegui uma configuração adequada. Eu tenho que tricotar primeiro com tecido polimodal. Em seguida, selecione exportador e tudo o mais para criar o arquivo .tex. A partir daí, tenho que executar o látex. Tentei adaptar o script acima, mas meu elisp ainda não está lá. O que eu quero é tricotar o arquivo .Rnw, criar o pdf (pdflatex) e visualizá-lo. Eu configurei meu sistema, que se eu digitar "Cc Ca" (Tex-Command-run-all) no arquivo de látex, o pdf será criado e exibido, mas não posso chamar essa função no arquivo tex de my_knitr () . Ajuda seria apreciada. (defun my_knitr () "Execute o Knitr no modo R-Poly e crie e visualize o pdf" (interacti
Krisselack
@Krisselack, parece que os recursos que descrevi na minha resposta abaixo foram removidos do ESS. Vou ter que atualizá-lo para refletir a nova abordagem que eles usam
Tyler

Respostas:

12

ATUALIZAR

No ESS 19.04, as bibliotecas ess-nowebe ess-swvsão obsoletas:

Consequentemente, minha resposta original (abaixo) não se aplica mais. Os recursos que essas bibliotecas costumavam fornecer agora são fornecidos pelo polymode e a configuração é mais simples. Para obter suporte mínimo, tudo que você precisa é instalar o ess, polymodee poly-Rpacotes (de Melpa, ou da fonte, se é assim que você rolo).

É isso aí! Agora, se você abrir um Rnwarquivo, deverá ver a PM-Rnwbandeira na modelagem e haverá um Polymodemenu na parte superior. Você pode transformar seu arquivo em um .texarquivo via M-n w(ou no menu polymode) e exportá-lo para .pdfvia M-n e(ou no menu). Você será solicitado a exportar a primeira vez que fizer isso; Eu apenas peguei knitr.

NOTA: exportar ( M-n e) executa automaticamente o seu código, gera o pdf e o exibe, tudo de uma só vez. Não consegui obter esse comportamento de "um clique" com a versão antiga descrita abaixo.

Os arquivos gerados terão a palavra -wovene serão -exportedanexados. Se você não gostar disso, poderá personalizar as opções polymode-weaver-output-file-formate polymode-exporter-output-file-format.

O processo é semelhante para os arquivos RMarkdown ( .Rmd).

Detalhes completos são fornecidos no manual do polimodo

Resposta original (obsoleta após o ESS 19.04)

Três variáveis ​​precisam ser definidas:

  1. ess-swv-pdflatex-commands, no grupo de personalização ess-sweaveprecisa ter "pdflatex" como o primeiro comando. ou seja, deve ser algo como:("pdflatex" "texi2pdf" "make")
  2. ess-swv-processor, no grupo de personalização ess-R, deve ser o valor"knitr"
  3. ess-pdf-viewer-prefno grupo de personalização esspara "emacsclient". Isso pressupõe que você esteja executando o servidor emacs ou o emacs esteja executando no modo --daemon. Você também deve usar as ferramentas pdf, se possível, pois é muito preferível ao visualizador de PDF embutido no Emacs.

Eu uso um gancho para adicionar duas combinações de teclas para chamar BibTeX e 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"))))

Uma vez feito isso, M-n svocê tricotará seu documento, M-n bfará bibtex e M-n Po processará com pdflatex.

Observe que não há uma maneira simples de o Emacs saber quando o tricô está completo; portanto, você não pode configurá-lo para tricotar e látex em uma única etapa; você deve acionar manualmente o pdflatex depois de ver o tricô terminado.

Dadas as várias etapas aqui - Rnw -> Látex -> PDF, não acho que você possa fazer com que o Synctex mantenha seus arquivos PDF e Rnw para rolar juntos, mas ficaria emocionado por estar errado.

Quanto ao arranjo das janelas, nunca consigo que eles fiquem onde quero. Então, para compensar, tornei-me bastante hábil em baralhar janelas e buffers conforme necessário;)

Yihui postou um pequeno vídeo no site knitr demonstrando parte disso.

Tyler
fonte
Eu fiz o meu melhor para extrair toda a configuração necessária, e somente a necessária, do meu .emacs. Eu posso ter perdido alguma coisa, é meio peludo lá.
Tyler
tornou possível compilar documentos knitr. Mas ainda tente encontrar a combinação de uma tecla (para Rnw -> PDF). Espero que seja possível, já que a Rstudio tem isso.
precisa saber é o seguinte
@ Tyler: Obrigado, sua solução funciona como um charme ootb (mesmo sem editar a configuração)! Pacotes: ess, polymode, poly-r
Krisselack 13/03
5

Esta é uma solução multifuncional. Ele criará e exibirá um PDF a partir de um Rnw .
Especificamente:

  1. Salve o buffer Rnw e tricote-o,
  2. Aplique um mecanismo LaTeX ao arquivo TeX resultante,
  3. Identifique o executável do mecanismo BibTeX (por exemplo, biber, bibtex8),
  4. Execute o mecanismo BibTeX no arquivo TeX se o arquivo bib for mais novo que o arquivo TeX,
  5. Execute o LaTeX novamente, 6 Abra o PDF resultante no visualizador designado.

O procedimento tenta sair com mensagens informativas se uma das etapas acima falhar.
Uma instância R será aberta, se necessário, ou a atual será usada para mostrar o processo de tricô.
A saída LaTeX é enviada para o buffer "TeX-output", que também é acionado em caso de erro de compilação.

Uso

Meta- x knit-mepara criar e visualizar o PDF.
Meta- x knit-me-clearpara remover arquivos intermediários do LaTeX e knit-me.

A bibliografia requer o pacote "biblatex", ou seja:

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

O nome do mecanismo de jardinagem (por exemplo bibtex, biber) é obtido analisando a backendpalavra - chave.
\addbibresourceO comando é analisado para obter o arquivo bibliográfico: se foo.bibfor mais recente que o arquivo TeX, o mecanismo de babagem será executado. Nesse sentido, somente o primeiro \addbibresourcecomando é levado em consideração se houver muitos.

Customizar

Para realmente visualizar o PDF, defina o caminho executável do visualizador com:

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

Possivelmente, use um visualizador como o SumatraPDF , que atualiza automaticamente o PDF quando recompilado e não bloqueia o arquivo aberto, impedindo novas compilações.

O mecanismo LaTeX padrão é pdflatex(assumido no caminho atual). Personalize com:

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

Claro que você pode querer ligar knit-mee knit-me-cleara algumas teclas convenientes.

Notas

Testado no Windows MiKTeX, com bibere bibtex8back-ends e GNU Emacs 25.1.1.

Código 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
fonte