emacs/layers.personal/misctools/my-polymode/local/polymode/modes/poly-R.el
2018-04-07 10:54:04 +08:00

467 lines
17 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; poly-R.el --- Popymodes for R
;;
;; Filename: poly-R.el
;; Author: Spinu Vitalie
;; Maintainer: Spinu Vitalie
;; Copyright (C) 2013-2014, Spinu Vitalie, all rights reserved.
;; Version: 1.0
;; URL: https://github.com/vitoshka/polymode
;; Keywords: emacs
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This file is *NOT* part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 3, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'polymode)
(defcustom pm-poly/R
(pm-polymode-one "R"
:hostmode 'pm-host/R
:innermode 'pm-inner/fundamental)
"R root polymode. Not intended to be used directly."
:group 'polymodes
:type 'object)
;; NOWEB
(require 'poly-noweb)
(defcustom pm-poly/noweb+R
(clone pm-poly/noweb :innermode 'pm-inner/noweb+R)
"Noweb for R configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/noweb+R
(clone pm-inner/noweb
:mode 'R-mode)
"Noweb for R"
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-noweb+r-mode "poly-R")
(define-polymode poly-noweb+r-mode pm-poly/noweb+R :lighter " PM-Rnw")
;; MARKDOWN
(require 'poly-markdown)
;;;###autoload (autoload 'poly-markdown+r-mode "poly-R")
(define-polymode poly-markdown+r-mode pm-poly/markdown :lighter " PM-Rmd")
;; RAPPORT
(defcustom pm-poly/rapport
(clone pm-poly/markdown "rapport"
:innermodes '(pm-inner/brew+R
pm-inner/rapport+YAML))
"Rapport template configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/rapport+YAML
(pm-hbtchunkmode "rapport+YAML"
:mode 'yaml-mode
:head-reg "<!--head"
:tail-reg "head-->")
"YAML header in Rapport files"
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-rapport-mode "poly-R")
(define-polymode poly-rapport-mode pm-poly/rapport nil)
;; HTML
(defcustom pm-poly/html+R
(clone pm-poly/html "html+R" :innermode 'pm-inner/html+R)
"HTML + R configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/html+R
(pm-hbtchunkmode "html+R"
:mode 'R-mode
:head-reg "<!--[ \t]*begin.rcode"
:tail-reg "end.rcode[ \t]*-->")
"HTML KnitR innermode."
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-html+r-mode "poly-R")
(define-polymode poly-html+r-mode pm-poly/html+R)
;;; R-brew
(defcustom pm-poly/brew+R
(clone pm-poly/brew "brew+R"
:innermode 'pm-inner/brew+R)
"Brew + R configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/brew+R
(pm-hbtchunkmode "brew+R"
:mode 'R-mode
:head-reg "<%[=%]?"
:tail-reg "[#=%=-]?%>")
"Brew R chunk."
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-brew+r-mode "poly-R")
(define-polymode poly-brew+r-mode pm-poly/brew+R)
;;; R+C++
;; todo: move into :matcher-subexp functionality?
(defun pm--R+C++-head-matcher (ahead)
(when (re-search-forward "cppFunction(\\(['\"]\n\\)"
nil t ahead)
(cons (match-beginning 1) (match-end 1))))
(defun pm--R+C++-tail-matcher (ahead)
(when (< ahead 0)
(goto-char (car (pm--R+C++-head-matcher -1))))
(goto-char (max 1 (1- (point))))
(let ((end (or (ignore-errors (scan-sexps (point) 1))
(buffer-end 1))))
(cons (max 1 (1- end)) end)))
(defcustom pm-poly/R+C++
(clone pm-poly/R "R+C++" :innermode 'pm-inner/R+C++)
"R + C++ configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/R+C++
(pm-hbtchunkmode "R+C++"
:mode 'c++-mode
:head-mode 'host
:head-reg 'pm--R+C++-head-matcher
:tail-reg 'pm--R+C++-tail-matcher
:font-lock-narrow nil)
"HTML KnitR chunk."
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-r+c++-mode "poly-R")
(define-polymode poly-r+c++-mode pm-poly/R+C++)
;;; C++R
(defun pm--C++R-head-matcher (ahead)
(when (re-search-forward "^[ \t]*/[*]+[ \t]*R" nil t ahead)
(cons (match-beginning 0) (match-end 0))))
(defun pm--C++R-tail-matcher (ahead)
(when (< ahead 0)
(error "backwards tail match not implemented"))
;; may be rely on syntactic lookup ?
(when (re-search-forward "^[ \t]*\\*/")
(cons (match-beginning 0) (match-end 0))))
(defcustom pm-poly/C++R
(clone pm-poly/C++ "C++R" :innermode 'pm-inner/C++R)
"R + C++ configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/C++R
(pm-hbtchunkmode "C++R"
:mode 'R-mode
:head-reg 'pm--C++R-head-matcher
:tail-reg 'pm--C++R-tail-matcher)
"HTML KnitR chunk."
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-c++r-mode "poly-R")
(define-polymode poly-c++r-mode pm-poly/C++R)
;;; R help
(defcustom pm-poly/ess-help+R
(pm-polymode-one "ess-R-help"
:innermode 'pm-inner/ess-help+R)
"ess-R-help"
:group 'polymodes
:type 'object)
(defcustom pm-inner/ess-help+R
(pm-hbtchunkmode "ess-help+R"
:mode 'R-mode
:head-reg "^Examples:"
:tail-reg "\\'"
:indent-offset 5
:switch-buffer-functions '(pm--ess-help+R-turn-off-read-only))
"Ess help R chunk"
:group 'innermodes
:type 'object)
(defun pm--ess-help+R-turn-off-read-only (&rest ignore)
;; don't transfer read only status from main help buffer
(cl-case pm/type
(body (read-only-mode -1))
(head (read-only-mode 1))))
;;;###autoload (autoload 'poly-ess-help+r-mode "poly-R")
(define-polymode poly-ess-help+r-mode pm-poly/ess-help+R)
(add-hook 'ess-help-mode-hook '(lambda ()
(when (string= ess-dialect "R")
(poly-ess-help+r-mode))))
(defun pm--Rd-examples-head-matcher (ahead)
(when (re-search-forward "\\examples *\\({\n\\)" nil t ahead)
(cons (match-beginning 1) (match-end 1))))
(defun pm--Rd-examples-tail-matcher (ahead)
(when (< ahead 0)
(goto-char (car (pm--R+C++-head-matcher -1))))
(let ((end (or (ignore-errors (scan-sexps (point) 1))
(buffer-end 1))))
(cons (max 1 (- end 1)) end)))
(defcustom pm-poly/Rd
(pm-polymode-one "R-documentation"
:innermode 'pm-inner/Rd)
"R polymode for Rd files"
:group 'polymodes
:type 'object)
(defcustom pm-inner/Rd
(pm-hbtchunkmode "R+C++"
:mode 'R-mode
:head-mode 'host
:head-reg 'pm--Rd-examples-head-matcher
:tail-reg 'pm--Rd-examples-tail-matcher)
"Rd examples chunk."
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-Rd-mode "poly-R")
(define-polymode poly-Rd-mode pm-poly/Rd)
(add-hook 'Rd-mode-hook 'poly-Rd-mode)
;; Rmarkdown
(defcustom pm-exporter/Rmarkdown
(pm-shell-exporter "Rmarkdown"
:from
'(("Rmarkdown" "\\.[rR]?md\\|rapport\\'" "R Markdown"
"Rscript -e \"rmarkdown::render('%i', output_format = '%t', output_file = '%o',encoding='UTF-8')\""))
:to
'(("auto" . pm--rmarkdown-shell-auto-selector)
("html" "html" "html document" "html_document")
("pdf" "pdf" "pdf document" "pdf_document")
("word" "docx" "word document" "word_document")
("md" "md" "md document" "md_document")
("ioslides" "html" "ioslides presentation" "ioslides_presentation")
("slidy" "html" "slidy presentation" "slidy_presentation")
("beamer" "pdf" "beamer presentation" "beamer_presentation")
("tufte handout pdf" "pdf" "pdf with tufte handout" "tufte::tufte_handout")
("tufte book pdf" "pdf" "tufte book in PDF" "tufte::tufte_book")
("tufte handout html" "html" "html with tufte handout" "tufte::tufte_html")))
"R Markdown exporter.
Please not that with 'AUTO DETECT' export options, output file
names are inferred by Rmarkdown from YAML description
block. Thus, output file names don't comply with
`polymode-exporter-output-file-format'."
:group 'polymode-export
:type 'object)
(polymode-register-exporter pm-exporter/Rmarkdown nil
pm-poly/markdown pm-poly/rapport)
(defun pm--rmarkdown-shell-auto-selector (action &rest ignore)
(cl-case action
(doc "AUTO DETECT")
(command "Rscript -e \"rmarkdown::render('%i', output_format = 'all', encoding='UTF-8')\"")
(output-file #'pm--rmarkdown-output-file-sniffer)))
(defcustom pm-exporter/Rmarkdown-ESS
(pm-callback-exporter "Rmarkdown-ESS"
:from
'(("Rmarkdown" "\\.[rR]?md\\|rapport\\'" "R Markdown"
"rmarkdown::render('%I', output_format = '%t', output_file = '%O')\n"))
:to
'(("auto" . pm--rmarkdown-callback-auto-selector)
("html" "html" "html document" "html_document")
("pdf" "pdf" "pdf document" "pdf_document")
("word" "docx" "word document" "word_document")
("md" "md" "md document" "md_document")
("ioslides" "html" "ioslides presentation" "ioslides_presentation")
("slidy" "html" "slidy presentation" "slidy_presentation")
("beamer" "pdf" "beamer presentation" "beamer_presentation")
("tufte handout pdf" "pdf" "pdf with tufte handout" "tufte::tufte_handout")
("tufte book pdf" "pdf" "tufte book in PDF" "tufte::tufte_book")
("tufte handout html" "html" "html with tufte handout" "tufte::tufte_html"))
:function 'pm--ess-run-command
:callback 'pm--ess-callback)
"R Markdown exporter.
Please not that with 'AUTO DETECT' export options, output file
names are inferred by Rmarkdown from YAML description
block. Thus, output file names don't comply with
`polymode-exporter-output-file-format'."
:group 'polymode-export
:type 'object)
(polymode-register-exporter pm-exporter/Rmarkdown-ESS nil
pm-poly/markdown pm-poly/rapport)
(defun pm--rmarkdown-callback-auto-selector (action &rest ignore)
(cl-case action
(doc "AUTO DETECT")
;; last file is not auto-detected unless we cat new line
(command "rmarkdown::render('%I', output_format = 'all')")
(output-file #'pm--rmarkdown-output-file-sniffer)))
(defun pm--rmarkdown-output-file-sniffer ()
(goto-char (point-min))
(let (files)
(while (re-search-forward "Output created: +\\(.*\\)" nil t)
(push (expand-file-name (match-string 1)) files))
(reverse (delete-dups files))))
;; KnitR
(defcustom pm-weaver/knitR
(pm-shell-weaver "knitr"
:from-to
'(("latex" "\\.\\(tex\\|[rR]nw\\)\\'" "tex" "LaTeX" "Rscript -e \"knitr::knit('%i', output='%o',encoding='UTF-8')\"")
("html" "\\.x?html?\\'" "html" "HTML" "Rscript -e \"knitr::knit('%i', output='%o',encoding='UTF-8')\"")
("markdown" "\\.[rR]?md]\\'" "md" "Markdown" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("rst" "\\.rst" "rst" "ReStructuredText" "Rscript -e \"knitr::knit('%i', output='%o', encoding='UTF-8')\"")
("brew" "\\.[rR]?brew\\'" "brew" "Brew" "Rscript -e \"knitr::knit('%i', output='%o', encoding='UTF-8')\"")
("asciidoc" "\\.asciidoc\\'" "txt" "AsciiDoc" "Rscript -e \"knitr::knit('%i', output='%o', encoding='UTF-8')\"")
("textile" "\\.textile\\'" "textile" "Textile" "Rscript -e \"knitr::knit('%i', output='%o', encoding='UTF-8')\"")))
"Shell knitR weaver."
:group 'polymode-weave
:type 'object)
(polymode-register-weaver pm-weaver/knitR nil
pm-poly/noweb+R pm-poly/markdown
pm-poly/rapport pm-poly/html+R)
(defcustom pm-weaver/knitR-ESS
(pm-callback-weaver "knitR-ESS"
:from-to
'(("latex" "\\.\\(tex\\|rnw\\)\\'" "tex" "LaTeX" "knitr::knit('%I', output='%O', encoding='UTF-8')")
("html" "\\.x?html?\\'" "html" "HTML" "knitr::knit('%I', output='%O', encoding='UTF-8')")
("markdown" "\\.r?md\\'" "md" "Markdown" "knitr::knit('%I', output='%O', encoding='UTF-8')")
("rst" "\\.rst\\'" "rst" "ReStructuredText" "knitr::knit('%I', output='%O', encoding='UTF-8')")
("brew" "\\.r?brew\\'" "brew" "Brew" "knitr::knit('%I', output='%O', encoding='UTF-8')")
("asciidoc" "\\.asciidoc\\'" "txt" "AsciiDoc" "knitr::knit('%I', output='%O',encoding='UTF-8')")
("textile" "\\.textile\\'" "textile" "Textile" "knitr::knit('%I', output='%O',encoding='UTF-8')"))
:function 'pm--ess-run-command
:callback 'pm--ess-callback)
"ESS knitR weaver."
:group 'polymode-weave
:type 'object)
(polymode-register-weaver pm-weaver/knitR-ESS nil
pm-poly/noweb+R pm-poly/markdown
pm-poly/rapport pm-poly/html+R)
(defcustom pm-weaver/Sweave-ESS
(pm-callback-weaver "ESS-Sweave"
:from-to '(("latex" "\\.\\(tex\\|r?s?nw\\)\\'" "tex"
"LaTeX" "Sweave('%I', output='%O')"))
:function 'pm--ess-run-command
:callback 'pm--ess-callback)
"ESS 'Sweave' weaver."
:group 'polymode-weave
:type 'object)
(polymode-register-weaver pm-weaver/Sweave-ESS nil
pm-poly/noweb+R)
;; Sweave
(defcustom pm-weaver/Sweave
(pm-shell-weaver "sweave"
:from-to
'(("latex" "\\.\\(tex\\|r?s?nw\\)\\'"
"tex" "LaTeX" "R CMD Sweave %i --options=\"output='%o'\"")))
"Shell 'Sweave' weaver."
:group 'polymode-weave
:type 'object)
(polymode-register-weaver pm-weaver/Sweave nil
pm-poly/noweb+R)
;; ESS command
(declare-function ess-async-command nil)
(declare-function ess-force-buffer-current nil)
(declare-function ess-process-get nil)
(declare-function ess-process-put nil)
(declare-function comint-previous-prompt nil)
(defun pm--ess-callback (proc string)
(let ((ofile (process-get proc :output-file)))
;; This is getting silly. Ess splits output for optimization reasons. So we
;; are collecting output from 3 places:
;; - most recent STRING
;; - string in accumulation buffer 'accum-buffer-name
;; - string already in output buffer
(with-current-buffer (process-get proc 'accum-buffer-name)
(setq string (concat (buffer-substring (point-min) (point-max))
string)))
(with-current-buffer (process-buffer proc)
(setq string (concat (buffer-substring (or ess--tb-last-input (comint-previous-prompt)) (point-max))
string)))
(with-temp-buffer
(message string)
(insert string)
(when (string-match-p "Error\\(:\\| +in\\)" string)
(error "Errors durring ESS async command"))
(unless (stringp ofile)
(setq ofile (funcall ofile))))
ofile))
(defun pm--ess-run-command (command callback &rest ignore)
(require 'ess)
(let ((ess-eval-visibly t)
(ess-dialect "R"))
(ess-force-buffer-current)
(ess-process-put :output-file pm--output-file)
(ess-process-put 'callbacks (list callback))
(ess-process-put 'interruptable? t)
(ess-process-put 'running-async? t)
(ess-eval-linewise command)))
;; COMPAT
(when (fboundp 'advice-add)
(advice-add 'ess-eval-paragraph :around 'pm-execute-narrowed-to-span)
(advice-add 'ess-eval-buffer :around 'pm-execute-narrowed-to-span)
(advice-add 'ess-beginning-of-function :around 'pm-execute-narrowed-to-span))
(provide 'poly-R)