Initial commit

This commit is contained in:
Mateus Pinto Rodrigues
2017-11-11 15:15:10 -02:00
commit 58c3bd6728
1202 changed files with 434097 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
;;; Directory Local Variables
;;; See Info node `(emacs) Directory Variables' for more information.
((nil
(require-final-newline . t)
;; not tabs in code
(indent-tabs-mode)
;; checkdoc, one space is enough
(sentence-end-double-space . nil)
;; checkdoc, don't botch English grammar
(checkdoc-arguments-in-order-flag . nil)
;; checkdoc, we don't want docs for internal vars
(checkdoc-force-docstrings-flag . nil))
(emacs-lisp-mode
;; remove trailing whitespace
(eval . (add-hook 'before-save-hook 'delete-trailing-whitespace nil t))))

View File

@@ -0,0 +1,459 @@
;;; 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')\""))
: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")))
"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')\"")
(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"))
: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')\"")
("html" "\\.x?html?\\'" "html" "HTML" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("markdown" "\\.[rR]?md]\\'" "md" "Markdown" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("rst" "\\.rst" "rst" "ReStructuredText" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("brew" "\\.[rR]?brew\\'" "brew" "Brew" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("asciidoc" "\\.asciidoc\\'" "txt" "AsciiDoc" "Rscript -e \"knitr::knit('%i', output='%o')\"")
("textile" "\\.textile\\'" "textile" "Textile" "Rscript -e \"knitr::knit('%i', output='%o')\"")))
"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')")
("html" "\\.x?html?\\'" "html" "HTML" "knitr::knit('%I', output='%O')")
("markdown" "\\.r?md\\'" "md" "Markdown" "knitr::knit('%I', output='%O')")
("rst" "\\.rst\\'" "rst" "ReStructuredText" "knitr::knit('%I', output='%O')")
("brew" "\\.r?brew\\'" "brew" "Brew" "knitr::knit('%I', output='%O')")
("asciidoc" "\\.asciidoc\\'" "txt" "AsciiDoc" "knitr::knit('%I', output='%O')")
("textile" "\\.textile\\'" "textile" "Textile" "knitr::knit('%I', output='%O')"))
: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
(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)

Binary file not shown.

View File

@@ -0,0 +1,109 @@
;;; CORE POLYMODE AND HOST OBJECTS
;;; POLYMODE objects
;; These are simple generic configuration objects. More specialized
;; configuration objects are defined in language-specific files (e.g. poly-R.el,
;; poly-markdown.el etc).
(defcustom pm-inner/fallback
(pm-chunkmode "FallBack" :mode 'poly-fallback-mode)
"Polymode fall back inner mode."
:group 'hostmodes
:type 'object)
(defcustom pm-poly/brew
(pm-polymode-one "brew"
:hostmode 'pm-host/text
:innermode 'pm-inner/fallback)
"Typical Brew configuration"
:group 'polymodes
:type 'object)
(defcustom pm-poly/html
;; fixme: should probably be pm-polymode-multi
(pm-polymode-one "html"
:hostmode 'pm-host/html
:innermode 'pm-inner/fallback)
"HTML typical configuration"
:group 'polymodes
:type 'object)
(defcustom pm-poly/C++
(pm-polymode-one "C++"
:hostmode 'pm-host/C++
:innermode 'pm-inner/fallback)
"C++ typical configuration"
:group 'polymodes
:type 'object)
;; HOST MODES
(defcustom pm-host/blank
(pm-bchunkmode "FallBack" :mode nil)
"Blank. Used as a placeholder for currently installed mode.
It is specifically intended to be used with minor modes."
:group 'hostmodes
:type 'object)
(defcustom pm-host/fallback
(pm-bchunkmode "FallBack"
:mode 'poly-fallback-mode)
"Polymode fall back host mode."
:group 'hostmodes
:type 'object)
(defcustom pm-host/fundamental
(pm-bchunkmode "fundamental"
:mode 'fundamental-mode)
"Fundamental host mode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/latex
(pm-bchunkmode "latex"
:mode 'latex-mode)
"Latex host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/html
(pm-bchunkmode "html"
:mode 'html-mode)
"HTML host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/R
(pm-bchunkmode "R"
:mode 'R-mode)
"R host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/C++
(pm-bchunkmode "C++"
:mode 'c++-mode
:font-lock-narrow nil)
"C++ host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/text
(pm-bchunkmode "text"
:mode 'text-mode)
"Text host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/yaml
(pm-bchunkmode "YAML"
:mode 'yaml-mode)
"YAML chunkmode"
:group 'hostmodes
:type 'object)
(provide 'poly-base)

Binary file not shown.

View File

@@ -0,0 +1,53 @@
;;; poly-C.el --- Popymodes for C and C++
;;
;; Filename: poly-C.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)
(require 'poly-noweb)
(defcustom pm-poly/noweb+c
(clone pm-poly/noweb
:innermode 'pm-inner/noweb+c)
"Noweb polymode for c"
:group 'polymodes
:type 'object)
(defcustom pm-inner/noweb+c
(clone pm-inner/noweb
:mode 'c-mode)
"Noweb innermode for C"
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-noweb+c-mode "poly-c")
(define-polymode poly-noweb+c-mode pm-poly/noweb+c :lighter " PM-Cnw")
(provide 'poly-c)

Binary file not shown.

View File

@@ -0,0 +1,60 @@
(require 'polymode)
(defcustom pm-host/coffee
(pm-bchunkmode "coffee" :mode 'coffee-mode)
"coffee host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-host/javascript
(pm-bchunkmode "javascript" :mode 'js-mode)
"javascript host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-inner/erb
(pm-hbtchunkmode "erb"
:mode 'ruby-mode
:head-reg "\"?\<\% *[-=]?"
:tail-reg "\%\>\"?")
"erb typical chunk."
:group 'innermodes
:type 'object)
(defcustom pm-poly/coffee-erb
(pm-polymode-one "coffee-erb"
:hostmode 'pm-host/coffee
:innermode 'pm-inner/erb)
"coffee-erb typical polymode."
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-coffee+erb-mode "poly-erb")
(define-polymode poly-coffee+erb-mode pm-poly/coffee-erb)
(define-obsolete-function-alias 'poly-coffee-erb-mode 'poly-coffee+erb-mode)
(defcustom pm-poly/javascript-erb
(pm-polymode-one "javascript-erb"
:hostmode 'pm-host/javascript
:innermode 'pm-inner/erb)
"javascript-erb typical polymode."
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-javascript+erb-mode "poly-erb")
(define-polymode poly-javascript+erb-mode pm-poly/javascript-erb)
(define-obsolete-function-alias 'poly-javascript-erb-mode 'poly-javascript+erb-mode)
(defcustom pm-poly/html-erb
(pm-polymode-one "html-erb"
:hostmode 'pm-host/html
:innermode 'pm-inner/erb)
"html-erb typical polymode."
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-html+erb-mode "poly-erb")
(define-polymode poly-html+erb-mode pm-poly/html-erb)
(define-obsolete-function-alias 'poly-html-erb-mode 'poly-html+erb-mode)
(provide 'poly-erb)

Binary file not shown.

View File

@@ -0,0 +1,219 @@
;; `font-lock-mode' call graph:
;; -> font-lock-function <- we are replacing this with `poly-lock-mode'
;; -> font-lock-default-function
;; -> font-lock-mode-internal
;; -> font-lock-turn-on-thing-lock
;; -> font-lock-turn-on-thing-lock
;; -> (setq font-lock-flush-function jit-lock-refontify)
;; -> (setq font-lock-ensure-function jit-lock-fontify-now)
;; -> (setq font-lock-fontify-buffer-function jit-lock-refontify)
;; -> (jit-lock-register #'font-lock-fontify-region)
;; -> (add-hook 'jit-lock-functions #'font-lock-fontify-region nil t)
;; -> jit-lock-mode
(require 'polymode-core)
(require 'polymode-compat)
(defvar poly-lock-fontification-in-progress nil)
(defvar-local poly-lock-mode nil)
(defvar-local poly-lock--fontify-region-original nil)
(eval-when-compile
(defmacro with-buffer-prepared-for-poly-lock (&rest body)
"Execute BODY in current buffer, overriding several variables.
Preserves the `buffer-modified-p' state of the current buffer."
(declare (debug t))
`(let ((inhibit-point-motion-hooks t))
(with-silent-modifications
,@body))))
(defun poly-lock-no-jit-lock-in-polymode-buffers (orig-fun arg)
"Don't activate `jit-lock-mode' when in `polymode' buffers.
We are reusing some of the jit-lock functionality but don't want
to activate jit-lock."
(unless (and polymode-mode pm/polymode)
(funcall orig-fun arg)))
(pm-around-advice 'jit-lock-mode #'poly-lock-no-jit-lock-in-polymode-buffers)
(defun poly-lock-mode (arg)
;; value of `font-lock-function' in polymode buffers
(unless polymode-mode
(error "Trying to (de)activate `poly-lock-mode' in a non-polymode buffer (%s)" (current-buffer)))
(setq poly-lock-mode arg)
(if arg
(progn
;; a lot of the following is inspired by what jit-lock does in
;; `font-lock-turn-on-thing-lock'
(setq-local font-lock-support-mode 'poly-lock-mode)
(setq-local font-lock-dont-widen t)
;; re-use jit-lock registration. Some minor modes (adaptive-wrap)
;; register extra functionality.
(jit-lock-register 'font-lock-fontify-region)
;; don't allow other functions
(setq-local fontification-functions '(poly-lock-fontification-function))
(setq-local font-lock-flush-function 'poly-lock-refontify)
(setq-local font-lock-ensure-function 'poly-lock-fontify-now)
(setq-local font-lock-fontify-buffer-function 'poly-lock-refontify)
;; There are some more, jit-lock doesn't change those, neither do we:
;; font-lock-unfontify-region-function (defaults to font-lock-default-unfontify-region)
;; font-lock-unfontify-buffer-function (defualts to font-lock-default-unfontify-buffer)
;; Don't fontify eagerly (and don't abort if the buffer is large). This
;; is probably not needed but let it be.
(setq-local font-lock-fontified t)
;; Now we can finally call `font-lock-default-function' because
;; `font-lock-support-mode' is set to "unrecognizible" value, only core
;; font-lock setup happens.
(font-lock-default-function arg)
;; Must happen after call to `font-lock-default-function'
(remove-hook 'after-change-functions 'font-lock-after-change-function t)
(remove-hook 'after-change-functions 'jit-lock-after-change t)
(add-hook 'after-change-functions 'poly-lock-after-change nil t)
;; Reusing jit-lock var becuase mode populate it directly. We are using
;; this in `poly-lock-after-change' below. Taken from `jit-lock
;; initialization.
(add-hook 'jit-lock-after-change-extend-region-functions
'font-lock-extend-jit-lock-region-after-change
nil t))
(remove-hook 'after-change-functions 'poly-lock-after-change t)
(remove-hook 'fontification-functions 'poly-lock-fontification-function t))
(current-buffer))
(defun poly-lock-fontification-function (start)
"The only function in `fontification-functions'.
This is the entry point called by the display engine. START is
defined in `fontification-functions'. This function is has the
same scope as `jit-lock-function'."
(unless pm-initialization-in-progress
(if pm-allow-fontification
(when (and poly-lock-mode (not memory-full))
(unless (input-pending-p)
(let ((end (or (text-property-any start (point-max) 'fontified t)
(point-max))))
(when (< start end)
(poly-lock-fontify-now start end)))))
(with-buffer-prepared-for-poly-lock
(put-text-property start (point-max) 'fontified t)))))
(defun poly-lock-fontify-now (beg end &optional verbose)
"Polymode font-lock fontification function.
Fontifies chunk-by chunk within the region BEG END."
(unless (or poly-lock-fontification-in-progress
pm-initialization-in-progress)
(let* ((font-lock-dont-widen t)
(pmarker (point-marker))
(dbuffer (current-buffer))
;; Fontification in one buffer can trigger fontification in another
;; buffer. Particularly, this happens when new indirect buffers are
;; created and `normal-mode' triggers font-lock in those buffers. We
;; avoid this by dynamically binding
;; `poly-lock-fontification-in-progress' and un-setting
;; `fontification-functions' in case re-display suddenly decides to
;; fontify something else in other buffer.
(poly-lock-fontification-in-progress t)
(fontification-functions nil))
(save-restriction
(widen)
(save-excursion
(pm-map-over-spans
(lambda ()
(with-buffer-prepared-for-poly-lock
(let ((sbeg (nth 1 *span*))
(send (nth 2 *span*)))
(when (> send sbeg)
(if (not (and font-lock-mode font-lock-keywords))
;; when no font-lock, set to t to avoid repeated calls
;; from display engine
(put-text-property sbeg send 'fontified t)
(let ((new-beg (max sbeg beg))
(new-end (min send end)))
(condition-case-unless-debug err
;; (if (oref pm/chunkmode :font-lock-narrow)
;; (pm-with-narrowed-to-span *span*
;; (font-lock-unfontify-region new-beg new-end)
;; (font-lock-fontify-region new-beg new-end verbose))
;; (font-lock-unfontify-region new-beg new-end)
;; (font-lock-fontify-region new-beg new-end verbose))
(if (oref pm/chunkmode :font-lock-narrow)
(pm-with-narrowed-to-span *span*
(jit-lock-fontify-now new-beg new-end))
(jit-lock-fontify-now new-beg new-end))
(error (message "(poly-lock-fontify-now %s %s) -> (%s %s %s %s): %s "
beg end poly-lock--fontify-region-original new-beg new-end verbose
(error-message-string err))))
;; even if failed set to t
(put-text-property new-beg new-end 'fontified t))
(pm--adjust-chunk-face sbeg send (pm-get-adjust-face pm/chunkmode)))))))
beg end))))
(current-buffer)))
(defun poly-lock-refontify (&optional beg end)
"Force refontification of the region BEG..END.
END is extended to the next chunk separator. This function is
pleased in `font-lock-flush-function' and
`font-lock-ensure-function'"
(when (and pm-allow-fontification
(not poly-lock-fontification-in-progress)
(not pm-initialization-in-progress))
(with-buffer-prepared-for-poly-lock
(save-restriction
(widen)
(cond ((and beg end)
(setq end (cdr (pm-get-innermost-range end))))
(beg
(setq end (cdr (pm-get-innermost-range beg))))
(t
(setq beg (point-min)
end (point-max))))
(put-text-property beg end 'fontified nil)))))
(defun poly-lock-after-change (beg end old-len)
"Mark changed region as not fontified after change.
Installed on `after-change-functions'."
(save-match-data
(when (and poly-lock-mode
pm-allow-after-change-hook
(not memory-full))
(let ((jit-lock-start beg)
(jit-lock-end end)
;; useful info for tracing
(gl-beg end)
(gl-end beg)
exp-error)
(save-excursion
(condition-case err
;; set jit-lock-start and jit-lock-end locally
(run-hook-with-args 'jit-lock-after-change-extend-region-functions
beg end old-len)
(error (message "(poly-lock-after-change:jl-expand (%s %s %s)): %s"
beg end old-len (error-message-string err))
(setq jit-lock-start beg
jit-lock-end end)))
(setq beg (min beg jit-lock-start)
end (max end jit-lock-end))
(pm-map-over-spans
(lambda ()
(with-buffer-prepared-for-poly-lock
(let ((sbeg (nth 1 *span*))
(send (nth 2 *span*)))
(save-restriction
(widen)
(setq gl-beg (min gl-beg (max jit-lock-start sbeg))
gl-end (max gl-beg jit-lock-end send))
(put-text-property gl-beg gl-end 'fontified nil)))))
beg end nil nil nil 'no-cache)
(cons gl-beg gl-end))))))
(provide 'poly-lock)

Binary file not shown.

View File

@@ -0,0 +1,71 @@
;;; poly-markdown.el
;;
;; Filename: poly-markdown.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)
;; (require 'markdown-mode)
(defcustom pm-host/markdown
(pm-bchunkmode "Markdown"
:mode 'markdown-mode
:init-functions '(poly-markdown-remove-markdown-hooks))
"Markdown host chunkmode"
:group 'hostmodes
:type 'object)
(defcustom pm-inner/markdown
(pm-hbtchunkmode-auto "markdown"
:head-reg "^[ \t]*```[{ \t]*\\w.*$"
:tail-reg "^[ \t]*```[ \t]*$"
:retriever-regexp "```[ ]*{?\\(?:lang *= *\\)?\\([^ \n;=,}]+\\)"
:font-lock-narrow t)
"Markdown typical chunk."
:group 'innermodes
:type 'object)
(defcustom pm-poly/markdown
(pm-polymode-multi-auto "markdown"
:hostmode 'pm-host/markdown
:auto-innermode 'pm-inner/markdown)
"Markdown typical configuration"
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-markdown-mode "poly-markdown")
(define-polymode poly-markdown-mode pm-poly/markdown)
;;; FIXES:
(defun poly-markdown-remove-markdown-hooks ()
;; get rid of awful hooks
(remove-hook 'window-configuration-change-hook 'markdown-fontify-buffer-wiki-links t)
(remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t))
(provide 'poly-markdown)

Binary file not shown.

View File

@@ -0,0 +1,123 @@
;;; poly-noweb.el
;;
;; Filename: poly-noweb.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/noweb
(pm-polymode-one "noweb"
:hostmode 'pm-host/latex
:innermode 'pm-inner/noweb
:exporters '(pm-exporter/latexmk
pm-exporter/pdflatex
pm-exporter/lualatex
pm-exporter/xelatex)
:map '(("<" . poly-noweb-electric-<)))
"Noweb typical configuration"
:group 'polymodes
:type 'object)
(defcustom pm-inner/noweb
(pm-hbtchunkmode "noweb"
:head-reg "^[ \t]*<<\\(.*\\)>>="
:tail-reg "^[ \t]*@ *\\(%def.*\\)?$")
"Noweb typical chunk."
:group 'innermodes
:type 'object)
;;;###autoload (autoload 'poly-noweb-mode "poly-noweb")
(define-polymode poly-noweb-mode pm-poly/noweb)
(defun poly-noweb-electric-< (arg)
"Auto insert noweb chunk if at bol followed by white space.
If given an numerical argument, it simply insert `<'. Otherwise,
if at the beginning of a line in a host chunk insert \"<<>>=\", a
closing \"@\" and a newline if necessary."
(interactive "P")
(if (or arg (not (eq pm/type 'host)))
(self-insert-command (if (numberp arg) arg 1))
(if (not (looking-back "^[ \t]*"))
(self-insert-command 1)
(insert "<<")
(save-excursion
(insert ">>=\n\n@ ")
(unless(looking-at "\\s *$")
(newline)))
(ess-noweb-update-chunk-vector))))
(defcustom pm-exporter/pdflatex
(pm-shell-exporter "pdflatex"
:from
'(("latex" "\\.tex\\'" "LaTeX" "pdflatex -jobname %b %t %i"))
:to
'(("pdf" "pdf" "PDF" ""))
:quote t)
"Shell pdflatex exporter."
:group 'polymode-export
:type 'object)
(defcustom pm-exporter/lualatex
(pm-shell-exporter "LuaLaTeX"
:from
'(("latex" "\\.tex\\'" "LuaLaTeX" "lualatex -jobname %b %t %i"))
:to
'(("pdf" "pdf" "PDF" ""))
:quote t)
"Shell pdflatex exporter."
:group 'polymode-export
:type 'object)
(defcustom pm-exporter/xelatex
(pm-shell-exporter "XeLaTeX"
:from
'(("latex" "\\.tex\\'" "XeLaTeX" "xelatex -jobname %b %t %i"))
:to
'(("pdf" "pdf" "PDF" ""))
:quote t)
"Shell pdflatex exporter."
:group 'polymode-export
:type 'object)
(defcustom pm-exporter/latexmk
(pm-shell-exporter "latexmk"
:from
'(("latex" "\\.tex\\'" "LaTeX(MK)" "latexmk -jobname=%b %t %i"))
:to
'(("pdf" "pdf" "latex" "-pdf")
("xelatex" "pdf" "xe" "-xelatex")
("lualatex" "pdf" "lua" "-lualatex")
("ps" "ps" "latex" "-ps")
("dvi" "dvi" "latex" "-dvi"))
:quote t)
"Shell latexmk dvi, ps and pdf exporter."
:group 'polymode-export
:type 'object)
(provide 'poly-noweb)

Binary file not shown.

View File

@@ -0,0 +1,67 @@
;;; poly-org.el
;;
;; Filename: poly-org.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 'org-src)
(require 'polymode)
(defcustom pm-host/org
(pm-bchunkmode "Org mode"
:mode 'org-mode)
"Org host innermode"
:group 'hostmodes
:type 'object)
(defcustom pm-inner/org
(pm-hbtchunkmode-auto "org"
:head-reg "^[ \t]*#\\+begin_src .*$"
:tail-reg "^[ \t]*#\\+end_src"
:head-mode 'host
:tail-mode 'host
:retriever-regexp "#\\+begin_src +\\(\\(\\w\\|\\s_\\)+\\)"
:indent-offset org-edit-src-content-indentation
:font-lock-narrow t)
"Org typical chunk."
:group 'innermodes
:type 'object)
(defcustom pm-poly/org
(pm-polymode-multi-auto "org"
:hostmode 'pm-host/org
:auto-innermode 'pm-inner/org)
"Org typical configuration"
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-org-mode "poly-org")
(define-polymode poly-org-mode pm-poly/org)
(provide 'poly-org)

Binary file not shown.

View File

@@ -0,0 +1,107 @@
(require 'polymode)
;; We cannot have all these "requires" as part of polymode
;; https://github.com/vspinu/polymode/issues/69
;; (require 'css-mode)
;; (require 'scss-mode)
;; (require 'coffee-mode)
;; (require 'slim-mode)
;; (require 'ruby-mode)
;; (require 'markdown-mode)
(defcustom pm-host/slim
(pm-bchunkmode "slim" :mode 'slim-mode)
"slim host chunkmode"
:group 'hostmodes
:type 'object)
(pm-create-indented-block-matchers "slim-coffee" "^[^ ]*\\(.*:? *coffee: *\\)$")
(defcustom pm-inner/slim-coffee
(pm-hbtchunkmode "slim coffee include"
:mode 'coffee-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-coffee-head-matcher
:tail-reg 'pm-slim-coffee-tail-matcher)
"slim-coffee typical chunk."
:group 'innermodes
:type 'object)
(pm-create-indented-block-matchers "slim-css" "^[^ ]*\\(.*:? *css: *\\)$")
(defcustom pm-inner/slim-css
(pm-hbtchunkmode "slim css include"
:mode 'css-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-css-head-matcher
:tail-reg 'pm-slim-css-tail-matcher)
"slim-css typical chunk."
:group 'innermodes
:type 'object)
(pm-create-indented-block-matchers "slim-scss" "^[^ ]*\\(.*:? *scss: *\\)$")
(defcustom pm-inner/slim-scss
(pm-hbtchunkmode "slim scss include"
:mode 'scss-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-scss-head-matcher
:tail-reg 'pm-slim-scss-tail-matcher)
"slim-scss typical chunk."
:group 'innermodes
:type 'object)
(pm-create-indented-block-matchers "slim-ruby" "^[^ ]*\\(.*:? *ruby: *\\)$")
(defcustom pm-inner/slim-ruby
(pm-hbtchunkmode "slim ruby include"
:mode 'ruby-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-ruby-head-matcher
:tail-reg 'pm-slim-ruby-tail-matcher)
"slim-ruby typical chunk."
:group 'innermodes
:type 'object)
(pm-create-indented-block-matchers "slim-js" "^[^ ]*\\(.*:? *javascript: *\\)$")
(defcustom pm-inner/slim-js
(pm-hbtchunkmode "slim js include"
:mode 'js-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-js-head-matcher
:tail-reg 'pm-slim-js-tail-matcher)
"slim-js typical chunk."
:group 'innermodes
:type 'object)
(pm-create-indented-block-matchers "slim-md" "^[^ ]*\\(.*:? *markdown: *\\)$")
(defcustom pm-inner/slim-md
(pm-hbtchunkmode "slim markdown include"
:mode 'markdown-mode
:head-mode 'slim-mode
:tail-mode 'slim-mode
:head-reg 'pm-slim-md-head-matcher
:tail-reg 'pm-slim-md-tail-matcher)
"slim-markdown typical chunk."
:group 'innermodes
:type 'object)
(defcustom pm-poly/slim
(pm-polymode-multi "slim"
:hostmode 'pm-host/slim
:innermodes '(pm-inner/slim-coffee
pm-inner/slim-css
pm-inner/slim-scss
pm-inner/slim-js
pm-inner/slim-md
pm-inner/slim-ruby))
"slim typical polymode."
:group 'polymodes
:type 'object)
;;;###autoload (autoload 'poly-slim-mode "poly-slim")
(define-polymode poly-slim-mode pm-poly/slim)
(provide 'poly-slim)

Binary file not shown.

View File

@@ -0,0 +1,131 @@
;;; polymode-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "poly-R" "poly-R.el" (22987 56199 915383 887000))
;;; Generated autoloads from poly-R.el
(autoload 'poly-noweb+r-mode "poly-R")
(autoload 'poly-markdown+r-mode "poly-R")
(autoload 'poly-rapport-mode "poly-R")
(autoload 'poly-html+r-mode "poly-R")
(autoload 'poly-brew+r-mode "poly-R")
(autoload 'poly-r+c++-mode "poly-R")
(autoload 'poly-c++r-mode "poly-R")
(autoload 'poly-ess-help+r-mode "poly-R")
(autoload 'poly-Rd-mode "poly-R")
;;;***
;;;### (autoloads nil "poly-c" "poly-c.el" (22987 56199 825383 755000))
;;; Generated autoloads from poly-c.el
(autoload 'poly-noweb+c-mode "poly-c")
;;;***
;;;### (autoloads nil "poly-erb" "poly-erb.el" (22987 56199 785383
;;;;;; 697000))
;;; Generated autoloads from poly-erb.el
(autoload 'poly-coffee+erb-mode "poly-erb")
(autoload 'poly-javascript+erb-mode "poly-erb")
(autoload 'poly-html+erb-mode "poly-erb")
;;;***
;;;### (autoloads nil "poly-markdown" "poly-markdown.el" (22987 56199
;;;;;; 885383 843000))
;;; Generated autoloads from poly-markdown.el
(autoload 'poly-markdown-mode "poly-markdown")
;;;***
;;;### (autoloads nil "poly-noweb" "poly-noweb.el" (22987 56199 795383
;;;;;; 711000))
;;; Generated autoloads from poly-noweb.el
(autoload 'poly-noweb-mode "poly-noweb")
;;;***
;;;### (autoloads nil "poly-org" "poly-org.el" (22987 56199 895383
;;;;;; 864000))
;;; Generated autoloads from poly-org.el
(autoload 'poly-org-mode "poly-org")
;;;***
;;;### (autoloads nil "poly-slim" "poly-slim.el" (22987 56199 875383
;;;;;; 829000))
;;; Generated autoloads from poly-slim.el
(autoload 'poly-slim-mode "poly-slim")
;;;***
;;;### (autoloads nil "polymode" "polymode.el" (22987 56199 865383
;;;;;; 820000))
;;; Generated autoloads from polymode.el
(autoload 'define-polymode "polymode" "\
Define a new polymode MODE.
This macro defines command MODE and an indicator variable MODE
which becomes t when MODE is active and nil otherwise.
MODE command is similar to standard emacs major modes and it can
be used in `auto-mode-alist'. Standard hook MODE-hook is run at
the end of the initialization of each polymode buffer (both
indirect and base buffers). Additionally MODE-map is created
based on the CONFIG's :map slot and the value of the :keymap
argument; see below.
CONFIG is a name of a config object representing the mode.
MODE command can also be use as a minor mode. Current major mode
is not reinitialized if it coincides with the :mode slot of
CONFIG object or if the :mode slot is nil.
BODY contains code to be executed after the complete
initialization of the polymode (`pm-initialize') and before
running MODE-hook. Before the BODY code, you can write keyword
arguments, i.e. alternating keywords and values. The following
special keywords are supported:
:lighter SPEC Optional LIGHTER is displayed in the mode line when
the mode is on. If omitted, it defaults to
the :lighter slot of CONFIG object.
:keymap MAP Same as the KEYMAP argument.
If nil, a new MODE-map keymap is created what
directly inherits from the keymap defined by
the :map slot of CONFIG object. In most cases it
is a simple map inheriting form
`polymode-mode-map'. If t or an alist (of
bindings suitable to be passed to
`easy-mmode-define-keymap') a keymap MODE-MAP is
build by mergin this alist with the :map
specification of the CONFIG object. If a symbol,
it should be a variable whose value is a
keymap. No MODE-MAP is automatically created in
the latter case and :map slot of the CONFIG
object is ignored.
:after-hook A single lisp form which is evaluated after the mode hooks
have been run. It should not be quoted.
\(fn MODE CONFIG &optional KEYMAP &rest BODY)" nil t)
;;;***
;;;### (autoloads nil nil ("poly-base.el" "poly-lock.el" "polymode-classes.el"
;;;;;; "polymode-compat.el" "polymode-configuration.el" "polymode-core.el"
;;;;;; "polymode-debug.el" "polymode-export.el" "polymode-methods.el"
;;;;;; "polymode-pkg.el" "polymode-tangle.el" "polymode-weave.el")
;;;;;; (22987 56199 935383 917000))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:
;;; polymode-autoloads.el ends here

View File

@@ -0,0 +1,375 @@
(require 'eieio)
(require 'polymode-core)
;;; ROOT CLASS
(if (fboundp 'eieio-named)
(progn
(defclass pm-root (eieio-instance-inheritor eieio-named)
((-props
:initform '()
:type list
:documentation "Internal. Used to store various user
history values. Use `pm--prop-get' and `pm--prop-put' to
place key value pairs into this list."))
"Root polymode class.")
(when (fboundp 'defmethod)
;; bug #22840
(defmethod clone ((obj eieio-named) &rest params)
"Clone OBJ, initializing `:parent' to OBJ.
All slots are unbound, except those initialized with
PARAMS."
(let* ((newname (and (stringp (car params)) (pop params)))
(nobj (apply #'call-next-method obj params))
(nm (slot-value obj 'object-name)))
(eieio-oset nobj 'object-name
(or newname
(save-match-data
(if (and nm (string-match "-\\([0-9]+\\)" nm))
(let ((num (1+ (string-to-number
(match-string 1 nm)))))
(concat (substring nm 0 (match-beginning 0))
"-" (int-to-string num)))
(concat nm "-1")))))
nobj))))
(defclass pm-root (eieio-instance-inheritor)
((-props
:initform '()
:type list
:documentation "Internal. Plist used to store various extra
metadata such as user history. Use `pm--prop-get' and
`pm--prop-put' to place key value pairs into this list."))
"Root polymode class."))
;;; CONFIG
(defclass pm-polymode (pm-root)
((hostmode
:initarg :hostmode
:initform 'pm-host/blank
:type symbol
:custom symbol
:documentation
"Symbol pointing to an object of class pm-chunkmode
representing the host chunkmode.")
(minor-mode
:initarg :minor-mode
:initform 'polymode-minor-mode
:type symbol
:custom symbol
:documentation
"Symbol pointing to minor-mode function that should be
activated in all buffers (base and indirect). This is a
\"glue\" mode and is `polymode-minor-mode' by default. You
will rarely need to change this.")
(lighter
:initarg :lighter
:initform " PM"
:type string
:custom string
:documentation "Modline lighter.")
(exporters
:initarg :exporters
:initform '(pm-exporter/pandoc)
:type list
:custom list
:documentation
"List of names of polymode exporters available for this polymode.")
(exporter
:initarg :exporter
:initform nil
:type (or null symbol)
:custom symbol
:documentation
"Current exporter name. If non-nil should be the name of the
default exporter for this polymode. Can be set with
`polymode-set-exporter' command.")
(weavers
:initarg :weavers
:initform '()
:type list
:custom list
:documentation
"List of names of polymode weavers available for this polymode.")
(weaver
:initarg :weaver
:initform nil
:type (or null symbol)
:custom symbol
:documentation
"Current weaver name. If non-nil this is the default weaver
for this polymode. Can be dynamically set with
`polymode-set-weaver'")
(map
:initarg :map
:initform 'polymode-mode-map
:type (or symbol list)
:documentation
"Has a similar role as the :keymap argument in
`define-polymode' with the difference that this argument is
inherited through cloning, but :keymap argument is not. That
is, child objects derived through clone will inherit
the :map argument of its parents through the following
scheme: if :map is nil or an alist of keys, the parent is
inspected for :map argument and the keys are merged
recursively from parent to parent till a symbol :map slot is
met. If :map is a symbol, it must be a keymap, in which case
this keymap is used and no parents are further inspected
for :map slot. If :map is an alist it must be suitable for
`easy-mmode-define-keymap'.")
(init-functions
:initarg :init-functions
:initform '()
:type list
:documentation
"List of functions to run at the initialization time.
All init-functions in the inheritance chain are called. Parents
hooks first. So, if current config object C inherits from object
B, which in turn inherits from object A. Then A's init-functions
are called first, then B's and then C's.
Either customize this slot or use `object-add-to-list' function.")
(switch-buffer-functions
:initarg :switch-buffer-functions
:initform '()
:type list
:documentation
"List of functions to run at polymode buffer switch.
Each function is run with two arguments, OLD-BUFFER and
NEW-BUFFER.")
(-hostmode
:type (or null pm-chunkmode)
:documentation
"Dynamically populated `pm-chunkmode' object.")
(-innermodes
:type list
:initform '()
:documentation
"Dynamically populated list of chunkmodes objects that
inherit from `pm-hbtchunkmode'.")
(-buffers
:initform '()
:type list
:documentation
"Holds all buffers associated with current buffer. Dynamically populated."))
"Configuration for a polymode. Each polymode buffer contains a local
variable `pm/polymode' instantiated from this class or a subclass
of this class.")
(defclass pm-polymode-one (pm-polymode)
((innermode
:initarg :innermode
:type symbol
:custom symbol
:documentation
"Symbol of the chunkmode. At run time this object is cloned
and placed in -innermodes slot."))
"Configuration for a simple polymode that allows only one
innermode. For example noweb.")
(defclass pm-polymode-multi (pm-polymode)
((innermodes
:initarg :innermodes
:type list
:custom list
:initform nil
:documentation
"List of names of the chunkmode objects that are associated
with this configuration. At initialization time, all of
these are cloned and plased in -innermodes slot."))
"Configuration for a polymode that allows multiple (known in
advance) innermodes.")
(defclass pm-polymode-multi-auto (pm-polymode-multi)
((auto-innermode
:initarg :auto-innermode
:type symbol
:custom symbol
:documentation
"Name of pm-hbtchunkmode-auto object (a symbol). At run time
this object is cloned and placed in -auto-innermodes with
coresponding :mode slot initialized at run time.")
(-auto-innermodes
:type list
:initform '()
:documentation
"List of chunkmode objects that are auto-generated in
`pm-get-span' method for this class."))
"Configuration for a polymode that allows multiple innermodes
that are not known in advance. Examples are org-mode and markdown.")
;;; CHUNKMODE CLASSES
(defclass pm-chunkmode (pm-root)
((mode :initarg :mode
:type symbol
:initform nil
:custom symbol)
(protect-indent-line :initarg :protect-indent-line
:type boolean
:initform t
:custom boolean
:documentation
"Whether to modify local `indent-line-function' by narrowing
to current span first")
(indent-offset :initarg :indent-offset
:type integer
:initform 0
:documentation
"Offset to add when indenting chunk's line. Takes effect only
when :protect-indent-line is non-nil.")
(font-lock-narrow :initarg :font-lock-narrow
:type boolean
:initform t
:documentation
"Whether to narrow to span during font lock")
(adjust-face :initarg :adjust-face
:type (or number face list)
:custom (or number face list)
:initform nil
:documentation
"Fontification adjustments chunk face. It should be either,
nil, number, face or a list of text properties as in
`put-text-property' specification. If nil no highlighting
occurs. If a face, use that face. If a number, it is a
percentage by which to lighten/darken the default chunk
background. If positive - lighten the background on dark
themes and darken on light thems. If negative - darken in
dark thems and lighten in light thems.")
(init-functions
:initarg :init-functions
:initform '()
:type list
:documentation
"List of functions to called after the initialization of chunkmode has finished.
Functions are called the buffer associated with this
chunkmode. All init-functions in the inheritance chain are
called. Parents hooks first. So, if current config object C
inherits from object B, which in turn inherits from object
A. Then A's init-functions are called first, then B's and
then C's. Either customize this slot or use
`object-add-to-list' function.")
(switch-buffer-functions
:initarg :switch-buffer-functions
:initform '()
:type list
:documentation
"List of functions to run at polymode buffer switch.
Each function is run with two arguments, OLD-BUFFER and
NEW-BUFFER. In contrast to identically named slot in
`pm-polymode' class, these functions are run only when
NEW-BUFFER is associated with this chunkmode.")
(-buffer
:type (or null buffer)
:initform nil))
"Representatioin of a generic chunkmode object.")
(defclass pm-bchunkmode (pm-chunkmode)
()
"Representation of the body-only chunkmodes. Body-only
chunkmodes are commonly used as host modes. For example for a
the web-mdoe the hostmode is `html-mode', for nowweb mode the
host mode is usually `latex-mode', etc.")
(defclass pm-hbtchunkmode (pm-chunkmode)
((head-mode
:initarg :head-mode
:type symbol
:initform 'poly-head-tail-mode
:custom symbol
:documentation
"Chunk's header mode. If set to 'body, the head is considered
part of the chunk body. If set to 'host, head is considered
part of the surrounding host mode.")
(tail-mode
:initarg :tail-mode
:type symbol
:initform nil
:custom symbol
:documentation
"Chunk's tail mode. If nil, or 'head, the mode is picked
from :HEAD-MODE slot. If set to 'body, the tail's mode is the
same as chunk's body mode. If set to 'host, the mode will be
of the parent host.")
(head-reg
:initarg :head-reg
:initform ""
:type (or string symbol)
:custom (or string symbol)
:documentation "Regexp for the chunk start (aka head), or a
function returning the start and end positions of the head.
See `pm--default-matcher' for an example function.")
(tail-reg
:initarg :tail-reg
:initform ""
:type (or string symbol)
:custom (or string symbol)
:documentation "Regexp for chunk end (aka tail), or a
function returning the start and end positions of the tail.
See `pm--default-matcher' for an example function.")
(adjust-face
:initform 2)
(head-adjust-face
:initarg :head-adjust-face
:initform font-lock-type-face
:type (or null number face list)
:custom (or null number face list)
:documentation
"Can be a number, list or face.")
(tail-adjust-face
:initarg :tail-adjust-face
:initform nil
:type (or null number face list)
:custom (or null number face list)
:documentation
"Can be a number, list or face. If nil, take the
configuration from :head-adjust-face.")
(-head-buffer
:type (or null buffer)
:initform nil
:documentation
"This buffer is set automatically to -buffer if :head-mode is
'body, and to base-buffer if :head-mode is 'host")
(-tail-buffer
:initform nil
:type (or null buffer)))
"Representation of an inner Head-Body-Tail chunkmode.")
(defclass pm-hbtchunkmode-auto (pm-hbtchunkmode)
((retriever-regexp :initarg :retriever-regexp
:type (or null string)
:custom string
:initform nil
:documentation
"Regexp that is used to retrive the modes symbol from the
head of the chunkmode chunk. fixme: elaborate")
(retriever-num :initarg :retriever-num
:type integer
:custom integer
:initform 1
:documentation
"Subexpression to be matched by :retriver-regexp")
(retriever-function :initarg :retriever-function
:type symbol
:custom symbol
:initform nil
:documentation
"Function symbol used to retrive the modes symbol from the
head of the chunkmode chunk. It is called with no arguments
with the point positioned at the beginning of the chunk
header. It must return the mode name string or symbol (need
not include '-mode' postfix).)"))
"Representation of an inner chunkmode")
(provide 'polymode-classes)

Binary file not shown.

View File

@@ -0,0 +1,251 @@
;;; COMPATIBILITY and FIXES
(require 'polymode-core)
(require 'advice nil t)
(defgroup polymode-compat nil
"Polymode compatibility settings."
:group 'polymode)
;;; Various Wrappers for Around Advice
(defvar *span* nil)
;; advice doesn't provide named symbols. So we need to define specialized
;; wrappers for some key functions (unfinished)
(defmacro pm-define-wrapp-protected (fun)
"Declare protected function with the name fun--pm-wrapped.
Return new name (symbol). FUN is an unquoted name of a function."
(let* ((fun-name (symbol-name fun))
(new-fun (intern (format "%s--pm-wrapped" fun-name)))
(new-doc (format " Error Protected function created with `pm-define-protected-wrapp'.\n\n%s"
(or (documentation fun) ""))))
`(progn
(defun ,new-fun (&rest args)
,new-doc
(condition-case err
(apply ',fun args)
(error (message "(%s %s): %s"
,fun-name
(mapconcat (lambda (x) (format "%s" x)) args " ")
(error-message-string err)))))
',new-fun)))
(defun pm-apply-protected (fun args)
(when fun
(condition-case-unless-debug err
(apply fun args)
(error (message "(%s %s): %s %s"
(if (symbolp fun)
(symbol-name fun)
"anonymous")
(mapconcat (lambda (x) (format "%s" x)) args " ")
(error-message-string err)
;; (or (and (symbolp fun) "")
;; (replace-regexp-in-string "\n" "" (format "[%s]" fun)))
"[M-x pm-debug-mode RET for more info]"
)
(when pm-debug-mode
(backtrace))
nil))))
(defun pm-override-output-position (orig-fun &rest args)
"Restrict returned value of ORIG-FUN to fall into the current span.
*span* in `pm-map-over-spans` has precedence over span at point.'"
(if (and polymode-mode pm/polymode)
(let ((range (or (pm-span-to-range *span*)
(pm-get-innermost-range)))
(pos (pm-apply-protected orig-fun args)))
(and pos
(min (max pos (car range))
(cdr range))))
(apply orig-fun args)))
(defun pm-override-output-cons (orig-fun &rest args)
"Restrict returned (beg . end) of ORIG-FUN to fall into the current span.
*span* in `pm-map-over-spans` has precedence over span at point.'"
(if (and polymode-mode pm/polymode)
(let ((range (or (pm-span-to-range *span*)
(pm-get-innermost-range)))
(be (pm-apply-protected orig-fun args)))
(and be
(cons (min (max (car be) (car range))
(cdr range))
(max (min (cdr be) (cdr range))
(car range)))))
(apply orig-fun args)))
(defun pm-substitute-beg-end (orig-fun beg end &rest args)
"Execute orig-fun with first two arguments limited to current span.
*span* in `pm-map-over-spans` has precedence over span at point."
(if (and polymode-mode pm/polymode)
(let* ((pos (if (and (<= (point) end) (>= (point) beg))
(point)
end))
(range (or (pm-span-to-range *span*)
(pm-get-innermost-range pos)))
(new-beg (max beg (car range)))
(new-end (min end (cdr range))))
(pm-apply-protected orig-fun (append (list new-beg new-end) args)))
(apply orig-fun beg end args)))
(defun pm-execute-narrowed-to-span (orig-fun &rest args)
"Execute ORIG-FUN narrowed to the current span.
*span* in `pm-map-over-spans` has precedence over span at point."
(if (and polymode-mode pm/polymode)
(pm-with-narrowed-to-span *span*
(pm-apply-protected orig-fun args))
(apply orig-fun args)))
(defun pm-execute-with-no-polymode-hooks (orig-fun &rest args)
"Execute ORIG-FUN without allowing polymode core hooks.
That is, bind `pm-allow-post-command-hook' and
`pm-allow-after-change-hook' to nil. *span* in
`pm-map-over-spans' has precedence over span at point."
;; this advice is nowhere used yet
(if (and polymode-mode pm/polymode)
(let ((pm-allow-post-command-hook t)
(pm-allow-after-change-hook t))
;; This advice might be useful when functions can switch buffers to work
;; inside the base buffer (like basic-save-buffer does). Thus, we sync
;; points first.
(pm--synchronize-points)
;; save-excursion might be also often necessary
(apply orig-fun args))
(apply orig-fun args)))
(defun pm-execute-with-save-excursion (orig-fun &rest args)
"Execute ORIG-FUN within save-excursion."
;; This advice is required when other functions switch buffers to work inside
;; base buffer and don't restore the point. For some not very clear reason
;; this seem to be necessary for save-buffer which saves buffer but not point.
(if (and polymode-mode pm/polymode)
(progn
(pm--synchronize-points)
(save-excursion
(apply orig-fun args)))
(apply orig-fun args)))
(defun pm-around-advice (fun advice)
"Apply around ADVICE to FUN.
Check for if new advice is available and if FUN is a symbol, do
nothing otherwise. If FUN is a list, apply advice to each element
in a list. "
(when (and fun (fboundp 'advice-add))
(cond ((listp fun)
(dolist (el fun) (pm-around-advice el advice)))
((and (symbolp fun)
(not (advice-member-p advice fun)))
(advice-add fun :around advice)))))
;;; Syntax
(defun pm-execute-syntax-propertize-narrowed-to-span (orig-fun pos)
"Execute `syntax-propertize' narrowed to the current span.
Don't throw errors, but give relevant messages instead."
;; in emacs 25.1 internal--syntax-propertize is called from C. We
;; cannot advice it, but we can check for its argument. Very hackish
;; but I don't see another way besides re-defining that function.
(if (and polymode-mode pm/polymode)
(condition-case err
(save-excursion
(when (< syntax-propertize--done pos)
(pm-map-over-spans
(lambda ()
(when (< syntax-propertize--done pos)
(pm-with-narrowed-to-span *span*
(funcall orig-fun (min pos (point-max)))
(let ((new--done syntax-propertize--done))
(dolist (buff (oref pm/polymode -buffers))
(with-current-buffer buff
(setq-local syntax-propertize--done new--done)))))))
syntax-propertize--done pos)))
(error (message "(syntax-propertize %s): %s [M-x pm-debug-info RET to see backtrace]"
pos (error-message-string err))
(and pm-debug-mode
(backtrace))))
(funcall orig-fun pos)))
(pm-around-advice 'syntax-propertize 'pm-execute-syntax-propertize-narrowed-to-span)
;;; Flyspel
(defun pm--flyspel-dont-highlight-in-chunkmodes (beg end poss)
(or (get-text-property beg :pm-span-type)
(get-text-property end :pm-span-type)))
;;; C/C++/Java
(pm-around-advice 'c-before-context-fl-expand-region #'pm-override-output-cons)
(pm-around-advice 'c-state-semi-safe-place #'pm-override-output-position)
;; (advice-remove 'c-state-semi-safe-place #'pm-override-output-position)
;; c-font-lock-fontify-region calls it directly
;; (pm-around-advice 'font-lock-default-fontify-region #'pm-substitute-beg-end)
(pm-around-advice 'c-determine-limit #'pm-execute-narrowed-to-span)
;;; Python
(defun pm--python-dont-indent-to-0 (fun)
"Don't cycle to 0 indentation in polymode chunks."
(if (and polymode-mode pm/type)
(let ((last-command (unless (eq (pm--first-line-indent) (current-indentation))
last-command)))
(funcall fun))
(funcall fun)))
(pm-around-advice 'python-indent-line-function #'pm--python-dont-indent-to-0)
;;; Core Font Lock
(defun pm-check-for-real-change-in-extend-multiline (fun)
"Fix `font-lock-extend-region-multiline' which causes infloops on point-max.
Propagate only real change."
;; fixme: report this ASAP!
(let ((obeg font-lock-beg)
(oend font-lock-end)
(change (funcall fun)))
(and change
(not (eq obeg font-lock-beg))
(not (eq oend font-lock-end)))))
(pm-around-advice 'font-lock-extend-region-multiline #'pm-check-for-real-change-in-extend-multiline)
;;; Editing
(pm-around-advice 'fill-paragraph #'pm-execute-narrowed-to-span)
;; `save-buffer` misbehaves because after each replacement modification hooks
;; are triggered and poly buffer is switched in unpredictable fashion.
;;
;; https://github.com/vspinu/polymode/issues/93 It can be
;; reproduced with (add-hook 'before-save-hook 'delete-trailing-whitespace nil
;; t) in the base buffer.
;;
;; save-excursion is probably not quite right fix for this but it seem to work
(pm-around-advice 'basic-save-buffer #'pm-execute-with-save-excursion)
;; Query replace were probably misbehaving due to unsaved match data.
;; (https://github.com/vspinu/polymode/issues/92) The following is probably not
;; necessary.
;; (pm-around-advice 'perform-replace 'pm-execute-inhibit-modification-hooks)
;;; EVIL
(defun polymode-switch-buffer-keep-evil-state-maybe (old-buffer new-buffer)
(when (and (boundp 'evil-state)
evil-state)
(let ((old-state (buffer-local-value 'evil-state old-buffer))
(new-state (buffer-local-value 'evil-state new-buffer)))
(unless (eq old-state new-state)
(with-current-buffer new-buffer
(evil-change-state old-state))))))
(eval-after-load 'evil-core
'(add-hook 'polymode-switch-buffer-hook 'polymode-switch-buffer-keep-evil-state-maybe))
(provide 'polymode-compat)

Binary file not shown.

View File

@@ -0,0 +1,29 @@
;; Examples of polymode configuration. Choose what suits your needs and place
;; into your .emacs file.
;;; MARKDOWN
(add-to-list 'auto-mode-alist '("\\.md$" . poly-markdown-mode))
;;; ORG
;; org is not working presently
;; (add-to-list 'auto-mode-alist '("\\.org" . poly-org-mode))
;;; R related modes
(add-to-list 'auto-mode-alist '("\\.Snw$" . poly-noweb+r-mode))
(add-to-list 'auto-mode-alist '("\\.Rnw$" . poly-noweb+r-mode))
(add-to-list 'auto-mode-alist '("\\.Rmd$" . poly-markdown+r-mode))
(add-to-list 'auto-mode-alist '("\\.rapport$" . poly-rapport-mode))
(add-to-list 'auto-mode-alist '("\\.Rhtml$" . poly-html+r-mode))
(add-to-list 'auto-mode-alist '("\\.Rbrew$" . poly-brew+r-mode))
(add-to-list 'auto-mode-alist '("\\.Rcpp$" . poly-r+c++-mode))
(add-to-list 'auto-mode-alist '("\\.cppR$" . poly-c++r-mode))
;;; ERB modes
(add-to-list 'auto-mode-alist '("\\.js.erb$" . poly-javascript+erb-mode))
(add-to-list 'auto-mode-alist '("\\.coffee.erb$" . poly-coffee+erb-mode))
(add-to-list 'auto-mode-alist '("\\.html.erb$" . poly-html+erb-mode))
;;; Slim mode
(add-to-list 'auto-mode-alist '("\\.slim$" . poly-slim-mode))
(provide 'polymode-configuration)

Binary file not shown.

View File

@@ -0,0 +1,660 @@
;; -*- lexical-binding: t -*-
;; COMMON INITIALIZATION, UTILITIES and INTERNALS which didn't fit anywhere else
(require 'cl)
(require 'font-lock)
(require 'color)
(require 'eieio)
(require 'eieio-base)
(require 'eieio-custom)
(require 'format-spec)
(defgroup polymode nil
"Object oriented framework for multiple modes based on indirect buffers"
:link '(emacs-commentary-link "polymode")
:group 'tools)
(defgroup polymodes nil
"Polymode Configuration Objects"
:group 'polymode)
(defgroup hostmodes nil
"Polymode Host Chunkmode Objects"
:group 'polymode)
(defgroup innermodes nil
"Polymode Chunkmode Objects"
:group 'polymode)
(defcustom polymode-display-process-buffers t
"When non-nil, display weaving and exporting process buffers."
:group 'polymode
:type 'boolean)
(defcustom polymode-skip-processing-when-unmodified t
"If non-nil, consider modification times of input and output files.
Skip weaving or exporting process when output file is more recent
than the input file."
:group 'polymode
:type 'boolean)
(defcustom polymode-mode-name-override-alist '((elisp . emacs-lisp))
"An alist of inner mode overrides.
When inner mode is automatically detected from the header of the
inner chunk (such as in markdown mode), the detected symbol might
not correspond to the desired mode. This alist maps discovered
symbols into desired modes.
For example
(add-to-list 'polymode-mode-name-override-alist '(julia . ess-julia))
will cause installation of `ess-julia-mode' in markdown ```julia chunks."
:group 'polymode
:type 'alist)
(defvar polymode-switch-buffer-hook nil
"Hook run on switching to a different buffer.
Each function is run with two arguments `old-buffer' and
`new-buffer'. This hook is commonly used to transfer state
between buffers. The hook is run in a new buffer, but you should
not rely on that. Slot :switch-buffer-functions in `pm-polymode'
and `pm-chunkmode' objects provides same functionality for
narrower scope.")
(defvar polymode-init-host-hook nil
"Hook run on initialization of every hostmode.
Ran in a base buffer from `pm-initialze'
methods. Slot :init-functions in `pm-polymode' objects provides
similar hook for more focused scope. See
`polymode-init-inner-hook' and :init-functions slot in
`pm-chunkmode' objects for similar hooks for inner chunkmodes.")
(defvar polymode-init-inner-hook nil
"Hook run on initialization of every `pm-chunkmode' object.
The hook is run in chunkmode's body buffer from `pm-initialze'
`pm-chunkmode' methods. Slot :init-functions `pm-chunkmode'
objects provides same functionality for narrower scope. See also
`polymode-init-host-hook'.")
;; esential vars
(defvar-local pm/polymode nil)
(defvar-local pm/chunkmode nil)
(defvar-local pm/type nil)
(defvar-local pm--indent-line-function-original nil)
;; (defvar-local pm--killed-once nil)
(defvar-local polymode-mode nil
"This variable is t if current \"mode\" is a polymode.")
;; silence the compiler for now
(defvar pm--output-file nil)
(defvar pm--input-buffer nil)
(defvar pm--input-file nil)
(defvar pm--export-spec nil)
(defvar pm--input-not-real nil)
(defvar pm--output-not-real nil)
(defvar pm/type)
(defvar pm/polymode)
(defvar pm/chunkmode)
(defvar *span*)
(defvar pm-allow-fontification t)
(defvar pm-allow-after-change-hook t)
(defvar pm-allow-post-command-hook t)
(defvar pm-initialization-in-progress nil
;; We need this during cascading call-next-method in pm-initialize.
;; -innermodes are initialized after the hostmode setup has taken place. This
;; means that pm-get-span and all the functionality that relies on it will
;; fail to work correctly during the initialization in the call-next-method.
;; This is particularly relevant to font-lock setup and user hooks.
"Non nil during polymode objects initialization.
If this variable is non-nil, various chunk manipulation commands
relying on `pm-get-span' might not function correctly.")
;; methods api from polymode-methods.el
(declare-function pm-initialize "polymode-methods")
(declare-function pm-get-buffer-create "polymode-methods")
(declare-function pm-select-buffer "polymode-methods")
(declare-function pm-get-adjust-face "polymode-methods")
(declare-function pm-get-span "polymode-methods")
(declare-function pm-indent-line "polymode-methods")
;;; CORE
(defsubst pm-base-buffer ()
;; fixme: redundant with :base-buffer
"Return base buffer of current buffer, or the current buffer if it's direct."
(or (buffer-base-buffer (current-buffer))
(current-buffer)))
(defun pm-get-cached-span (&optional pos)
"Get cached span at POS"
(let ((span (get-text-property (or pos (point)) :pm-span)))
(when span
(save-restriction
(widen)
(let* ((beg (nth 1 span))
(end (max beg (1- (nth 2 span)))))
(when (<= end (point-max))
(and (eq span (get-text-property beg :pm-span))
(eq span (get-text-property end :pm-span))
span)))))))
(defun pm-get-innermost-span (&optional pos no-cache)
"Get span object at POS.
If NO-CACHE is non-nil, don't use cache and force re-computation
of the span."
(save-excursion
(save-restriction
(widen)
(let* ((span (or (and (not no-cache)
(pm-get-cached-span pos))
(pm-get-span pm/polymode pos)))
(beg (nth 1 span))
(end (nth 2 span)))
;; might be used by external applications like flyspell
(with-silent-modifications
(add-text-properties beg end
(list :pm-span span
:pm-span-type (car span)
:pm-span-beg beg
:pm-span-end end)))
span))))
(defun pm-span-to-range (span)
(and span (cons (nth 1 span) (nth 2 span))))
(defun pm-get-innermost-range (&optional pos no-cache)
(pm-span-to-range (pm-get-innermost-span pos no-cache)))
(defvar pm--select-buffer-visibly nil)
(defun pm-switch-to-buffer (&optional pos-or-span)
"Bring the appropriate polymode buffer to front.
This is done visually for the user with `switch-to-buffer'. All
necessary adjustment like overlay and undo history transport are
performed."
(let ((span (if (or (null pos-or-span)
(number-or-marker-p pos-or-span))
(pm-get-innermost-span pos-or-span)
pos-or-span))
(pm--select-buffer-visibly t))
(pm-select-buffer (car (last span)) span)))
(defun pm-set-buffer (&optional pos-or-span)
"Set buffer to polymode buffer appropriate for POS-OR-SPAN.
This is done with `set-buffer' and no visual adjustments are
done."
(let ((span (if (or (null pos-or-span)
(number-or-marker-p pos-or-span))
(pm-get-innermost-span pos-or-span)
pos-or-span))
(pm--select-buffer-visibly nil))
(pm-select-buffer (car (last span)) span)))
(defun pm-map-over-spans (fun beg end &optional count backwardp visiblyp no-cache)
"For all spans between BEG and END, execute FUN.
FUN is a function of no args. It is executed with point at the
beginning of the span. Buffer is *not* narrowed to the span. If
COUNT is non-nil, jump at most that many times. If BACKWARDP is
non-nil, map backwards. During the call of FUN, a dynamically
bound variable *span* holds the current innermost span."
;; Important! Never forget to save-excursion when calling
;; map-overs-spans. Mapping can end different buffer and invalidate whatever
;; caller that used your function.
(save-restriction
(widen)
(setq end (min end (point-max)))
(goto-char (if backwardp end beg))
(let* ((nr 1)
(*span* (pm-get-innermost-span (point) no-cache))
old-span
moved)
;; if beg (end) coincide with span's end (beg) don't process previous (next) span
(if backwardp
(and (eq end (nth 1 *span*))
(setq moved t)
(not (bobp))
(forward-char -1))
(and (eq beg (nth 2 *span*))
(setq moved t)
(not (eobp))
(forward-char 1)))
(when moved
(setq *span* (pm-get-innermost-span (point) no-cache)))
(while (and (if backwardp
(> (point) beg)
(< (point) end))
(or (null count)
(< nr count)))
(let ((pm--select-buffer-visibly visiblyp))
(pm-select-buffer (car (last *span*)) *span*)) ;; object and span
;; FUN might change buffer and invalidate our *span*. How can we
;; intelligently check for this? After-change functions have not been
;; run yet (or did they?). We can track buffer modification time
;; explicitly (can we?)
(goto-char (nth 1 *span*))
(save-excursion
(funcall fun))
;; enter next/previous chunk as head-tails don't include their boundaries
(if backwardp
(goto-char (max 1 (1- (nth 1 *span*))))
(goto-char (min (point-max) (1+ (nth 2 *span*)))))
(setq old-span *span*)
(setq *span* (pm-get-innermost-span (point) no-cache)
nr (1+ nr))
;; Ensure progress and avoid infloop due to bad regexp or who knows
;; what. Move char by char till we get higher/lower span. Cache is not
;; used.
(while (and (not (eobp))
(if backwardp
(> (nth 2 *span*) (nth 1 old-span))
(< (nth 1 *span*) (nth 2 old-span))))
(forward-char 1)
(setq *span* (pm-get-innermost-span (point) t)))))))
(defun pm--reset-ppss-last (&optional span-start force)
"Reset `syntax-ppss-last' cache if it was recorded before SPAN-START.
If SPAN-START is nil, use span at point. If force, reset
regardless of the position `syntax-ppss-last' was recorder at."
;; syntax-ppss has its own condition-case for this case, but that means
;; throwing an error each time it calls parse-partial-sexp
(setq span-start (or span-start (car (pm-get-innermost-range))))
(when (or force
(and syntax-ppss-last
(car syntax-ppss-last)
;; non-strict is intentional (occasionally ppss is screwed)
(<= (car syntax-ppss-last) span-start)))
(setq syntax-ppss-last
(cons span-start (list 0 nil span-start nil nil nil 0)))))
(defun pm-narrow-to-span (&optional span)
"Narrow to current chunk."
(interactive)
(unless (= (point-min) (point-max))
(let ((span (or span
(pm-get-innermost-span))))
(let ((sbeg (nth 1 span))
(send (nth 2 span)))
(pm--reset-ppss-last sbeg t)
(narrow-to-region sbeg send)))))
(defmacro pm-with-narrowed-to-span (span &rest body)
(declare (indent 1) (debug body))
`(save-restriction
(pm-narrow-to-span ,span)
,@body))
;;; UTILITIES
(defvar polymode-display-output-file t
"When non-nil automatically display output file in emacs.
This is temporary variable, it might be changed or removed in the
near future.")
(defun pm--display-file (ofile)
(when ofile
;; errors might occur (most notably with open-with package errors are intentional)
;; We need to catch those if we want to display multiple files like with Rmarkdown
(condition-case err
(let ((buff (get-file-buffer ofile)))
;; silently kill and re-open
(when buff
(with-current-buffer buff
(revert-buffer t t)))
(when polymode-display-output-file
(if (string-match-p "html\\|htm$" ofile)
(browse-url ofile)
(display-buffer (find-file-noselect ofile 'nowarn)))))
(error (message "Error while displaying '%s': %s"
(file-name-nondirectory ofile)
(error-message-string err))))))
(defun pm--symbol-name (str-or-symbol)
(if (symbolp str-or-symbol)
(symbol-name str-or-symbol)
str-or-symbol))
(defun pm--get-mode-symbol-from-name (str &optional no-fallback)
"Guess and return mode function."
(let* ((str (pm--symbol-name
(or (cdr (assq (intern (pm--symbol-name str))
polymode-mode-name-override-alist))
str)))
(mname (if (string-match-p "-mode$" str)
str
(concat str "-mode"))))
(or (pm--get-existent-mode (intern mname) t)
(pm--get-existent-mode (intern (downcase mname))) no-fallback)))
(defun pm--get-existent-mode (mode &optional no-fallback)
"Check if MODE symbol is defined and is a valid function.
If so, return it, otherwise return `poly-fallback-mode' and issue
a warning."
(cond ((fboundp mode) mode)
(no-fallback nil)
(t (message "Cannot find function `%s', using `poly-fallback-mode'" mode)
'poly-fallback-mode)))
(defun pm--oref-with-parents (object slot)
"Merge slots SLOT from the OBJECT and all its parent instances."
(let (VALS)
(while object
(setq VALS (append (and (slot-boundp object slot) ; don't cascade
(eieio-oref object slot))
VALS)
object (and (slot-boundp object :parent-instance)
(oref object :parent-instance))))
VALS))
(defun pm--abrev-names (list abrev-regexp)
"Abbreviate names in LIST by replacing abrev-regexp with empty string."
(mapcar (lambda (nm)
(let ((str-nm (if (symbolp nm)
(symbol-name nm)
nm)))
(cons (replace-regexp-in-string abrev-regexp "" str-nm)
str-nm)))
list))
(defun pm--prop-put (key val &optional object)
(oset (or object pm/polymode) -props
(plist-put (oref (or object pm/polymode) -props) key val)))
(defun pm--prop-get (key &optional object)
(plist-get (oref (or object pm/polymode) -props) key))
(defun pm--comment-region (beg end)
;; mark as syntactic comment
(when (> end 1)
(with-silent-modifications
(let ((beg (or beg (region-beginning)))
(end (or end (region-end))))
(let ((ch-beg (char-after beg))
(ch-end (char-before end)))
(add-text-properties beg (1+ beg)
(list 'syntax-table (cons 11 ch-beg)
'rear-nonsticky t
'polymode-comment 'start))
(add-text-properties (1- end) end
(list 'syntax-table (cons 12 ch-end)
'rear-nonsticky t
'polymode-comment 'end)))))))
(defun pm--uncomment-region (beg end)
;; Remove all syntax-table properties.
;; fixme: this beggs for problems
(when (> end 1)
(with-silent-modifications
(let ((props '(syntax-table nil rear-nonsticky nil polymode-comment nil)))
(remove-text-properties (max beg (point-min)) (min end (point-max)) props)
;; (remove-text-properties beg (1+ beg) props)
;; (remove-text-properties end (1- end) props)
))))
(defun pm--synchronize-points (&rest ignore)
"Synchronize points in all buffers.
IGNORE is there to allow this function in advises."
(when polymode-mode
(let ((pos (point))
(cbuff (current-buffer)))
(dolist (buff (oref pm/polymode -buffers))
(when (and (not (eq buff cbuff))
(buffer-live-p buff))
(with-current-buffer buff
(goto-char pos)))))))
(defun pm--completing-read (prompt collection &optional predicate require-match initial-input hist def inherit-input-method)
"Wrapper for `completing-read'.
Takes care when collection is an alist of (name . meta-info). If
so, asks for names, but returns meta-info for that name. Enforce
require-match = t. Also takes care of adding the most relevant
DEF from history."
(if (and (listp collection)
(listp (car collection)))
(let* ((candidates (mapcar #'car collection))
(thist (and hist
(delq nil (mapcar (lambda (x) (car (member x candidates)))
(symbol-value hist)))))
(def (or def (car thist))))
(assoc (completing-read prompt candidates predicate t initial-input hist def inherit-input-method)
collection))
(completing-read prompt candidates predicate require-match initial-input hist def inherit-input-method)))
;; Weaving and Exporting common utilities
(defun pm--wrap-callback (processor slot ifile)
;; replace processor :sentinel or :callback temporally in order to export-spec as a
;; followup step or display the result
(let ((sentinel1 (eieio-oref processor slot))
(cur-dir default-directory)
(exporter (symbol-value (oref pm/polymode :exporter)))
(obuffer (current-buffer)))
(if pm--export-spec
(let ((espec pm--export-spec))
(lambda (&rest args)
(with-current-buffer obuffer
(let ((wfile (apply sentinel1 args))
(pm--export-spec nil)
(pm--input-not-real t))
;; If no wfile, probably errors occurred. So we stop.
(when wfile
(when (listp wfile)
;; In an unlikely situation weaver can generate multiple
;; files. Pick the first one.
(setq wfile (car wfile)))
(pm-export exporter (car espec) (cdr espec) wfile))))))
(lambda (&rest args)
(with-current-buffer obuffer
(let ((ofile (apply sentinel1 args)))
(when ofile
(let ((ofiles (if (listp ofile) ofile (list ofile))))
(dolist (f ofiles)
(pm--display-file (expand-file-name f cur-dir)))))))))))
(defun pm--file-mod-time (file)
(and (stringp file)
(file-exists-p file)
(nth 5 (file-attributes file))))
(defvar-local pm--process-buffer nil)
(defun pm--run-shell-command (command sentinel buff-name message)
"Run shell command interactively.
Run command in a buffer (in comint-shell-mode) in order to be
able to accept user interaction."
;; simplified version of TeX-run-TeX
(require 'comint)
(let* ((buffer (get-buffer-create buff-name))
(process nil)
(command-buff (current-buffer))
(ofile pm--output-file)
;; weave/export buffers are re-usable; need to transfer some vars
(dd default-directory)
;; (command (shell-quote-argument command))
)
(with-current-buffer buffer
(setq-local default-directory dd)
(read-only-mode -1)
(erase-buffer)
(insert message)
(comint-exec buffer buff-name shell-file-name nil
(list shell-command-switch command))
(setq process (get-buffer-process buffer))
(comint-mode)
(set-process-sentinel process sentinel)
(setq pm--process-buffer t)
(set-marker (process-mark process) (point-max))
;; for communication with sentinel
(process-put process :output-file pm--output-file)
(process-put process :output-file-mod-time (pm--file-mod-time pm--output-file))
(process-put process :input-file pm--input-file)
(when polymode-display-process-buffers
(display-buffer buffer `(nil . ((inhibit-same-window . ,pop-up-windows)))))
nil)))
(defun pm--make-shell-command-sentinel (action)
(lambda (process name)
"Sentinel built with `pm--make-shell-command-sentinel'."
(let ((buff (process-buffer process))
(status (process-exit-status process)))
(if (> status 0)
(progn
(message "Errors during %s; process exit status %d" action status)
(ding) (sit-for 1)
nil)
(with-current-buffer buff
(let ((ofile (process-get process :output-file)))
(cond
;; 1. output-file guesser
((functionp ofile) (funcall ofile))
;; 2. string
(ofile
(let ((otime (process-get process :output-file-mod-time))
(ntime (pm--file-mod-time ofile)))
(if (or (null ntime)
(and otime
(not (time-less-p otime ntime))))
;; mod time didn't change
;; tothink: shall we still return ofile for display?
(progn
(display-buffer (current-buffer))
(message "Output file unchanged. Either input unchanged or errors during %s." action)
(ding) (sit-for 1)
ofile)
;; else, all is good, we return the file name
;; (display-buffer (current-buffer))
(message "Done with %s" action)
ofile)))
;; 3. output file is not known; display process buffer
(t (display-buffer (current-buffer)) nil))))))))
(fset 'pm-default-export-sentinel (pm--make-shell-command-sentinel "export"))
(fset 'pm-default-shell-weave-sentinel (pm--make-shell-command-sentinel "weaving"))
(defun pm--make-selector (specs elements)
(cond ((listp elements)
(let ((spec-alist (cl-mapcar #'cons specs elements)))
(lambda (selsym &rest ignore)
(cdr (assoc selsym spec-alist)))))
((functionp elements) elements)
(t (error "elements argument must be either a list or a function"))))
(defun pm--selector (processor type id)
(let ((spec (or (assoc id (eieio-oref processor type))
(error "%s spec '%s' cannot be found in '%s'"
(symbol-name type) id (eieio-object-name processor))))
(names (cond
;; exporter slots
((eq type :from) '(regexp doc command))
((eq type :to) '(ext doc t-spec))
;; weaver slot
((eq type :from-to) '(regexp ext doc command))
(t (error "invalid type '%s'" type)))))
(pm--make-selector names (cdr spec))))
(defun pm--selector-match (selector &optional file)
(or (funcall selector 'match file)
(string-match-p (funcall selector 'regexp)
(or file buffer-file-name))))
(defun pm--selectors (processor type)
(let ((ids (mapcar #'car (eieio-oref processor type))))
(mapcar (lambda (id) (cons id (pm--selector processor type id))) ids)))
(defun pm--output-command.file (output-file-format sfrom &optional sto quote)
;; !!Must be run in input buffer!!
(cl-flet ((squote (arg) (or (and (stringp arg)
(if quote (shell-quote-argument arg) arg))
"")))
(let* ((base-ofile (or (funcall (or sto sfrom) 'output-file)
(let ((ext (funcall (or sto sfrom) 'ext)))
(when ext
(concat (format output-file-format
(file-name-base buffer-file-name))
"." ext)))))
(ofile (and (stringp base-ofile)
(expand-file-name base-ofile)))
(oname (and (stringp base-ofile)
(file-name-base base-ofile)))
(t-spec (and sto (funcall sto 't-spec)))
(command-w-formats (or (and sto (funcall sto 'command))
(and (listp t-spec) (car t-spec))
(funcall sfrom 'command)))
(command (format-spec command-w-formats
(list (cons ?i (squote (file-name-nondirectory buffer-file-name)))
(cons ?I (squote buffer-file-name))
(cons ?o (squote base-ofile))
(cons ?O (squote ofile))
(cons ?b (squote oname))
(cons ?t (squote t-spec))))))
(cons command (or ofile base-ofile)))))
(defun pm--process-internal (processor from to ifile &optional callback quote)
(let ((is-exporter (object-of-class-p processor 'pm-exporter)))
(if is-exporter
(unless (and from to)
(error "For exporter both FROM and TO must be supplied (from: %s, to: %s)" from to))
(unless from
;; it represents :from-to slot
(error "For weaver FROM must be supplied (from: %s)" from)))
(let* ((sfrom (if is-exporter
(pm--selector processor :from from)
(pm--selector processor :from-to from)))
(sto (and is-exporter (pm--selector processor :to to)))
(ifile (or ifile buffer-file-name))
;; fixme: nowarn is only right for inputs from weavers, you need to
;; save otherwise
(ibuffer (if pm--input-not-real
;; for exporter input we silently re-fetch the file
;; even if it was modified
(find-file-noselect ifile t)
;; if real user file, get it or fetch it
(or (get-file-buffer ifile)
(find-file-noselect ifile))))
(output-format (if is-exporter
polymode-exporter-output-file-format
polymode-weave-output-file-format)))
(with-current-buffer ibuffer
(save-buffer)
(let ((comm.ofile (pm--output-command.file output-format sfrom sto quote)))
(message "%s '%s' with '%s' ..." (if is-exporter "Exporting" "Weaving")
(file-name-nondirectory ifile) (eieio-object-name processor))
(let* ((pm--output-file (cdr comm.ofile))
(pm--input-file ifile)
;; skip weaving step if possible
;; :fixme this should not happen after weaver/exporter change
;; or after errors in previous exporter
(omt (and polymode-skip-processing-when-unmodified
(stringp pm--output-file)
(pm--file-mod-time pm--output-file)))
(imt (and omt (pm--file-mod-time pm--input-file)))
(ofile (or (and imt (time-less-p imt omt) pm--output-file)
(let ((fun (oref processor :function))
(args (delq nil (list callback from to))))
(apply fun (car comm.ofile) args)))))
;; ofile is non-nil in two cases:
;; -- synchronous back-ends (very uncommon)
;; -- when output is transitional (not real) and mod time of input < output
(when ofile
(if pm--export-spec
;; same logic as in pm--wrap-callback
(let ((pm--input-not-real t)
(espec pm--export-spec)
(pm--export-spec nil))
(when (listp ofile)
(setq ofile (car ofile)))
(pm-export (symbol-value (oref pm/polymode :exporter))
(car espec) (cdr espec)
ofile))
(pm--display-file ofile)))))))))
(provide 'polymode-core)

Binary file not shown.

View File

@@ -0,0 +1,339 @@
;;; polymode.el --- Various tools for debugging and tracing polymode
(defvar pm--underline-overlay
(let ((overlay (make-overlay (point) (point))))
(overlay-put overlay 'face '(:underline (:color "red" :style wave)))
overlay)
"Overlay used in `pm-debug-mode'.")
(defvar pm--inverse-video-overlay
(let ((overlay (make-overlay (point) (point))))
(overlay-put overlay 'face '(:inverse-video t))
overlay)
"Overlay used by `pm-debug-map-over-spans-and-highlight'.")
(defvar pm-debug-minor-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "M-n M-i") 'pm-debug-info-on-current-span)
(define-key map (kbd "M-n i") 'pm-debug-info-on-current-span)
(define-key map (kbd "M-n M-p") 'pm-debug-print-relevant-variables)
(define-key map (kbd "M-n M-t m") 'pm-debug-toogle-info-message)
(define-key map (kbd "M-n M-t f") 'pm-debug-toggle-fontification)
(define-key map (kbd "M-n M-t M-f") 'pm-debug-toggle-fontification)
(define-key map (kbd "M-n M-t p") 'pm-debug-toggle-post-command)
(define-key map (kbd "M-n M-t c") 'pm-debug-toggle-after-change)
(define-key map (kbd "M-n M-t M-c") 'pm-debug-toggle-after-change)
(define-key map (kbd "M-n M-t a") 'pm-debug-toggle-all)
(define-key map (kbd "M-n M-t M-a") 'pm-debug-toggle-all)
(define-key map (kbd "M-n M-t t") 'pm-debug-trace-relevant-functions)
(define-key map (kbd "M-n M-t M-t") 'pm-debug-trace-relevant-functions)
(define-key map (kbd "M-n M-t u") 'pm-debug-untrace-relevant-functions)
(define-key map (kbd "M-n M-t M-u") 'pm-debug-untrace-relevant-functions)
(define-key map (kbd "M-n M-h") 'pm-debug-map-over-spans-and-highlight)
(define-key map (kbd "M-n M-f t") 'pm-debug-toggle-fontification)
(define-key map (kbd "M-n M-f s") 'pm-debug-fontify-current-span)
(define-key map (kbd "M-n M-f e") 'pm-debug-fontify-last-font-lock-error)
(define-key map (kbd "M-n M-f h") 'pm-debug-highlight-last-font-lock-error-region)
(define-key map (kbd "M-n M-f M-t") 'pm-debug-toggle-fontification)
(define-key map (kbd "M-n M-f M-s") 'pm-debug-fontify-current-span)
(define-key map (kbd "M-n M-f M-e") 'pm-debug-fontify-last-font-lock-error)
map))
(define-minor-mode pm-debug-minor-mode
"Turns on/off useful facilities for debugging polymode.
Key bindings:
\\{pm-debug-minor-mode-map}"
nil
" PMDBG"
:group 'polymode
(interactive)
(if pm-debug-minor-mode
(progn
;; this is global hook. No need to complicate with local hooks
(add-hook 'post-command-hook 'pm-debug-highlight-current-span))
(delete-overlay pm--underline-overlay)
(delete-overlay pm--inverse-video-overlay)
(remove-hook 'post-command-hook 'pm-debug-highlight-current-span)))
(defun pm-debug-minor-mode-on ()
;; activating everywhere (in case font-lock infloops in a polymode buffer )
;; this doesn't activate in fundamental mode
(pm-debug-minor-mode t))
(define-globalized-minor-mode pm-debug-mode pm-debug-minor-mode pm-debug-minor-mode-on)
(defun pm-debug-highlight-current-span ()
(when polymode-mode
(unless (memq this-command '(pm-debug-info-on-current-span
pm-debug-highlight-last-font-lock-error-region))
(delete-overlay pm--inverse-video-overlay))
(condition-case err
(let ((span (pm-get-innermost-span)))
(when pm-debug-display-info-message
(pm--debug-info span))
(move-overlay pm--underline-overlay (nth 1 span) (nth 2 span) (current-buffer)))
(error (message "%s" (error-message-string err))))))
(defgeneric pm-debug-info (chunkmode))
(defmethod pm-debug-info (chunkmode)
(format "class:%s" (eieio-object-class-name chunkmode)))
(defmethod pm-debug-info ((chunkmode pm-hbtchunkmode))
(format "head-reg:\"%s\" tail-reg:\"%s\" %s"
(oref chunkmode :head-reg) (oref chunkmode :tail-reg)
(call-next-method)))
(defmethod pm-debug-info ((chunkmode pm-hbtchunkmode))
(format "head-reg:\"%s\" tail-reg:\"%s\" %s"
(oref chunkmode :head-reg) (oref chunkmode :tail-reg)
(call-next-method)))
(defmethod pm-debug-info ((chunkmode pm-hbtchunkmode-auto))
(call-next-method))
(defun pm--debug-info (&optional span)
(let* ((span (or span (and polymode-mode (pm-get-innermost-span))))
(message-log-max nil)
(beg (nth 1 span))
(end (nth 2 span))
(obj (nth 3 span))
(type (and span (or (car span) 'host))))
(list (current-buffer)
(point-min) (point) (point-max)
major-mode
type beg end
(and obj (pm-debug-info obj))
(format "lppss:%s"
syntax-ppss-last))))
(defun pm-debug-info-on-current-span ()
(interactive)
(if (not polymode-mode)
(message "not in a polymode buffer")
(let ((span (pm-get-innermost-span)))
(apply 'message "min:%d pos:%d max:%d || (%s) type:%s span:%s-%s %s" (pm--debug-info span))
(move-overlay pm--inverse-video-overlay (nth 1 span) (nth 2 span) (current-buffer)))))
(defvar pm-debug-display-info-message nil)
(defun pm-debug-toogle-info-message ()
(interactive)
(setq pm-debug-display-info-message (not pm-debug-display-info-message)))
(defun pm-debug-toggle-fontification ()
(interactive)
(if pm-allow-fontification
(progn
(message "fontificaiton disabled")
(setq pm-allow-fontification nil))
(message "fontificaiton enabled")
(setq pm-allow-fontification t)))
(defun pm-debug-toggle-after-change ()
(interactive)
(if pm-allow-after-change-hook
(progn
(message "after-change disabled")
(setq pm-allow-after-change-hook nil))
(message "after-change enabled")
(setq pm-allow-after-change-hook t)))
(defun pm-debug-toggle-post-command ()
(interactive)
(if pm-allow-post-command-hook
(progn
(message "post-command disabled")
(setq pm-allow-post-command-hook nil))
(message "post-command enabled")
(setq pm-allow-post-command-hook t)))
(defun pm-debug-toggle-all ()
(interactive)
(if pm-allow-fontification
(progn
(message "fontificaiton, after-chnage and command-hook disabled")
(setq pm-allow-fontification nil
pm-allow-after-change-hook nil
pm-allow-post-command-hook nil))
(message "fontificaiton, after-change and command-hook enabled")
(setq pm-allow-fontification t
pm-allow-after-change-hook t
pm-allow-post-command-hook t)))
(defun pm-debug-fontify-current-span ()
(interactive)
(let ((span (pm-get-innermost-span))
(pm-allow-fontification t))
(poly-lock-fontify-region (nth 1 span) (nth 2 span))))
(defun pm-debug-fontify-last-font-lock-error ()
(interactive)
(let ((reg (pm--debug-get-last-fl-error))
(pm-allow-fontification t))
(if reg
(progn
;; (pm-debug-blink-region (car reg) (cdr reg) 2)
(poly-lock-fontify-region (car reg) (cdr reg)))
(message "No last font-lock errors found"))))
(defun pm--debug-get-last-fl-error ()
(with-current-buffer (messages-buffer)
(goto-char (point-max))
(when (re-search-backward "(poly-lock-fontify-region \\([0-9]+\\) \\([0-9]+\\))" nil t)
(cons (string-to-number (match-string 1))
(string-to-number (match-string 2))))))
(defun pm-debug-highlight-last-font-lock-error-region ()
(interactive)
(let ((reg (pm--debug-get-last-fl-error)))
(if reg
(progn
(goto-char (car reg))
(recenter)
(move-overlay pm--inverse-video-overlay (car reg) (cdr reg) (current-buffer))
(message "Region %s" reg))
(message "No last font-lock errors found"))))
(defvar pm-debug-relevant-functions-alist
'((polymode-initialization . (pm-initialize pm--mode-setup pm--common-setup
pm--get-chunkmode-buffer-create))
(poly-lock . (poly-lock-mode poly-lock-fontify-region
poly-lock-fontification-function
poly-lock-after-change
poly-lock-refontify
poly-lock--fontify-region-original))
(jit-loc . (jit-lock-refontify jit-lock-mode jit-lock-fontify-now))
(font-lock . (;; font-lock-mode turn-on-font-lock-if-desired
turn-on-font-lock
font-lock-after-change-function
font-lock-default-fontify-region
font-lock-fontify-syntactically-region
font-lock-extend-region-wholelines
font-lock-extend-region-multiline
font-lock-fontify-syntactic-keywords-region
font-lock-fontify-keywords-region
font-lock-unfontify-region
font-lock-fontify-region font-lock-flush
font-lock-fontify-buffer font-lock-ensure))
(methods . (pm-select-buffer pm-get-buffer-create))
(select . (pm-get-innermost-span pm-map-over-spans))
(insert . (self-insert-command))))
(defun pm-debug-trace-background-1 (fn)
(interactive (trace--read-args "Trace function in background: "))
(unless (symbolp fn)
(error "can trace symbols only"))
(unless (get fn 'cl--class)
(trace-function-background fn nil
'(lambda ()
(format " [buf:%s pos:%s type:%s (%f)]"
(current-buffer) (point)
(get-text-property (point) :pm-span-type)
(float-time))))))
(defun pm-debug-trace-relevant-functions (&optional group)
"GROUP is either a string or a list of functions to trace.
If string, it must b an entry in
`pm-debug-relevant-functions-alist'."
(interactive)
(require 'trace)
(if (and group (listp group))
(mapc #'pm-debug-trace-background-1 group)
(let* ((groups (append '("*ALL*") (mapcar #'car pm-debug-relevant-functions-alist)))
(group-name (or group (completing-read "Trace group: " groups nil t))))
(if (equal group-name "*ALL*")
(mapc (lambda (group)
(mapc #'pm-debug-trace-background-1
(assoc group pm-debug-relevant-functions-alist)))
(cdr groups))
(mapc #'pm-debug-trace-background-1
(assoc (intern group-name) pm-debug-relevant-functions-alist))))))
(defun pm-debug-trace-functions-by-regexp (regexp)
"Trace all functions whose name matched REGEXP."
(cl-loop for sym being the symbols
when (and (fboundp sym)
(not (eq sym 'pm-debug-trace-background-1)))
when (string-match regexp (symbol-name sym))
do (pm-debug-trace-background-1 sym)))
(defvar pm-debug-relevant-variables '(fontification-functions
font-lock-flush-function
font-lock-ensure-function
font-lock-fontify-region-function
font-lock-fontify-buffer-function
font-lock-unfontify-region-function
font-lock-unfontify-buffer-function
post-command-hook
indent-line-function))
(defun pm-debug-print-relevant-variables ()
(interactive)
(let ((buff (get-buffer-create "*polymode-vars*"))
(vars (mapcar (lambda (v) (cons v (buffer-local-value v (current-buffer))))
pm-debug-relevant-variables))
(cbuff (current-buffer)))
(require 'pp)
(with-current-buffer buff
(goto-char (point-max))
(insert "===============================================================\n")
(insert (format "relevant vars in buffer: %s\n" cbuff))
(insert (pp-to-string vars))
(toggle-truncate-lines -1))
(display-buffer buff)))
(defun pm-debug-untrace-relevant-functions ()
(interactive)
(require 'trace)
(let* ((groups (append `("*ALL*") (mapcar #'car pm-debug-relevant-functions-alist)))
(group-name (completing-read "Trace group: " groups nil t)))
(if (equal group-name "*ALL*")
(mapc (lambda (group)
(mapc #'untrace-function (assoc group pm-debug-relevant-functions-alist)))
(cdr groups))
(mapc #'untrace-function (assoc groups pm-debug-relevant-functions-alist)))))
(defun pm-debug-blink-region (start end &optional delay)
(move-overlay pm--inverse-video-overlay start end (current-buffer))
(run-with-timer (or delay 0.4) nil (lambda () (delete-overlay pm--inverse-video-overlay))))
(defun pm-debug-map-over-spans-and-highlight ()
(interactive)
(pm-map-over-spans (lambda ()
(let ((start (nth 1 *span*))
(end (nth 2 *span*)))
(pm-debug-blink-region start end)
(sit-for 1)))
(point-min) (point-max) nil nil t))
(defun pm--highlight-span (&optional hd-matcher tl-matcher)
(interactive)
(let* ((hd-matcher (or hd-matcher (oref pm/chunkmode :head-reg)))
(tl-matcher (or tl-matcher (oref pm/chunkmode :tail-reg)))
(span (pm--span-at-point hd-matcher tl-matcher)))
(pm-debug-blink-region (nth 1 span) (nth 2 span))
(message "span: %s" span)))
(defun pm-debug-run-over-check ()
(interactive)
(goto-char (point-min))
(let ((start (current-time))
(count 1))
(pm-switch-to-buffer)
(while (< (point) (point-max))
(setq count (1+ count))
(forward-char)
(pm-switch-to-buffer))
(let ((elapsed (float-time (time-subtract (current-time) start))))
(message "elapsed: %s per-char: %s" elapsed (/ elapsed count)))))
(defun pm-dbg (msg &rest args)
(let ((cbuf (current-buffer))
(cpos (point)))
(with-current-buffer (get-buffer-create "*pm-dbg*")
(save-excursion
(goto-char (point-max))
(insert "\n")
(insert (apply 'format (concat "%f [%s at %d]: " msg)
(float-time) cbuf cpos args))))))
(provide 'polymode-debug)

Binary file not shown.

View File

@@ -0,0 +1,392 @@
(require 'polymode-core)
(require 'polymode-classes)
(defgroup polymode-export nil
"Polymode Exporters"
:group 'polymode)
(defcustom polymode-exporter-output-file-format "%s[exported]"
"Format of the exported files.
%s is substituted with the current file name sans extension."
:group 'polymode-export
:type 'string)
(defclass pm-exporter (pm-root)
((from
:initarg :from
:initform '()
:type list
:custom list
:documentation
"Input exporter specifications.
This is an alist of elements of the form (id regexp doc
commmand) or (id . selector). ID is the unique identifier of
the spec. REGEXP is a regexp which, if matched on current
file name, implies that the current file can be exported
with this specification. DOC is a short help string shown
during interactive export. COMMAND is the exporter
command (string). It can contain the following format specs:
%i - input file (no dir)
%I - input file (full path)
%o - output file (no dir)
%O - output file (full path)
%b - output file (base name only)
%t - 4th element of the :to spec
When specification is of the form (id . selector), SELECTOR
is a function of variable arguments that accepts at least
one argument ACTION. ACTION is a symbol and can be one of
the following:
match - must return non-nil if this specification
applies to the file that current buffer is visiting,
or :nomatch if specification does not apply. This
selector can receive an optional file-name
argument. In that case the decision must be made
solely on that file and current buffer must be
ignored. This is useful for matching exporters to
weavers when exported file does not exist yet.
regexp - return a string which is used to match input
file name. If nil, `match' selector must return
non-nil value. This selector is ignored if `match'
returned non-nil.
doc - return documentation string
commmand - return a string with optional %i, %f,
etc. format specs as described above. It will be
passed to the processing :function.")
(to
:initarg :to
:initform '()
:type list
:custom list
:documentation
"Output specifications alist. Each element is either a list
of the form (id ext doc t-spec) or a cons (id . selector).
In the former case EXT is an extension of the output
file. DOC is a short documentation string. t-spec is a
string what is substituted instead of %t in :from spec
commmand. `t-spec' can be a list of one element '(command),
in which case the whole :from spec command is substituted
with command from %t-spec.
When specification is of the form (id . selector), SELECTOR
is a function of variable arguments that accepts at least
one argument ACTION. This function is called in a buffer
visiting input file. ACTION is a symbol and can one of the
following:
output-file - return an output file name or a list of file
names. Receives input-file as argument. If this
command returns nil, the output is built from input
file and value of 'output-ext command.
This selector can also return a function. This
function will be called in the callback or sentinel of
the weaving process after the weaving was
completed. This function should sniff the output of
the process for errors or file names. It must return a
file name, a list of file names or nil if no such
files have been detected.
ext - extension of output file. If nil and
`output' also returned nil, the exporter won't be able
to identify the output file and no automatic display
or preview will be available.
doc - return documentation string
command - return a string to be used instead of
the :from command. If nil, :from spec command is used.
t-spec - return a string to be substituted as %t :from
spec in :from command. If `command' selector returned
non-nil, this spec is ignored.")
(function
:initarg :function
:initform (lambda (command from to)
(error "Function not defined for this exporter"))
:type (or symbol function)
:documentation
"Function to process the commmand. Must take 3 arguments
COMMAND, FROM-ID and TO-ID. COMMAND is the 4th argument
of :from spec with all the formats substituted. FROM-ID is
the id of requested :from spec, TO-ID is the id of the :to
spec."))
"Root exporter class.")
(defclass pm-callback-exporter (pm-exporter)
((callback
:initarg :callback
:initform (lambda (&optional rest)
(error "No callback defined for this exporter."))
:type (or symbol function)
:documentation
"Callback function to be called by :function. There is no
default callback. Callback must return the output file
name."))
"Class to represent asynchronous exporters.")
(defclass pm-shell-exporter (pm-exporter)
((function
:initform 'pm-default-shell-export-function)
(sentinel
:initarg :sentinel
:initform 'pm-default-export-sentinel
:type (or symbol function)
:documentation
"Sentinel function to be called by :function when a shell
call is involved. Sentinel should return the output file
name.")
(quote
:initarg :quote
:initform nil
:type boolean
:documentation "Non-nil when file arguments must be quoted
with `shell-quote-argument'."))
"Class to represent exporters that call external processes.")
(defun pm-default-shell-export-function (command sentinel from to)
"Run exporting command interactively.
Run command in a buffer (in comint-shell-mode) so that it accepts
user interaction. This is a default function in all exporters
that call a shell command"
(pm--run-shell-command command sentinel "*polymode export*"
(concat "Exporting " from "-->" to " with command:\n\n "
command "\n\n")))
;;; METHODS
(defgeneric pm-export (exporter from to &optional ifile)
"Process IFILE with EXPORTER.")
(defmethod pm-export ((exporter pm-exporter) from to &optional ifile)
(pm--process-internal exporter from to ifile))
(defmethod pm-export ((exporter pm-callback-exporter) from to &optional ifile)
(let ((cb (pm--wrap-callback exporter :callback ifile)))
(pm--process-internal exporter from to ifile cb)))
(defmethod pm-export ((exporter pm-shell-exporter) from to &optional ifile)
(let ((cb (pm--wrap-callback exporter :sentinel ifile)))
(pm--process-internal exporter from to ifile cb (oref exporter :quote))))
;; UI
(defvar pm--exporter-hist nil)
(defvar pm--export:from-hist nil)
(defvar pm--export:from-last nil)
(defvar pm--export:to-hist nil)
(defvar pm--export:to-last nil)
(declare-function polymode-set-weaver "polymode-weave")
(declare-function pm-weave "polymode-weave")
(defun polymode-export (&optional from to)
"Export current file.
FROM and TO are the ids of the :from and :to slots of the current
exporter. If the current exporter hasn't been set yet, set the
exporter with `polymode-set-exporter'. You can always change the
exporter manually by invoking `polymode-set-exporter'.
When FROM or TO are missing they are determined automatically
from the current exporter's specifications and file's
extension. If no appropriate export specification has been found,
look into current weaver and try to match weaver's output to
exporters input extension. When such combination is possible,
settle on weaving first and exporting the weaved output. When
none of the above worked, ask the user for `from' and `to' specs.
When called interactively with C-u argument, ask for FROM and TO
interactively. See class `pm-exporter' for the complete
specification."
(interactive "P")
(cl-flet ((to-name.id (el) (let* ((ext (funcall (cdr el) 'ext))
(name (if ext
(format "%s (%s)" (funcall (cdr el) 'doc) ext)
(funcall (cdr el) 'doc))))
(cons name (car el))))
(from-name.id (el) (cons (funcall (cdr el) 'doc) (car el))))
(let* ((exporter (symbol-value (or (oref pm/polymode :exporter)
(polymode-set-exporter))))
(fname (file-name-nondirectory buffer-file-name))
(gprompt nil)
(case-fold-search t)
(from-opts (mapcar #'from-name.id (pm--selectors exporter :from)))
(from-id
(cond
;; A: guess from spec
((null from)
(or
;; 1. repeated export; don't ask
pm--export:from-last
;; 2. select :from entries which match to current file
(let ((matched (cl-loop for el in (pm--selectors exporter :from)
when (pm--selector-match (cdr el))
collect (from-name.id el))))
(when matched
(if (> (length matched) 1)
(cdr (pm--completing-read "Multiple `from' specs matched. Choose one: " matched))
(cdar matched))))
;; 3. guess from weaver and return a cons (weaver-id . exporter-id)
(let ((weaver (symbol-value (or (oref pm/polymode :weaver)
(progn
(setq gprompt "Choose `from' spec: ")
(polymode-set-weaver))))))
(when weaver
;; fixme: weaver was not yet ported to selectors
;; fixme: currently only first match is returned
(let ((pair (cl-loop for w in (oref weaver :from-to)
;; weaver input extension matches the filename
if (string-match-p (nth 1 w) fname)
return (cl-loop for el in (pm--selectors exporter :from)
;; input exporter extensnion matches weaver output extension
when (pm--selector-match (cdr el) (concat "dummy." (nth 2 w)))
return (cons (car w) (car el))))))
(when pair
(message "Matching weaver found. Weaving to '%s' first." (car pair))
pair))))
;; 4. nothing matched; ask
(let* ((prompt (or gprompt
(format "No `from' specs matched. Choose one: "
(file-name-nondirectory fname) (eieio-object-name-string exporter))))
(sel (pm--completing-read prompt from-opts nil t nil 'pm--export:from-hist)))
(cdr sel))))
;; B: C-u, force a :from spec
((equal from '(4))
(cdr (if (> (length from-opts) 1)
(pm--completing-read "Input type: " from-opts nil t nil 'pm--export:from-hist)
(car from-opts))))
;; C. string
((stringp from)
(if (assoc from (oref exporter :from))
from
(error "Cannot find `from' spec '%s' in %s exporter"
from (eieio-object-name exporter))))
;; D. error
(t (error "'from' argument must be nil, universal argument or a string"))))
(to-opts (mapcar #'to-name.id (pm--selectors exporter :to)))
(to-id
(cond
;; A. guess from spec
((null to)
(or
;; 1. repeated export; don't ask and use first entry in history
(unless (equal from '(4))
pm--export:to-last)
;; 2. First export or C-u
(cdr (pm--completing-read "Export to: " to-opts nil t nil 'pm--export:to-hist))))
;; B. string
((stringp to)
(if (assoc to (oref exporter :to))
to
(error "Cannot find output spec '%s' in %s exporter"
to (eieio-object-name exporter))))
;; C . Error
(t (error "'to' argument must be nil or a string")))))
(setq-local pm--export:from-last from-id)
(setq-local pm--export:to-last to-id)
(if (consp from-id)
;; run through weaver
(let ((pm--export-spec (cons (cdr from-id) to-id))
(pm--output-not-real t))
(pm-weave (symbol-value (oref pm/polymode :weaver)) (car from-id)))
(pm-export exporter from-id to-id)))))
(defun polymode-set-exporter ()
"Interactively set exporter for the current file."
(interactive)
(unless pm/polymode
(error "No pm/polymode object found. Not in polymode buffer?"))
(let* ((exporters (pm--abrev-names
(delete-dups (pm--oref-with-parents pm/polymode :exporters))
"pm-exporter/"))
(sel (pm--completing-read "Choose exporter: " exporters nil t nil 'pm--exporter-hist))
(out (intern (cdr sel))))
(setq-local pm--export:from-last nil)
(setq-local pm--export:to-last nil)
(oset pm/polymode :exporter out)
out))
(defmacro polymode-register-exporter (exporter defaultp &rest configs)
"Add EXPORTER to :exporters slot of all config objects in CONFIGS.
When DEFAULT? is non-nil, also make EXPORTER the default exporter
for each polymode in CONFIGS."
`(dolist (pm ',configs)
(object-add-to-list (symbol-value pm) :exporters ',exporter)
(when ,defaultp (oset (symbol-value pm) :exporter ',exporter))))
;;; GLOBAL EXPORTERS
(defcustom pm-exporter/pandoc
(pm-shell-exporter "pandoc"
:from
'(;; ("json" "\\.json\\'" "JSON native AST" "pandoc %i -f json -t %t -o %o")
("markdown" "\\.md\\'" "pandoc's markdown" "pandoc %i -f markdown -t %t -o %o")
("markdown_strict" "\\.md\\'" "original markdown" "pandoc %i -f markdown_strict -t %t -o %o")
("markdown_phpextra" "\\.md\\'" "PHP markdown" "pandoc %i -f markdown_phpextra -t %t -o %o")
("markdown_phpextra" "\\.md\\'" "github markdown" "pandoc %i -f markdown_phpextra -t %t -o %o")
("textile" "\\.textile\\'" "Textile" "pandoc %i -f textile -t %t -o %o")
("rst" "\\.rst\\'" "reStructuredText" "pandoc %i -f rst -t %t -o %o")
("html" "\\.x?html?\\'" "HTML" "pandoc %i -f html -t %t -o %o")
("doocbook" "\\.xml\\'" "DocBook" "pandoc %i -f doocbook -t %t -o %o")
("mediawiki" "\\.wiki\\'" "MediaWiki" "pandoc %i -f mediawiki -t %t -o %o")
("latex" "\\.tex\\'" "LaTeX" "pandoc %i -f latex -t %t -o %o")
)
:to
'(;; ("json" "json" "JSON version of native AST" "json")
("plain" "txt" "plain text" "plain")
("markdown" "md" "pandoc's extended markdown" "markdown")
("markdown_strict" "md" "original markdown" "markdown_strict")
("markdown_phpextra" "md" "PHP extended markdown" "markdown_phpextra")
("markdown_github" "md" "github extended markdown" "markdown_github")
("rst" "rst" "reStructuredText" "rst")
("html" "html" "XHTML 1" "html")
("html5" "html" "HTML 5" "html5")
("latex" "tex" "LaTeX" "latex")
("beamer" "tex" "LaTeX beamer" "beamer")
("context" "tex" "ConTeXt" "context")
("man" "man" "groff man" "man")
("mediawiki" "wiki" "MediaWiki markup" "mediawiki")
("textile" "textile" "Textile" "textile")
("org" "org" "Emacs Org-Mode" "org")
("texinfo" "info" "GNU Texinfo" "texinfo")
("docbook" "xml" "DocBook XML" "docbook")
("opendocument" "xml" "OpenDocument XML" "opendocument")
("odt" "odt" "OpenOffice text document" "odt")
("docx" "docx" "Word docx" "docx")
("epub" "epub" "EPUB book" "epub")
("epub3" "epub" "EPUB v3" "epub3")
("fb2" "fb" "FictionBook2 e-book" "fb2")
("asciidoc" "txt" "AsciiDoc" "asciidoc")
("slidy" "html" "Slidy HTML slide show" "slidy")
("slideous" "html" "Slideous HTML slide show" "slideous")
("dzslides" "html" "HTML5 slide show" "dzslides")
("s5" "html" "S5 HTML slide show" "s5")
("rtf" "rtf" "rich text format" "rtf"))
:function 'pm-default-shell-export-function
:sentinel 'pm-default-export-sentinel)
"Pandoc exporter"
:group 'polymode-export
:type 'object)
(provide 'polymode-export)

Binary file not shown.

View File

@@ -0,0 +1,872 @@
(require 'polymode-core)
(require 'poly-lock)
;;; Initialization
(defgeneric pm-initialize (config)
"Initialize current buffer with CONFIG.")
(defmethod pm-initialize ((config pm-polymode))
;; fixme: (VS[06-03-2016]: probably not anymore) reinstalation leads to
;; infloop of poly-lock--fontify-region-original and others ... On startup with local
;; auto vars emacs reinstals the mode twice .. waf? Temporary fix: don't
;; install twice
(unless pm/polymode
(let ((chunkmode (clone (symbol-value (oref config :hostmode)))))
(let ((pm-initialization-in-progress t)
;; Set if nil! This allows unspecified host chunkmodes to be used in
;; minor modes.
(host-mode (or (oref chunkmode :mode)
(oset chunkmode :mode major-mode))))
(pm--mode-setup host-mode)
;; maybe: fixme: inconsistencies?
;; 1) Not calling config's :minor-mode (polymode function). But polymode
;; function calls pm-initialize, so it's probably ok.
(oset chunkmode -buffer (current-buffer))
(oset config -hostmode chunkmode)
(setq pm/polymode config
pm/chunkmode chunkmode
pm/type 'host)
(pm--common-setup)
(add-hook 'flyspell-incorrect-hook
'pm--flyspel-dont-highlight-in-chunkmodes nil t))
(pm--run-init-hooks config 'polymode-init-host-hook)
(pm--run-init-hooks chunkmode))))
(defmethod pm-initialize ((config pm-polymode-one))
(let ((pm-initialization-in-progress t))
(call-next-method))
(eval `(oset config -innermodes
(list (clone ,(oref config :innermode)))))
(pm--run-init-hooks config 'polymode-init-host-hook))
(defmethod pm-initialize ((config pm-polymode-multi))
(let ((pm-initialization-in-progress))
(call-next-method))
(oset config -innermodes
(mapcar (lambda (sub-name)
(clone (symbol-value sub-name)))
(oref config :innermodes)))
(pm--run-init-hooks config 'polymode-init-host-hook))
(defmethod pm-initialize ((chunkmode pm-chunkmode) &optional type mode)
;; run in chunkmode indirect buffer
(setq mode (or mode (pm--get-chunkmode-mode chunkmode type)))
(let ((pm-initialization-in-progress t)
(new-name (generate-new-buffer-name
(format "%s[%s]" (buffer-name (pm-base-buffer))
(replace-regexp-in-string "-mode" "" (symbol-name mode))))))
(rename-buffer new-name)
(pm--mode-setup (pm--get-existent-mode mode))
(pm--move-vars '(pm/polymode buffer-file-coding-system) (pm-base-buffer))
(setq pm/chunkmode chunkmode
pm/type type)
(funcall (oref pm/polymode :minor-mode))
(vc-find-file-hook)
(pm--common-setup)
(pm--run-init-hooks chunkmode 'polymode-init-inner-hook)))
(defun pm--mode-setup (mode &optional buffer)
;; General major-mode install. Should work for both indirect and base buffers.
;; PM objects are not yet initialized (pm/polymode, pm/chunkmode, pm/type)
(with-current-buffer (or buffer (current-buffer))
;; don't re-install if already there; polymodes can be used as minor modes.
(unless (eq major-mode mode)
(let ((polymode-mode t) ;major-modes might check this
;; (font-lock-fontified t)
;; Modes often call font-lock functions directly. We prevent that.
(font-lock-function 'ignore)
(font-lock-flush-function 'ignore)
(font-lock-fontify-buffer-function 'ignore)
;; Mode functions can do arbitrary things. We inhibt all PM hooks
;; because PM objects have not been setup yet.
(pm-allow-after-change-hook nil)
(pm-allow-fontification nil))
(condition-case-unless-debug err
(funcall mode)
(error (message "Polymode error (pm--mode-setup '%s): %s" mode (error-message-string err))))))
(setq polymode-mode t)
(current-buffer)))
(defun pm--common-setup (&optional buffer)
;; General buffer setup. Should work for indirect and base buffers. Assumes
;; that the buffer was fully prepared and objects like pm/polymode and
;; pm/chunkmode have been initialised. Return the BUFFER.
(with-current-buffer (or buffer (current-buffer))
;; INDENTATION
(when (and indent-line-function ; not that it should ever be nil...
(oref pm/chunkmode :protect-indent-line))
(setq pm--indent-line-function-original indent-line-function)
(setq-local indent-line-function 'pm-indent-line-dispatcher))
;; FONT LOCK
(setq-local font-lock-function 'poly-lock-mode)
(font-lock-mode t)
;; SYNTAX
;; We are executing `syntax-propertize' narrowed to span as per advice in
;; (polymode-compat.el)
(pm-around-advice syntax-begin-function 'pm-override-output-position) ; obsolete as of 25.1
(pm-around-advice syntax-propertize-extend-region-functions 'pm-override-output-cons)
;; flush ppss in all buffers and hook checks
(add-hook 'before-change-functions 'polymode-before-change-setup t t)
;; REST
(add-hook 'kill-buffer-hook 'pm--kill-indirect-buffer t t)
(add-hook 'post-command-hook 'polymode-post-command-select-buffer nil t)
(object-add-to-list pm/polymode '-buffers (current-buffer))
(current-buffer)))
(defun pm--run-init-hooks (object &optional emacs-hook)
(unless pm-initialization-in-progress
(when emacs-hook
(run-hooks emacs-hook))
(pm--run-hooks object :init-functions)))
(defun pm--run-hooks (object slot &rest args)
"Run hooks from SLOT of OBJECT and its parent instances.
Parents' hooks are run first."
(let ((inst object)
funs)
;; run hooks, parents first
(while inst
(setq funs (append (and (slot-boundp inst slot) ; don't cascade
(eieio-oref inst slot))
funs)
inst (and (slot-boundp inst :parent-instance)
(oref inst :parent-instance))))
(if args
(apply 'run-hook-with-args 'funs args)
(run-hooks 'funs))))
(defvar-local pm--killed-once nil)
(defun pm--kill-indirect-buffer ()
;; find-alternate-file breaks (https://github.com/vspinu/polymode/issues/79)
(let ((base (buffer-base-buffer)))
(when (and base (buffer-live-p base))
;; 'base' is non-nil in indirect buffers only
(set-buffer-modified-p nil)
(unless (buffer-local-value 'pm--killed-once base)
(with-current-buffer base
(setq pm--killed-once t))
(kill-buffer base)))))
(defgeneric pm-get-buffer-create (chunkmode &optional type)
"Get the indirect buffer associated with SUBMODE and
SPAN-TYPE. Should return nil if buffer has not yet been
installed. Also see `pm-get-span'.")
(defmethod pm-get-buffer-create ((chunkmode pm-chunkmode) &optional type)
(let ((buff (oref chunkmode -buffer)))
(or (and (buffer-live-p buff) buff)
(oset chunkmode -buffer
(pm--get-chunkmode-buffer-create chunkmode type)))))
(defmethod pm-get-buffer-create ((chunkmode pm-hbtchunkmode) &optional type)
(let ((buff (cond ((eq 'body type) (oref chunkmode -buffer))
((eq 'head type) (oref chunkmode -head-buffer))
((eq 'tail type) (oref chunkmode -tail-buffer))
(t (error "Don't know how to select buffer of type '%s' for chunkmode '%s' of class '%s'"
type (eieio-object-name chunkmode) (class-of chunkmode))))))
(if (buffer-live-p buff)
buff
(pm--set-chunkmode-buffer chunkmode type
(pm--get-chunkmode-buffer-create chunkmode type)))))
(defun pm--get-chunkmode-buffer-create (chunkmode type)
(let ((mode (pm--get-existent-mode
(pm--get-chunkmode-mode chunkmode type))))
(or
;; 1. look through existent buffer list
(loop for bf in (oref pm/polymode -buffers)
when (and (buffer-live-p bf)
(eq mode (buffer-local-value 'major-mode bf)))
return bf)
;; 2. create new
(with-current-buffer (pm-base-buffer)
(let* ((new-name (generate-new-buffer-name (buffer-name)))
(new-buffer (make-indirect-buffer (current-buffer) new-name)))
(with-current-buffer new-buffer
(pm-initialize chunkmode type mode))
new-buffer)))))
(defun pm--get-chunkmode-mode (obj type)
(with-slots (mode head-mode tail-mode) obj
(cond ((or (eq type 'body)
(and (eq type 'head)
(eq head-mode 'body))
(and (eq type 'tail)
(or (eq tail-mode 'body)
(and (or (null tail-mode)
(eq tail-mode 'head))
(eq head-mode 'body)))))
(oref obj :mode))
((or (and (eq type 'head)
(eq head-mode 'host))
(and (eq type 'tail)
(or (eq tail-mode 'host)
(and (or (null tail-mode)
(eq tail-mode 'head))
(eq head-mode 'host)))))
(oref (oref pm/polymode -hostmode) :mode))
((eq type 'head)
(oref obj :head-mode))
((eq type 'tail)
(if (or (null tail-mode)
(eq tail-mode 'head))
(oref obj :head-mode)
(oref obj :tail-mode)))
(t (error "type must be one of 'head 'tail 'body")))))
(defun pm--set-chunkmode-buffer (obj type buff)
"Assign BUFF to OBJ's slot(s) corresponding to TYPE."
(with-slots (-buffer head-mode -head-buffer tail-mode -tail-buffer) obj
(pcase (list type head-mode tail-mode)
(`(body body ,(or `nil `body))
(setq -buffer buff
-head-buffer buff
-tail-buffer buff))
(`(body ,_ body)
(setq -buffer buff
-tail-buffer buff))
(`(body ,_ ,_ )
(setq -buffer buff))
(`(head ,_ ,(or `nil `head))
(setq -head-buffer buff
-tail-buffer buff))
(`(head ,_ ,_)
(setq -head-buffer buff))
(`(tail ,_ ,(or `nil `head))
(setq -tail-buffer buff
-head-buffer buff))
(`(tail ,_ ,_)
(setq -tail-buffer buff))
(_ (error "type must be one of 'body, 'head or 'tail")))))
(defvar pm-move-vars-from-base '(buffer-file-name)
"Variables transferred from base buffer on buffer switch.")
(defvar pm-move-vars-from-old-buffer
'(buffer-invisibility-spec
selective-display overwrite-mode
;; truncation and word-wrap
truncate-lines word-wrap
line-move-visual truncate-partial-width-windows)
"Variables transferred from old buffer on buffer switch.")
(defgeneric pm-select-buffer (chunkmode span)
"Ask SUBMODE to select (make current) its indirect buffer
corresponding to the type of the SPAN returned by
`pm-get-span'.")
(defmethod pm-select-buffer ((chunkmode pm-chunkmode) span)
"Select the buffer associated with CHUNKMODE.
Install a new indirect buffer if it is not already installed. For
this method to work correctly, SUBMODE's class should define
`pm-get-buffer-create' methods."
(let* ((type (car span))
(buff (pm-get-buffer-create chunkmode type)))
(pm--select-existent-buffer buff)))
;; extracted for debugging purpose
(defun pm--select-existent-buffer (buffer)
(when (and (not (eq buffer (current-buffer)))
(buffer-live-p buffer))
(pm--move-vars pm-move-vars-from-base (pm-base-buffer) buffer)
(if pm--select-buffer-visibly
;; slow, visual selection
(pm--select-existent-buffer-visibly buffer)
;; fast set-buffer
(set-buffer buffer))))
;; extracted for debugging purpose
(defun pm--select-existent-buffer-visibly (new-buffer)
(let ((old-buffer (current-buffer))
(point (point))
(window-start (window-start))
(visible (pos-visible-in-window-p))
(vlm visual-line-mode)
(ractive (region-active-p))
;; text-scale-mode
(scale (and (boundp 'text-scale-mode) text-scale-mode))
(scale-amount (and (boundp 'text-scale-mode-amount) text-scale-mode-amount))
(hl-line (and (boundp 'hl-line-mode) hl-line-mode))
(mkt (mark t))
(bro buffer-read-only))
(when hl-line
(hl-line-mode -1))
(pm--move-vars pm-move-vars-from-old-buffer old-buffer new-buffer)
(pm--move-overlays old-buffer new-buffer)
(switch-to-buffer new-buffer)
(bury-buffer-internal old-buffer)
(unless (eq bro buffer-read-only)
(read-only-mode (if bro 1 -1)))
(pm--adjust-visual-line-mode vlm)
(when (and (boundp 'text-scale-mode-amount)
(not (and (eq scale text-scale-mode)
(= scale-amount text-scale-mode-amount))))
(if scale
(text-scale-set scale-amount)
(text-scale-set 0)))
;; fixme: what is the right way to do this ... activate-mark-hook?
(if (not ractive)
(deactivate-mark)
(set-mark mkt)
(activate-mark))
;; avoid display jumps
(goto-char point)
(when visible
(set-window-start (get-buffer-window buffer t) window-start))
(when hl-line
(hl-line-mode 1))
(run-hook-with-args 'polymode-switch-buffer-hook old-buffer new-buffer)
(pm--run-hooks pm/polymode :switch-buffer-functions old-buffer new-buffer)
(pm--run-hooks pm/chunkmode :switch-buffer-functions old-buffer new-buffer)))
(defun pm--move-overlays (from-buffer to-buffer)
(with-current-buffer from-buffer
(mapc (lambda (o)
(unless (eq 'linum-str (car (overlay-properties o)))
(move-overlay o (overlay-start o) (overlay-end o) to-buffer)))
(overlays-in 1 (1+ (buffer-size))))))
(defun pm--move-vars (vars from-buffer &optional to-buffer)
(let ((to-buffer (or to-buffer (current-buffer))))
(unless (eq to-buffer from-buffer)
(with-current-buffer to-buffer
(dolist (var vars)
(and (boundp var)
(set var (buffer-local-value var from-buffer))))))))
(defun pm--adjust-visual-line-mode (vlm)
(unless (eq visual-line-mode vlm)
(if (null vlm)
(visual-line-mode -1)
(visual-line-mode 1))))
(defmethod pm-select-buffer ((config pm-polymode-multi-auto) &optional span)
;; :fixme: pm-get-span on multi configs returns config as last object of
;; span. This unnatural and confusing. Same problem with pm-indent-line
(pm-select-buffer (pm--get-multi-chunk config span) span))
(defun pm--get-multi-chunk (config span)
;; fixme: cache somehow?
(if (null (car span))
(oref config -hostmode)
(let ((type (car span))
(proto (symbol-value (oref config :auto-innermode))))
(save-excursion
(goto-char (cadr span))
(unless (eq type 'head)
(let ((matcher (oref proto :head-reg)))
(if (functionp matcher)
(goto-char (car (funcall matcher -1)))
(re-search-backward matcher nil 'noerr))))
(let* ((str (or
;; a. try regexp matcher
(and (oref proto :retriever-regexp)
(re-search-forward (oref proto :retriever-regexp) nil t)
(match-string-no-properties (oref proto :retriever-num)))
;; b. otherwise function (fixme: these should be merged)
(and (oref proto :retriever-function)
(funcall (oref proto :retriever-function)))))
(mode (pm--get-mode-symbol-from-name str 'no-fallback)))
(if mode
;; Inferred body MODE serves as ID; this not need be the
;; case in the future and a generic id getter might replace
;; it. Currently head/tail/body indirect buffers are shared
;; across chunkmodes. This currently works ok. A more
;; general approach would be to track head/tails/body with
;; associated chunks. Then for example r hbt-chunk and elisp
;; hbt-chunk will not share head/tail buffers. There could
;; be even two r hbt-chunks with providing different
;; functionality and thus not even sharing body buffer.
(let ((name (concat (object-name-string proto) ":" (symbol-name mode))))
(or
;; a. loop through installed inner modes
(loop for obj in (oref config -auto-innermodes)
when (equal name (object-name-string obj))
return obj)
;; b. create new
(let ((innermode (clone proto name :mode mode)))
(object-add-to-list config '-auto-innermodes innermode)
innermode)))
;; else, use hostmode
(oref pm/polymode -hostmode)))))))
;;; SPAN MANIPULATION
(defgeneric pm-get-span (chunkmode &optional pos)
"Ask the CHUNKMODE for the span at point.
Return a list of three elements (TYPE BEG END OBJECT) where TYPE
is a symbol representing the type of the span surrounding
POS (head, tail, body). BEG and END are the coordinates of the
span. OBJECT is a sutable object which is 'responsable' for this
span. This is an object that could be dispached upon with
`pm-select-buffer', .. (fixme: complete this list).
Should return nil if there is no SUBMODE specific span around POS.")
(defmethod pm-get-span (chunkmode &optional pos)
"Return nil.
Base mode usually do not compute the span."
(unless chunkmode
(error "Dispatching `pm-get-span' on a nil object"))
nil)
(defmethod pm-get-span ((config pm-polymode) &optional pos)
"Apply pm-get-span on every element of chunkmodes slot of config object.
Return a cons (chunkmode . span), for which START is closest to
POS (and before it); i.e. the innermost span. POS defaults to
point."
(save-restriction
(widen)
;; fixme: host should be last, to take advantage of the chunkmodes computation
(let* ((smodes (cons (oref config -hostmode)
(oref config -innermodes)))
(start (point-min))
(end (point-max))
(pos (or pos (point)))
(span (list nil start end nil))
val)
(dolist (sm smodes)
(setq val (pm-get-span sm pos))
(when (and val
(or (> (nth 1 val) start)
(< (nth 2 val) end)))
(if (or (car val)
(null span))
(setq span val
start (nth 1 val)
end (nth 2 val))
;; nil car means outer chunkmode (usually host). And it can be an
;; intersection of spans returned by 2 different neighbour inner
;; chunkmodes. See rapport mode for an example
(setq start (max (nth 1 val)
(nth 1 span))
end (min (nth 2 val)
(nth 2 span)))
(setcar (cdr span) start)
(setcar (cddr span) end))))
(unless (and (<= start end) (<= pos end) (>= pos start))
(error "Bad polymode selection: span:%s pos:%s"
(list start end) pos))
(when (null (car span)) ; chunkmodes can compute the host span by returning nil
(setcar (last span) (oref config -hostmode)))
span)))
;; No need for this one so far. Basic method iterates through -innermodes
;; anyhow.
;; (defmethod pm-get-span ((config pm-polymode-multi) &optional pos))
(defmethod pm-get-span ((config pm-polymode-multi-auto) &optional pos)
(let ((span-other (call-next-method))
(proto (symbol-value (oref config :auto-innermode))))
(if (oref proto :head-reg)
(let ((span (pm--span-at-point (oref proto :head-reg)
(oref proto :tail-reg)
pos)))
(if (and span-other
(or (> (nth 1 span-other) (nth 1 span))
(< (nth 2 span-other) (nth 2 span))))
;; treat intersections with the host mode
(if (car span-other)
span-other ;not host
;; here, car span should better be nil; no explicit check
(setcar (cdr span-other) (max (nth 1 span-other) (nth 1 span)))
(setcar (cddr span-other) (min (nth 2 span-other) (nth 2 span)))
span-other)
(append span (list config)))) ;fixme: this returns config as last object
span-other)))
(defmethod pm-get-span ((chunkmode pm-hbtchunkmode) &optional pos)
"Return a list of the form (TYPE POS-START POS-END SELF).
TYPE can be 'body, 'head or 'tail. SELF is just a chunkmode object
in this case."
(with-slots (head-reg tail-reg head-mode tail-mode) chunkmode
(let* ((span (pm--span-at-point head-reg tail-reg pos))
(type (car span)))
(when (or (and (eq type 'head) (eq head-mode 'host))
(and (eq type 'tail) (or (eq tail-mode 'host)
(and (null tail-mode)
(eq head-mode 'host)))))
(setcar span nil))
(append span (list chunkmode)))))
(defmacro pm-create-indented-block-matchers (name regex)
"Defines 2 functions, each return a list of the start and end points of the
HEAD and TAIL portions of an indented block of interest, via some regex.
You can then use these functions in the defcustom pm-inner modes.
e.g.
(pm-create-indented-block-matchers 'slim-coffee' \"^[^ ]*\\(.*:? *coffee: *\\)$\")
creates the functions
pm-slim-coffee-head-matcher
pm-slim-coffee-tail-matcher
In the example below,
The head matcher will match against 'coffee:', returning the positions of the
start and end of 'coffee:'
The tail matcher will return a list (n, n) of the final characters is the block.
|<----- Uses this indentation to define the left edge of the 'block'
|
|<--->| This region is higlighted by the :head-mode in the block-matchers
| |
| |<----- the head matcher uses this column as the end of the head
| |
----:-----:-------------- example file -----------------------------------------
1| : :
2| coffee:
3| myCoffeeCode()
4| moreCode ->
5| do things
6| :
7| This is no longer in the block
8| :
----------------:---------------------------------------------------------------
--->|<----- this region of 0 width is highlighted by the :tail-mode
the 'block' ends after this column on line 5
All the stuff after the -end- of the head and before the start of the tail is
sent to the new mode for syntax highlighting."
(let* ((head-name (intern (format "pm-%s-head-matcher" name)))
(tail-name (intern (format "pm-%s-tail-matcher" name))))
`(progn
(defun ,head-name (ahead)
(when (re-search-forward ,regex nil t ahead)
(cons (match-beginning 1) (match-end 1))))
(defun ,tail-name (ahead)
(save-excursion
;; (cons (point-max) (point-max)))))))
(goto-char (car (,head-name 1)))
(let* ((block-col (current-indentation))
(posn (catch 'break
(while (not (eobp))
(forward-line 1)
(when (and (<= (current-indentation) block-col)
(not (progn
(beginning-of-line)
(looking-at "^[[:space:]]*$"))))
(throw 'break (point-at-bol))))
(throw 'break (point-max)))))
(cons posn posn)))))))
(defun pm--default-matcher (reg ahead)
(if (< ahead 0)
(if (re-search-backward reg nil t)
(cons (match-beginning 0) (match-end 0)))
(if (re-search-forward reg nil t)
(cons (match-beginning 0) (match-end 0)))))
;; fixme: there should be a simpler way... check the code and document
(defun pm--span-at-point-fun-fun (hd-matcher tl-matcher)
(save-excursion
(let ((pos (point))
(posh (funcall hd-matcher -1)))
(if (null posh)
;; special first chunk
(let ((posh1 (progn (goto-char (point-min))
(funcall hd-matcher 1))))
(if (and posh1
(<= (car posh1) pos)
(< pos (cdr posh1)))
(list 'head (car posh1) (cdr posh1))
(list nil (point-min) (or (car posh1)
(point-max)))))
(let ((post (progn (goto-char (car posh))
(or (funcall tl-matcher 1)
(cons (point-max) (point-max))))))
(if (and (<= (cdr posh) pos)
(< pos (car post)))
(list 'body (cdr posh) (car post))
(if (and (<= (car post) pos)
(< pos (cdr post)))
(list 'tail (car post) (cdr post))
(if (< pos (cdr post))
;; might be in the head
(progn
(goto-char (car post))
(let ((posh1 (funcall hd-matcher -1)))
(if (and (<= (car posh1) pos)
(< pos (cdr posh1)))
(list 'head (car posh1) (cdr posh1))
(list nil (cdr posh) (car posh1))))) ;; posh is point min, fixme: not true anymore?
(goto-char (cdr post))
(let ((posh1 (or (funcall hd-matcher 1)
(cons (point-max) (point-max)))))
(if (and posh
(<= (car posh1) pos )
(< pos (cdr posh1)))
(list 'head (car posh1) (cdr posh1))
(list nil (cdr post) (car posh1))))))))))))
(defun pm--span-at-point-reg-reg (head-matcher tail-matcher)
;; Guaranteed to produce non-0 length spans. If no span has been found
;; (head-matcher didn't match) return (nil (point-min) (point-max)).
;; xxx1 relate to the first ascending search
;; xxx2 relate to the second descending search
(save-excursion
(let* ((pos (point))
(head1-beg (and (re-search-backward head-matcher nil t)
(match-beginning 0)))
(head1-end (and head1-beg (match-end 0))))
(if head1-end
;; we know that (>= pos head1-end)
;; -----------------------
;; host](head)[body](tail)[host](head)
(let* ((tail1-beg (and (goto-char head1-end)
(re-search-forward tail-matcher nil t)
(match-beginning 0)))
(tail1-end (and tail1-beg (match-end 0)))
(tail1-beg (or tail1-beg (point-max)))
(tail1-end (or tail1-end (point-max))))
(if (or (< pos tail1-end)
(= tail1-end (point-max)))
(if (<= pos tail1-beg)
;; ------
;; host](head)[body](tail)[host](head))
(list 'body head1-end tail1-beg)
;; -----
;; host](head](body](tail)[host](head)
(list 'tail tail1-beg tail1-end))
;; ------------
;; host](head](body](tail)[host](head)
(let* ((head2-beg (or (and (re-search-forward head-matcher nil t)
(match-beginning 0))
(point-max))))
(if (<= pos head2-beg)
;; ------
;; host](head](body](tail)[host](head)
(list nil tail1-end head2-beg)
;; ------
;; host](head](body](tail)[host](head)
(list 'head head2-beg (match-end 0))))))
;; -----------
;; host](head)[body](tail)[host
(let ((head2-beg (and (goto-char (point-min))
(re-search-forward head-matcher nil t)
(match-beginning 0))))
(if (null head2-beg)
;; no span found
(list nil (point-min) (point-max))
(if (<= pos head2-beg)
;; -----
;; host](head)[body](tail)[host
(list nil (point-min) head2-beg)
;; ------
;; host](head)[body](tail)[host
(list 'head head2-beg (match-end 0)))))))))
(defun pm--span-at-point (head-matcher tail-matcher &optional pos)
"Basic span detector with head/tail.
Either of HEAD-MATCHER and TAIL-MATCHER can be a regexp or a
function. When a function the matcher must accept one argument
that can take either values 1 (forwards search) or -1 (backward
search). This function must return either nil (no match) or
a (cons BEG END) representing the span of the head or tail
respectively. See `pm--default-matcher' for an example.
Return (type span-start span-end) where type is one of the
follwoing symbols:
nil - pos is between point-min and head-reg, or between tail-reg and point-max
body - pos is between head-reg and tail-reg (exclusively)
head - head span
tail - tail span"
;; ! start of the span is part of the span !
(save-restriction
(widen)
(goto-char (or pos (point)))
(cond ((and (stringp head-matcher)
(stringp tail-matcher))
(pm--span-at-point-reg-reg head-matcher tail-matcher))
((and (stringp head-matcher)
(functionp tail-matcher))
(pm--span-at-point-fun-fun
(lambda (ahead) (pm--default-matcher head-matcher ahead))
tail-matcher))
((and (functionp head-matcher)
(stringp tail-matcher))
(pm--span-at-point-fun-fun
head-matcher
(lambda (ahead) (pm--default-matcher tail-matcher ahead))))
((and (functionp head-matcher)
(functionp tail-matcher))
(pm--span-at-point-fun-fun head-matcher tail-matcher))
(t (error "head and tail matchers should be either regexp strings or functions")))))
;;; INDENT
(defun pm-indent-line-dispatcher ()
"Dispatch methods indent methods on current span."
(let ((span (pm-get-innermost-span))
(inhibit-read-only t))
(pm-indent-line (car (last span)) span)))
(defgeneric pm-indent-line (&optional chunkmode span)
"Indent current line.
Protect and call original indentation function associated with
the chunkmode.")
(defun pm--indent-line (span)
(let (point)
(save-current-buffer
(pm-set-buffer span)
(pm-with-narrowed-to-span span
(funcall pm--indent-line-function-original)
(setq point (point))))
(goto-char point)))
(defmethod pm-indent-line ((chunkmode pm-chunkmode) &optional span)
(pm--indent-line span))
(defmethod pm-indent-line ((chunkmode pm-hbtchunkmode) &optional span)
"Indent line in inner chunkmodes.
When point is at the beginning of head or tail, use parent chunk
to indent."
(let ((pos (point))
(span (or span (pm-get-innermost-span)))
delta)
(unwind-protect
(cond
;; 1. in head or tail (we assume head or tail fit in one line for now)
((or (eq 'head (car span))
(eq 'tail (car span)))
(goto-char (nth 1 span))
(setq delta (- pos (point)))
(when (not (bobp))
(let ((prev-span (pm-get-innermost-span (1- pos))))
(if (and (eq 'tail (car span))
(eq (point) (save-excursion (back-to-indentation) (point))))
;; if tail is first on the line, indent as head
(indent-to (pm--head-indent prev-span))
(pm--indent-line prev-span)))))
;; 2. body
(t
(back-to-indentation)
(if (> (nth 1 span) (point))
;; first body line in the same line with header (re-indent at indentation)
(pm-indent-line-dispatcher)
(setq delta (- pos (point)))
(pm--indent-line span)
(let ((fl-indent (pm--first-line-indent span)))
(if fl-indent
(when (bolp)
;; Not first line. Indent only when original indent is at
;; 0. Otherwise it's a continuation indentation and we assume
;; the original function did it correctly with respect to
;; previous lines.
(indent-to fl-indent))
;; First line. Indent with respect to header line.
(indent-to
(+ (- (point) (point-at-bol)) ;; non-0 if code in header line
(pm--head-indent span) ;; indent with respect to header line
(oref chunkmode :indent-offset))))))))
;; keep point on same characters
(when (and delta (> delta 0))
(goto-char (+ (point) delta))))))
(defun pm--first-line-indent (&optional span)
(save-excursion
(let ((pos (point)))
(goto-char (nth 1 (or span (pm-get-innermost-span))))
(goto-char (point-at-eol))
(skip-chars-forward " \t\n")
(let ((indent (- (point) (point-at-bol))))
(when (< (point-at-eol) pos)
indent)))))
(defun pm--head-indent (&optional span)
(save-excursion
(goto-char (nth 1 (or span (pm-get-innermost-span))))
(back-to-indentation)
(- (point) (point-at-bol))))
(defmethod pm-indent-line ((config pm-polymode-multi-auto) &optional span)
;; fixme: pm-polymode-multi-auto is not a chunk, pm-get-innermost-span should
;; not return it in the first place
;; (pm-set-buffer span)
;; (pm-indent-line pm/chunkmode span))
(pm-indent-line (pm--get-multi-chunk config span) span))
;;; FACES
(defgeneric pm-get-adjust-face (chunkmode &optional type))
(defmethod pm-get-adjust-face ((chunkmode pm-chunkmode) &optional type)
(oref chunkmode :adjust-face))
(defmethod pm-get-adjust-face ((chunkmode pm-hbtchunkmode) &optional type)
(setq type (or type pm/type))
(cond ((eq type 'head)
(oref chunkmode :head-adjust-face))
((eq type 'tail)
(if (eq 'head (oref pm/chunkmode :tail-adjust-face))
(oref pm/chunkmode :head-adjust-face)
(oref pm/chunkmode :tail-adjust-face)))
(t (oref pm/chunkmode :adjust-face))))
(defun pm--get-adjusted-background (prop)
;; if > lighten on dark backgroun. Oposite on light.
(color-lighten-name (face-background 'default)
(if (eq (frame-parameter nil 'background-mode) 'light)
(- prop) ;; darken
prop)))
(defun pm--adjust-chunk-face (beg end face)
;; propertize 'face of the region by adding chunk specific configuration
(interactive "r")
(when face
(with-current-buffer (current-buffer)
(let ((face (or (and (numberp face)
(list (cons 'background-color
(pm--get-adjusted-background face))))
face))
(pchange nil))
;; (while (not (eq pchange end))
;; (setq pchange (next-single-property-change beg 'face nil end))
;; (put-text-property beg pchange 'face
;; `(,face ,@(get-text-property beg 'face)))
;; (setq beg pchange))
(font-lock-prepend-text-property beg end 'face face)))))
(provide 'polymode-methods)

Binary file not shown.

View File

@@ -0,0 +1,6 @@
(define-package "polymode" "20170307.322" "Versatile multiple modes with extensive literate programming support"
'((emacs "24"))
:url "https://github.com/vitoshka/polymode")
;; Local Variables:
;; no-byte-compile: t
;; End:

View File

@@ -0,0 +1,3 @@
(defgroup polymode-tangle nil
"Polymode Tanglers"
:group 'polymode)

Binary file not shown.

View File

@@ -0,0 +1,252 @@
;; -*- lexical-binding: t -*-
(require 'polymode-core)
(require 'polymode-classes)
(defgroup polymode-weave nil
"Polymode Weavers"
:group 'polymode)
(defcustom polymode-weave-output-file-format "%s[woven]"
"Format of the weaved files.
%s is substituted with the current file name sans extension."
:group 'polymode-weave
:type 'string)
(defclass pm-weaver (pm-root)
((from-to
:initarg :from-to
:initform '()
:type list
:custom list
:documentation
"Input-output specifications. An alist with elements of the
form (id reg-from ext-to doc command) or (id . selector).
In both cases ID is the unique identifier of the spec. In
the former case REG-FROM is a regexp used to identify if
current file can be weaved with the spec. EXT-TO is the
extension of the output file. DOC is a short help string
used for interactive completion and messages. COMMAND is a
weaver specific specific command. It can contain the
following format specs:
%i - input file (no dir)
%I - input file (full path)
%o - output file (no dir)
%O - output file (full path)
%b - output file (base name only)
%t - 4th element of the :to spec
When specification is of the form (id . selector), SELECTOR
is a function of variable arguments that accepts at least
one argument ACTION. This function is called in a buffer
visiting input file. ACTION is a symbol and can one of the
following:
match - must return non-nil if this specification
applies to the file that current buffer is visiting,
or :nomatch if specification does not apply.
regexp - return a string which is used to match input
file name. If nil, `match' selector must return
non-nil value. This selector is ignored if `match'
returned non-nil.
output-file - return an output file name or a list of
file names. Receives input-file as argument. If this
command returns nil, the output is built from the
input file name and value of 'output-ext command.
This selector can also return a function. This
function will be called in the callback or sentinel of
the weaving process after the weaving was
completed. This function should sniff the output of
the process for errors or file names. It must return a
file name, a list of file names or nil if no such
files have been detected.
ext - extension of output file. If nil and
`output' also returned nil, the exporter won't be able
to identify the output file and no automatic display
or preview will be available.
doc - return documentation string
command - return a string to be used instead of
the :from command. If nil, :from spec command is used.")
(function
:initarg :function
:initform (lambda (command id)
(error "No weaving function declared for this weaver"))
:type (or symbol function)
:documentation
"Function to perform the weaving. Must take 2 arguments
COMMAND and ID. COMMAND is the 5th argument of :from-to spec
with all the formats substituted. ID is the id the
corresponding element in :from-to spec.
If this function returns a filename that file will be
displayed to the user."))
"Root weaver class.")
(defclass pm-callback-weaver (pm-weaver)
((callback
:initarg :callback
:initform (lambda (&optional rest)
(error "No callback defined for this weaver."))
:type (or symbol function)
:documentation
"Callback function to be called by :function. There is no
default callback. Callbacks must return the output file."))
"Class to represent weavers that call processes spanned by
Emacs.")
(defclass pm-shell-weaver (pm-weaver)
((function
:initform 'pm-default-shell-weave-function)
(sentinel
:initarg :sentinel
:initform 'pm-default-shell-weave-sentinel
:type (or symbol function)
:documentation
"Sentinel function to be called by :function when a shell
call is involved. Sentinel must return the output file
name.")
(quote
:initarg :quote
:initform nil
:type boolean
:documentation "Non-nil when file arguments must be quoted
with `shell-quote-argument'."))
"Class for weavers that call external processes.")
(defun pm-default-shell-weave-function (command sentinel from-to-id &rest args)
"Run weaving command interactively.
Run command in a buffer (in comint-shell-mode) so that it accepts
user interaction. This is a default function in all weavers
that call a shell command"
(pm--run-shell-command command sentinel "*polymode weave*"
(concat "weaving " from-to-id " with command:\n\n "
command "\n\n")))
;;; METHODS
(declare-function pm-export "polymode-export")
(defgeneric pm-weave (weaver from-to-id &optional ifile)
"Weave current FILE with WEAVER.
WEAVER is an object of class `pm-weaver'. EXPORT is a list of the
form (FROM TO) suitable to be passed to `polymode-export'. If
EXPORT is provided, corresponding exporter's (from to)
specification will be called.")
(defmethod pm-weave ((weaver pm-weaver) from-to-id &optional ifile)
(pm--weave-internal weaver from-to-id ifile))
(defmethod pm-weave ((weaver pm-callback-weaver) fromto-id &optional ifile)
(let ((cb (pm--wrap-callback weaver :callback ifile))
;; with transitory output, callback might not run
(pm--export-spec (and pm--output-not-real pm--export-spec)))
(pm--process-internal weaver fromto-id nil ifile cb)))
(defmethod pm-weave ((weaver pm-shell-weaver) fromto-id &optional ifile)
(let ((cb (pm--wrap-callback weaver :sentinel ifile))
;; with transitory output, callback might not run
(pm--export-spec (and pm--output-not-real pm--export-spec)))
(pm--process-internal weaver fromto-id nil ifile cb (oref weaver :quote))))
;; UI
(defvar pm--weaver-hist nil)
(defvar pm--weave:fromto-hist nil)
(defvar pm--weave:fromto-last nil)
(defun polymode-weave (&optional from-to)
"Weave current file.
First time this command is called in a buffer the user is asked
for the weaver to use from a list of known weavers.
FROM-TO is the id of the specification declared in :from-to slot
of the current weaver. If the weaver hasn't been set yet, set the
weaver with `polymode-set-weaver'. You can always change the
weaver manually by invoking `polymode-set-weaver'.
If `from-to' dismissing detect automatically based on current
weaver :from-to specifications. If this detection is ambiguous
ask the user.
When `from-to' is universal argument ask user for specification
for the specification. See also `pm-weaveer' for the complete
specification."
(interactive "P")
(cl-flet ((name.id (el) (cons (funcall (cdr el) 'doc) (car el))))
(let* ((weaver (symbol-value (or (oref pm/polymode :weaver)
(polymode-set-weaver))))
(fname (file-name-nondirectory buffer-file-name))
(case-fold-search t)
(opts (mapcar #'name.id (pm--selectors weaver :from-to)))
(ft-id
(cond
;; A. guess from-to spec
((null from-to)
(or
;; 1. repeated weaving; don't ask
pm--weave:fromto-last
;; 2. select :from entries which match to current file
(let ((matched (cl-loop for el in (pm--selectors weaver :from-to)
when (pm--selector-match (cdr el))
collect (name.id el))))
(when matched
(if (> (length matched) 1)
(cdr (pm--completing-read "Multiple `from-to' specs matched. Choose one: " matched))
(cdar matched))))
;; 3. nothing matched, ask
(let* ((prompt (format "No `from-to' specs matched. Choose one: "
(file-name-extension fname) (eieio-object-name weaver)))
(sel (pm--completing-read prompt opts nil t nil 'pm--weave:fromto-hist)))
(cdr sel))))
;; B. C-u, force a :from-to spec
((equal from-to '(4))
(cdr (if (> (length opts) 1)
(pm--completing-read "Weaver type: " opts nil t nil 'pm--weave:fromto-hist)
(car opts))))
;; C. string
((stringp from-to)
(if (assoc from-to (oref weaver :from-to))
from-to
(error "Cannot find `from-to' spec '%s' in %s weaver"
from-to (eieio-object-name weaver))))
(t (error "'from-to' argument must be nil, universal argument or a string")))))
(setq-local pm--weave:fromto-last ft-id)
(pm-weave weaver ft-id))))
(defmacro polymode-register-weaver (weaver defaultp &rest configs)
"Add WEAVER to :weavers slot of all config objects in CONFIGS.
When DEFAULT? is non-nil, also make weaver the default WEAVER for
each polymode in CONFIGS."
`(dolist (pm ',configs)
(object-add-to-list (symbol-value pm) :weavers ',weaver)
(when ,defaultp (oset (symbol-value pm) :weaver ',weaver))))
(defun polymode-set-weaver ()
(interactive)
(unless pm/polymode
(error "No pm/polymode object found. Not in polymode buffer?"))
(let* ((weavers (pm--abrev-names
(delete-dups (pm--oref-with-parents pm/polymode :weavers))
"pm-weaver/"))
(sel (pm--completing-read "Choose weaver: " weavers nil t nil 'pm--weaver-hist))
(out (intern (cdr sel))))
(setq-local pm--weaver:from-last nil)
(setq-local pm--weaver:to-last nil)
(oset pm/polymode :weaver out)
out))
(provide 'polymode-weave)

Binary file not shown.

View File

@@ -0,0 +1,449 @@
;;; polymode.el --- Versatile multiple modes with extensive literate programming support
;;
;; Filename: polymode.el
;; Author: Spinu Vitalie
;; Maintainer: Spinu Vitalie
;; Copyright (C) 2013-2014, Spinu Vitalie, all rights reserved.
;; Version: 1.0
;; Package-Requires: ((emacs "24"))
;; 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.
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; Extensible, fast, objected-oriented multimode specifically designed for
;; literate programming. Extensible support for weaving, tangling and export.
;;
;; Usage: https://github.com/vspinu/polymode
;;
;; Design new polymodes: https://github.com/vspinu/polymode/tree/master/modes
;;
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:
(require 'polymode-core)
(require 'polymode-classes)
(require 'polymode-methods)
(require 'polymode-compat)
(require 'polymode-debug)
(require 'polymode-export)
(require 'polymode-weave)
(require 'poly-lock)
(require 'poly-base)
(defcustom polymode-prefix-key "\M-n"
"Prefix key for the polymode mode keymap.
Not effective after loading the polymode library."
:group 'polymode
:type '(choice string vector))
(defvar polymode-mode-map
(let ((map (make-sparse-keymap)))
(define-key map polymode-prefix-key
(let ((map (make-sparse-keymap)))
;; navigation
(define-key map "\C-n" 'polymode-next-chunk)
(define-key map "\C-p" 'polymode-previous-chunk)
(define-key map "\C-\M-n" 'polymode-next-chunk-same-type)
(define-key map "\C-\M-p" 'polymode-previous-chunk-same-type)
;; chunk manipulation
(define-key map "\M-k" 'polymode-kill-chunk)
(define-key map "\M-m" 'polymode-mark-or-extend-chunk)
(define-key map "\C-t" 'polymode-toggle-chunk-narrowing)
(define-key map "\M-i" 'polymode-insert-new-chunk)
;; backends
(define-key map "e" 'polymode-export)
(define-key map "E" 'polymode-set-exporter)
(define-key map "w" 'polymode-weave)
(define-key map "W" 'polymode-set-weaver)
(define-key map "t" 'polymode-tangle)
(define-key map "T" 'polymode-set-tangler)
(define-key map "$" 'polymode-show-process-buffer)
;; todo: add polymode-goto-process-buffer
map))
(define-key map [menu-bar Polymode]
(cons "Polymode"
(let ((map (make-sparse-keymap "Polymode")))
(define-key-after map [next]
'(menu-item "Next chunk" polymode-next-chunk))
(define-key-after map [previous]
'(menu-item "Previous chunk" polymode-previous-chunk))
(define-key-after map [next-same]
'(menu-item "Next chunk same type" polymode-next-chunk-same-type))
(define-key-after map [previous-same]
'(menu-item "Previous chunk same type" polymode-previous-chunk-same-type))
(define-key-after map [mark]
'(menu-item "Mark or extend chunk" polymode-mark-or-extend-chunk))
(define-key-after map [kill]
'(menu-item "Kill chunk" polymode-kill-chunk))
(define-key-after map [insert]
'(menu-item "Insert new chunk" polymode-insert-new-chunk))
map)))
map)
"The default minor mode keymap that is active in all polymode
modes.")
;;; COMMANDS
(defvar *span*)
(defun polymode-next-chunk (&optional N)
"Go COUNT chunks forwards.
Return, how many chucks actually jumped over."
(interactive "p")
(let* ((sofar 0)
(back (< N 0))
(beg (if back (point-min) (point)))
(end (if back (point) (point-max)))
(N (if back (- N) N)))
(condition-case nil
(pm-map-over-spans
(lambda ()
(unless (memq (car *span*) '(head tail))
(when (>= sofar N)
(signal 'quit nil))
(setq sofar (1+ sofar))))
beg end nil back)
(quit (when (looking-at "\\s *$")
(forward-line)))
(pm-switch-to-buffer))
sofar))
;;fixme: problme with long chunks .. point is recentered
;;todo: merge into next-chunk
(defun polymode-previous-chunk (&optional N)
"Go COUNT chunks backwards .
Return, how many chucks actually jumped over."
(interactive "p")
(polymode-next-chunk (- N)))
(defun polymode-next-chunk-same-type (&optional N)
"Go to next COUNT chunk.
Return, how many chucks actually jumped over."
(interactive "p")
(let* ((sofar 0)
(back (< N 0))
(beg (if back (point-min) (point)))
(end (if back (point) (point-max)))
(N (if back (- N) N))
this-type this-class)
(condition-case nil
(pm-map-over-spans
(lambda ()
(unless (memq (car *span*) '(head tail))
(when (and (equal this-class
(eieio-object-name (car (last *span*))))
(eq this-type (car *span*)))
(setq sofar (1+ sofar)))
(unless this-class
(setq this-class (eieio-object-name (car (last *span*)))
this-type (car *span*)))
(when (>= sofar N)
(signal 'quit nil))))
beg end nil back)
(quit (when (looking-at "\\s *$")
(forward-line)))
(pm-switch-to-buffer))
sofar))
(defun polymode-previous-chunk-same-type (&optional N)
"Go to previus COUNT chunk.
Return, how many chucks actually jumped over."
(interactive "p")
(polymode-next-chunk-same-type (- N)))
(defun pm--kill-span (types)
(let ((span (pm-get-innermost-span)))
(when (memq (car span) types)
(delete-region (nth 1 span) (nth 2 span)))))
(defun polymode-kill-chunk ()
"Kill current chunk"
(interactive)
(pcase (pm-get-innermost-span)
(`(,(or `nil `host) ,beg ,end ,_) (delete-region beg end))
(`(body ,beg ,end ,_)
(goto-char beg)
(pm--kill-span '(body))
(pm--kill-span '(head tail))
(pm--kill-span '(head tail)))
(`(tail ,beg ,end ,_)
(if (eq beg (point-min))
(delete-region beg end)
(goto-char (1- beg))
(polymode-kill-chunk)))
(`(head ,_ ,end ,_)
(goto-char end)
(polymode-kill-chunk))
(_ (error "canoot find chunk to kill"))))
(defun polymode-toggle-chunk-narrowing ()
"Toggle narrowing of the current chunk."
(interactive)
(if (buffer-narrowed-p)
(progn (widen) (recenter))
(pcase (pm-get-innermost-span)
(`(head ,_ ,end ,_)
(goto-char end)
(pm-narrow-to-span))
(`(tail ,beg ,end ,_)
(if (eq beg (point-min))
(error "Invalid chunk")
(goto-char (1- beg))
(pm-narrow-to-span)))
(_ (pm-narrow-to-span)))))
(defun polymode-mark-or-extend-chunk ()
(interactive)
(error "Not implemented yet"))
(defun polymode-insert-new-chunk ()
(interactive)
(error "Not implemented yet"))
(defun polymode-show-process-buffer ()
(interactive)
(let ((buf (cl-loop for b being the buffers
if (buffer-local-value 'pm--process-buffer b)
return b)))
(if buf
(pop-to-buffer buf `(nil . ((inhibit-same-window . ,pop-up-windows))))
(message "No polymode process buffers found."))))
;;; HOOKS
;; In addition to these hooks there is poly-lock-after-change which is placed in
;; after-change-functions. See poly-lock.el
(defun polymode-post-command-select-buffer ()
"Select the appropriate (indirect) buffer corresponding to point's context.
This funciton is placed in local `post-command-hook'."
(when (and pm-allow-post-command-hook
polymode-mode
pm/chunkmode)
(condition-case err
(pm-switch-to-buffer)
(error (message "(pm-switch-to-buffer %s): %s"
(point) (error-message-string err))))))
(defun polymode-before-change-setup (beg end)
"Run `syntax-ppss-flush-cache' in all polymode buffers.
This function is placed in `before-change-functions' hook."
;; Modification hooks are run only in current buffer and not in other (base or
;; indirect) buffers. Thus some actions like flush of ppss cache must be taken
;; care explicitly. We run some safety hooks checks here as well.
(dolist (buff (oref pm/polymode -buffers))
;; The following two checks are unnecessary by poly-lock design, but we are
;; checking them here, just in case.
;; VS[06-03-2016]: `fontification-functions' probably should be checked as well.
(when (memq 'font-lock-after-change-function after-change-functions)
(remove-hook 'after-change-functions 'font-lock-after-change-function t))
(when (memq 'jit-lock-after-change after-change-functions)
(remove-hook 'after-change-functions 'jit-lock-after-change t))
(with-current-buffer buff
;; now `syntax-ppss-flush-cache is harmless, but who knows in the future.
(when (memq 'syntax-ppss-flush-cache before-change-functions)
(remove-hook 'before-change-functions 'syntax-ppss-flush-cache t))
(syntax-ppss-flush-cache beg end))))
;;; DEFINE
;;;###autoload
(defmacro define-polymode (mode config &optional keymap &rest body)
"Define a new polymode MODE.
This macro defines command MODE and an indicator variable MODE
which becomes t when MODE is active and nil otherwise.
MODE command is similar to standard emacs major modes and it can
be used in `auto-mode-alist'. Standard hook MODE-hook is run at
the end of the initialization of each polymode buffer (both
indirect and base buffers). Additionally MODE-map is created
based on the CONFIG's :map slot and the value of the :keymap
argument; see below.
CONFIG is a name of a config object representing the mode.
MODE command can also be use as a minor mode. Current major mode
is not reinitialized if it coincides with the :mode slot of
CONFIG object or if the :mode slot is nil.
BODY contains code to be executed after the complete
initialization of the polymode (`pm-initialize') and before
running MODE-hook. Before the BODY code, you can write keyword
arguments, i.e. alternating keywords and values. The following
special keywords are supported:
:lighter SPEC Optional LIGHTER is displayed in the mode line when
the mode is on. If omitted, it defaults to
the :lighter slot of CONFIG object.
:keymap MAP Same as the KEYMAP argument.
If nil, a new MODE-map keymap is created what
directly inherits from the keymap defined by
the :map slot of CONFIG object. In most cases it
is a simple map inheriting form
`polymode-mode-map'. If t or an alist (of
bindings suitable to be passed to
`easy-mmode-define-keymap') a keymap MODE-MAP is
build by mergin this alist with the :map
specification of the CONFIG object. If a symbol,
it should be a variable whose value is a
keymap. No MODE-MAP is automatically created in
the latter case and :map slot of the CONFIG
object is ignored.
:after-hook A single lisp form which is evaluated after the mode hooks
have been run. It should not be quoted."
(declare
(debug (&define name name
[&optional [&not keywordp] sexp]
[&rest [keywordp sexp]]
def-body)))
(when (keywordp keymap)
(push keymap body)
(setq keymap nil))
(let* ((last-message (make-symbol "last-message"))
(mode-name (symbol-name mode))
(pretty-name (concat
(replace-regexp-in-string "poly-\\|-mode" "" mode-name)
" polymode"))
(keymap-sym (intern (concat mode-name "-map")))
(hook (intern (concat mode-name "-hook")))
(extra-keywords nil)
(after-hook nil)
keyw lighter)
;; Check keys.
(while (keywordp (setq keyw (car body)))
(setq body (cdr body))
(pcase keyw
(`:lighter (setq lighter (purecopy (pop body))))
(`:keymap (setq keymap (pop body)))
(`:after-hook (setq after-hook (pop body)))
(_ (push keyw extra-keywords) (push (pop body) extra-keywords))))
`(progn
:autoload-end
;; Define the variable to enable or disable the mode.
(defvar ,mode nil ,(format "Non-nil if %s mode is enabled." pretty-name))
(make-variable-buffer-local ',mode)
(let* ((keymap ,keymap)
(config ',config)
(lighter (or ,lighter
(oref (symbol-value config) :lighter)))
key-alist)
(unless (keymapp keymap)
;; keymap is either nil or list. Iterate through parents' :map slot
;; and gather keys.
(setq key-alist keymap)
(let* ((pi (symbol-value config))
map mm-name)
(while pi
(setq map (and (slot-boundp pi :map)
(oref pi :map)))
(if (and (symbolp map)
(keymapp (symbol-value map)))
;; If one of the parent's :map is a keymap, use it as our
;; keymap and stop further descent.
(setq keymap (symbol-value map)
pi nil)
;; Descend to next parent and append the key list to key-alist
(setq pi (and (slot-boundp pi :parent-instance)
(oref pi :parent-instance))
key-alist (append key-alist map))))))
(unless keymap
;; If we couldn't figure out the original keymap:
(setq keymap polymode-mode-map))
;; Define the minor-mode keymap:
(defvar ,keymap-sym
(easy-mmode-define-keymap key-alist nil nil `(:inherit ,keymap))
,(format "Keymap for %s." pretty-name))
;; The actual mode function:
(defun ,mode (&optional arg) ,(format "%s.\n\n\\{%s}" pretty-name keymap-sym)
(interactive)
(unless ,mode
(let ((,last-message (current-message)))
(unless pm/polymode ;; don't reinstall for time being
(let ((config (clone ,config)))
(oset config :minor-mode ',mode)
(pm-initialize config)))
;; set our "minor" mode
(setq ,mode t)
,@body
(run-hooks ',hook)
;; Avoid overwriting a message shown by the body,
;; but do overwrite previous messages.
(when (and (called-interactively-p 'any)
(or (null (current-message))
(not (equal ,last-message
(current-message)))))
(message ,(format "%s enabled" pretty-name)))
,@(when after-hook `(,after-hook))
(force-mode-line-update)))
;; Return the new setting.
,mode)
(add-minor-mode ',mode lighter ,keymap-sym)))))
(define-minor-mode polymode-minor-mode
"Polymode minor mode, used to make everything work."
nil " PM" polymode-mode-map)
(define-derived-mode poly-head-tail-mode prog-mode "HeadTail"
"Default major mode for polymode head and tail spans.")
(define-derived-mode poly-fallback-mode prog-mode "FallBack"
;; fixme:
;; 1. doesn't work as fallback for hostmode
;; 2. highlighting is lost (Rnw with inner fallback)
"Default major mode for modes which were not found.
This is better than fundamental-mode because it allows running
globalized minor modes and can run user hooks.")
;;; FONT-LOCK
;; indulge elisp font-lock :)
(dolist (mode '(emacs-lisp-mode lisp-interaction-mode))
(font-lock-add-keywords
mode
'(("(\\(define-polymode\\)\\s +\\(\\(\\w\\|\\s_\\)+\\)"
(1 font-lock-keyword-face)
(2 font-lock-variable-name-face)))))
(provide 'polymode)
;;; polymode.el ends here

Binary file not shown.