├── targets ├── deps ├── melpa-init.el ├── checkdoc.el ├── test.el ├── lint.el ├── local.el ├── utils.el └── melpa.el ├── .gitignore ├── Eask ├── README.md ├── tests ├── input │ ├── markdown-with-yaml.md │ └── markdown.md └── poly-markdown-tests.el ├── .github └── workflows │ └── test.yml ├── Makefile └── poly-markdown.el /targets/deps: -------------------------------------------------------------------------------- 1 | (yaml-mode) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *# 3 | #* 4 | auto/ 5 | *\[weaved\]* 6 | tmp/ 7 | *woven* 8 | *exported* 9 | .tmd 10 | tmp* 11 | .ELPA 12 | .MELPA 13 | site/ 14 | *autoloads.el 15 | *.elc 16 | *.html 17 | *.pdf 18 | 19 | # Packaging 20 | .eask/ 21 | dist/ 22 | -------------------------------------------------------------------------------- /targets/melpa-init.el: -------------------------------------------------------------------------------- 1 | 2 | (require 'package) 3 | 4 | (setq package-user-dir (expand-file-name (format ".ELPA/%s" emacs-version)) 5 | package-archives '(("gnu" . "https://elpa.gnu.org/packages/") 6 | ("melpa" . "https://melpa.org/packages/"))) 7 | 8 | (package-initialize) 9 | -------------------------------------------------------------------------------- /targets/checkdoc.el: -------------------------------------------------------------------------------- 1 | 2 | (let ((sentence-end-double-space) 3 | (checkdoc-arguments-in-order-flag) 4 | (checkdoc-verb-check-experimental-flag) 5 | (checkdoc-force-docstrings-flag)) 6 | (let ((files (directory-files default-directory nil "^[^.].*el$"))) 7 | (dolist (f files) 8 | (unless (member f '("poly-markdown-autoloads.el")) 9 | (checkdoc-file f))))) 10 | -------------------------------------------------------------------------------- /targets/test.el: -------------------------------------------------------------------------------- 1 | (setq polymode-test-dir (expand-file-name "tests/")) 2 | (add-to-list 'load-path polymode-test-dir) 3 | (dolist (f (directory-files polymode-test-dir t ".*el$")) 4 | (load f)) 5 | 6 | (setq pm-verbose (getenv "PM_VERBOSE") 7 | ert-batch-backtrace-right-margin 200 8 | ert-batch-print-level nil 9 | ert-batch-print-length nil) 10 | 11 | ;; (toggle-debug-on-error) 12 | (ert-run-tests-batch-and-exit t) 13 | -------------------------------------------------------------------------------- /targets/lint.el: -------------------------------------------------------------------------------- 1 | 2 | (require 'checkdoc) 3 | (require 'elisp-lint) 4 | 5 | (let ((sentence-end-double-space) 6 | (checkdoc-arguments-in-order-flag) 7 | (checkdoc-verb-check-experimental-flag) 8 | (checkdoc-force-docstrings-flag) 9 | (elisp-lint-indent-specs 10 | '((pm-test-run-on-file . 2) 11 | (pm-test-run-on-string . 1) 12 | (pm-test-poly-lock . 2))) 13 | (elisp-lint-ignored-validators '("package-format" "indent-character" "fill-column"))) 14 | 15 | (elisp-lint-files-batch)) 16 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | ;; -*- mode: eask; lexical-binding: t -*- 2 | 3 | (package "poly-markdown" 4 | "0.2.2" 5 | "Polymode for markdown-mode") 6 | 7 | (website-url "https://github.com/polymode/poly-markdown") 8 | (keywords "emacs") 9 | 10 | (package-file "poly-markdown.el") 11 | 12 | (script "test" "echo \"Error: no test specified\" && exit 1") 13 | 14 | (source 'gnu) 15 | (source 'melpa) 16 | 17 | (depends-on "emacs" "25") 18 | (depends-on "polymode") 19 | (depends-on "markdown-mode") 20 | 21 | (development 22 | (depends-on "yaml-mode")) 23 | -------------------------------------------------------------------------------- /targets/local.el: -------------------------------------------------------------------------------- 1 | (load-file "targets/utils.el") 2 | 3 | ;; add .ELPA packages 4 | (let ((elpa-dirs (directory-files (expand-file-name (format ".ELPA/%s" emacs-version)) t))) 5 | (setq load-path (append elpa-dirs load-path))) 6 | 7 | ;; add all poly* and deps in the parent directory and overate any in the .ELPA 8 | (let* ((deps (cons 'poly (polymode-library-deps "poly-markdown.el"))) 9 | (regx (format "^\\(%s\\)" (mapconcat #'symbol-name deps "\\|"))) 10 | (local-dirs (directory-files (file-name-directory (directory-file-name default-directory)) 11 | t regx))) 12 | (setq load-path (append local-dirs load-path))) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | License GPL 3 4 | MELPA 5 | MELPA Stable 6 |

7 | 8 | 9 | Polymode for [markdown-mode](https://github.com/jrblevin/markdown-mode). 10 | -------------------------------------------------------------------------------- /targets/utils.el: -------------------------------------------------------------------------------- 1 | 2 | (defun polymode-library-deps (lib-file) 3 | (let ((dev-deps '(elisp-lint)) 4 | (deps (let ((file (expand-file-name "targets/deps"))) 5 | (when (file-exists-p file) 6 | (with-temp-buffer 7 | (insert-file-contents file) 8 | (read (buffer-string)))))) 9 | (deps-requires (with-temp-buffer 10 | (insert-file-contents lib-file) 11 | (goto-char (point-min)) 12 | (when (re-search-forward "Package-Requires:" nil t) 13 | (car (read-from-string (buffer-substring (point) (point-at-eol)))))))) 14 | (delq 'emacs (delete-dups (append deps (mapcar #'car deps-requires)))))) 15 | -------------------------------------------------------------------------------- /tests/input/markdown-with-yaml.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | date: 2015-08-13 11:35:25 EST 4 | --- 5 | 6 | Polymode Markdown Test Cases 7 | ============================ 8 | 9 | ## Intro 10 | 11 | Fragments taken from `markdown-mode/tests/test-cases.text` and interspersed with 12 | various code blocks. 13 | 14 | 1. Lists 15 | -------- 16 | 17 | Unordered lists: 18 | 19 | - This is a `el (defvar bullet-point "bullet-point")`. 20 | - This is a `ada "Sub bullet point"`. 21 | - This is `python ["*" + x + "*" for x in ["another", "bullet", "point"]]` 22 | 23 | ```fortran 24 | * euclid.f (FORTRAN 77) 25 | * Find greatest common divisor using the Euclidean algorithm 26 | PROGRAM EUCLID 27 | PRINT *, 'A?' 28 | READ *, NA 29 | IF (NA.LE.0) THEN 30 | PRINT *, 'A must be a positive integer.' 31 | STOP 32 | END IF 33 | PRINT *, 'B?' 34 | READ *, NB 35 | IF (NB.LE.0) THEN 36 | PRINT *, 'B must be a positive integer.' 37 | STOP 38 | END IF 39 | PRINT *, 'The GCD of', NA, ' and', NB, ' is', NGCD(NA, NB), '.' 40 | STOP 41 | END 42 | ``` 43 | -------------------------------------------------------------------------------- /targets/melpa.el: -------------------------------------------------------------------------------- 1 | 2 | (load-file "targets/utils.el") 3 | (require 'package) 4 | 5 | (setq package-deps (polymode-library-deps "poly-markdown.el") 6 | package-user-dir (expand-file-name (format ".ELPA/%s" emacs-version)) 7 | package-archives '(("gnu" . "https://elpa.gnu.org/packages/") 8 | ("melpa" . "https://melpa.org/packages/"))) 9 | 10 | (defun package-desc-new (name) 11 | (cadr (assq name package-archive-contents))) 12 | 13 | (defun package-outdated-p (name) 14 | (let ((old-version (package-desc-version (cadr (assq name package-alist)))) 15 | (new-version (package-desc-version (cadr (assq name package-archive-contents))))) 16 | (and (listp old-version) (listp new-version) 17 | (version-list-< old-version new-version)))) 18 | 19 | (package-initialize) 20 | (package-refresh-contents) 21 | 22 | (dolist (package (append dev-deps package-deps)) 23 | (if (package-installed-p package) 24 | (when (package-outdated-p package) 25 | (package-install-from-archive (cadr (assq package package-archive-contents)))) 26 | (package-install package))) 27 | 28 | (message "INSTALLED DEPS: %s" 29 | (mapconcat 30 | (lambda (pkg) 31 | (format "%s:%S" pkg 32 | (package-desc-version (cadr (assq pkg package-archive-contents))))) 33 | package-deps 34 | " ")) 35 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | runs-on: ${{ matrix.os }} 17 | continue-on-error: ${{ matrix.experimental }} 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | os: [ubuntu-latest, macos-latest, windows-latest] 22 | emacs-version: 23 | #- 26.3 24 | #- 27.2 25 | #- 28.2 26 | #- 29.4 27 | - 30.1 28 | experimental: [false] 29 | include: 30 | - os: ubuntu-latest 31 | emacs-version: snapshot 32 | experimental: true 33 | - os: macos-latest 34 | emacs-version: snapshot 35 | experimental: true 36 | - os: windows-latest 37 | emacs-version: snapshot 38 | experimental: true 39 | 40 | steps: 41 | - uses: actions/checkout@v4 42 | 43 | - uses: jcs090218/setup-emacs@master 44 | with: 45 | version: ${{ matrix.emacs-version }} 46 | 47 | - uses: emacs-eask/setup-eask@master 48 | with: 49 | version: 'snapshot' 50 | 51 | - name: Build 52 | run: | 53 | eask package 54 | eask install 55 | eask compile 56 | 57 | - name: Run tests 58 | run: | 59 | eask install-deps --dev 60 | eask test ert ./tests/*.el 61 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MODULE = poly-markdown 2 | export EMACS ?= emacs 3 | EMACS_VERSION = $(shell ${EMACS} -Q --batch --eval "(princ emacs-version)") 4 | ELPA_DIR := .ELPA/$(EMACS_VERSION) 5 | EMACSRUN = $(EMACS) -Q --debug-init -L . -L modes -L tests -L $(ELPA_DIR) 6 | EMACSBATCH = $(EMACSRUN) --batch 7 | 8 | ELS = $(wildcard *.el) 9 | OBJECTS = $(ELS:.el=.elc) 10 | LINTELS = $(filter-out poly-markdown-autoloads.el, $(ELS)) 11 | 12 | # export PM_VERBOSE 13 | 14 | .PHONY: all build checkdoc lint clean cleanall melpa elpa start test version 15 | 16 | all: build checkdoc test 17 | 18 | build: version clean 19 | @echo "******************* BUILDING $(MODULE) *************************" 20 | $(EMACSBATCH) --load targets/melpa-init.el --funcall batch-byte-compile *.el 21 | 22 | checkdoc: version 23 | @echo "******************* CHECKDOC $(MODULE) *************************" 24 | $(EMACSBATCH) --load targets/checkdoc.el 25 | 26 | lint: version 27 | @$(EMACSBATCH) --load targets/melpa-init.el --load targets/lint.el $(LINTELS) 28 | 29 | clean: 30 | rm -f $(OBJECTS) 31 | 32 | cleanall: clean 33 | rm -rf $(ELPA_DIR) *autoloads.el 34 | 35 | melpa: version 36 | $(EMACSBATCH) --load targets/melpa.el 37 | 38 | elpa: melpa 39 | 40 | start: version melpa 41 | $(EMACSRUN) -L . --load targets/melpa-init.el --load tests/*.el 42 | 43 | startvs: version 44 | $(EMACSRUN) -L . --load targets/local.el \ 45 | --load tests/*.el --load ~/.eBasic.el 46 | 47 | test: build version 48 | @echo "******************* Testing $(MODULE) ***************************" 49 | $(EMACSBATCH) --load targets/melpa-init.el --load targets/test.el 50 | 51 | test-local: version 52 | @echo "******************* Testing $(MODULE) ***************************" 53 | $(EMACSBATCH) --load targets/local.el --load targets/test.el 54 | 55 | version: 56 | @echo "EMACS VERSION: $(EMACS_VERSION)" 57 | @echo "GIT HEAD: $(shell git rev-parse --short HEAD)" 58 | 59 | -------------------------------------------------------------------------------- /tests/input/markdown.md: -------------------------------------------------------------------------------- 1 | 2 | Polymode Markdown Test Cases 3 | ============================ 4 | 5 | ## Intro 6 | 7 | Fragments taken from `markdown-mode/tests/test-cases.text` and interspersed with 8 | various code blocks. 9 | 10 | 1. Lists 11 | -------- 12 | 13 | Unordered lists: 14 | 15 | - This is a `el (defvar bullet-point "bullet-point")`. 16 | - This is a `ada "Sub bullet point"`. 17 | - This is `python ["*" + x + "*" for x in ["another", "bullet", "point"]]` 18 | 19 | ```fortran 20 | * euclid.f (FORTRAN 77) 21 | * Find greatest common divisor using the Euclidean algorithm 22 | PROGRAM EUCLID 23 | PRINT *, 'A?' 24 | READ *, NA 25 | IF (NA.LE.0) THEN 26 | PRINT *, 'A must be a positive integer.' 27 | STOP 28 | END IF 29 | PRINT *, 'B?' 30 | READ *, NB 31 | IF (NB.LE.0) THEN 32 | PRINT *, 'B must be a positive integer.' 33 | STOP 34 | END IF 35 | PRINT *, 'The GCD of', NA, ' and', NB, ' is', NGCD(NA, NB), '.' 36 | STOP 37 | END 38 | ``` 39 | 40 | Ordered lists 41 | 42 | 1. This is an ordered list 43 | 2. With a second element. 44 | 44. And a forty-fourth element. 45 | 3. Remember, Markdown doesn't care which number you use. 46 | 47 | ```perl 48 | # Scan a file and print all the URL's it links to. 49 | sub scan { 50 | my ($fn) = @_; 51 | 52 | open(IN, $fn) or return 0; 53 | 54 | # Go through each line in the file. 55 | while() { 56 | # Repeatedly match URLs in the line. Each one is removed by 57 | # replacing it with the empty string. The loop body will execute 58 | # once for each match/replace, and prints the URL part of the 59 | # matched text. 60 | while(s/<\s*A\s+[^>]*HREF\s*\=\s*"([^"]+)"//i) { 61 | print " $1\n"; 62 | } 63 | } 64 | 65 | close IN; 66 | return 1; 67 | } 68 | ``` 69 | List items with bold and italic 70 | 71 | > * This is a list item *in italics*, just a test. 72 | > * *List item in italics.* 73 | > * This is a list item **in bold**, just a test. 74 | > * **List item in bold.** 75 | 76 | Bold and italic phrases at the beginning of lines: 77 | 78 | ```el 79 | (defmacro thread-last (&rest forms) 80 | "Thread FORMS elements as the last argument of their successor. 81 | Example: 82 | (thread-last 83 | 5 84 | (+ 20) 85 | (/ 25) 86 | - 87 | (+ 40)) 88 | Is equivalent to: 89 | (+ 40 (- (/ 25 (+ 20 5)))) 90 | Note how the single `-' got converted into a list before 91 | threading." 92 | (declare (indent 1) (debug thread-first)) 93 | `(internal--thread-argument nil ,@forms)) 94 | ``` 95 | 96 | *not a list* 97 | **also not a list** 98 | 99 | 100 | 2. Blockquotes 101 | -------------- 102 | 103 | > this is a test 104 | > of the blockquote mechanism 105 | 106 | ```pascal 107 | 108 | type 109 | pNode = ^Node; 110 | Node = record 111 | a :integer; 112 | b : char; 113 | c : pNode {extra semicolon not strictly required} 114 | end; 115 | var 116 | NodePtr : pNode; 117 | IntPtr : ^integer; 118 | 119 | ``` 120 | 121 | 122 | 3. Two Inline Links on One Line 123 | ------------------------------- 124 | 125 | I did notice a minor bug. if there are two inline links in the same line, e.g. 126 | [foo](bar) baz [foo](bar), it colors the text between the links (baz) as well. 127 | 128 | 129 | 4. Empty Inline Links 130 | --------------------- 131 | 132 | []() 133 | [](asdf) 134 | [asdf]() 135 | 136 | ```python 137 | def insertion_sort_bin(seq): 138 | for i in range(1, len(seq)): 139 | key = seq[i] 140 | # invariant: ``seq[:i]`` is sorted 141 | # find the least `low' such that ``seq[low]`` is not less then `key'. 142 | # Binary search in sorted sequence ``seq[low:up]``: 143 | low, up = 0, i 144 | while up > low: 145 | middle = (low + up) // 2 146 | if seq[middle] < key: 147 | low = middle + 1 148 | else: 149 | up = middle 150 | # insert key at position ``low`` // no-indent-test 151 | seq[:] = seq[:low] + [key] + seq[low:i] + seq[i + 1:] 152 | ``` 153 | 154 | ## 5. Bold and Italics on the Same Line 155 | 156 | **foo and doo** or *ziddle zop* 157 | 158 | ```el 159 | 160 | (defun delete-dups (list) 161 | "Destructively remove `equal' duplicates from LIST. 162 | Store the result in LIST and return it. LIST must be a proper list. 163 | Of several `equal' occurrences of an element in LIST, the first 164 | one is kept." 165 | (let ((l (length list))) 166 | (if (> l 100) 167 | (let ((hash (make-hash-table :test #'equal :size l)) 168 | (tail list) retail) 169 | (puthash (car list) t hash) 170 | (while (setq retail (cdr tail)) 171 | (let ((elt (car retail))) 172 | (if (gethash elt hash) 173 | (setcdr tail (cdr retail)) 174 | (puthash elt t hash) 175 | (setq tail retail))))) 176 | (let ((tail list)) 177 | (while tail 178 | (setcdr tail (delete (car tail) (cdr tail))) 179 | (setq tail (cdr tail)))))) 180 | list) 181 | 182 | ``` 183 | 184 | Indentation of modes with simple `indent-line-function` like `indent-relative` 185 | 186 | ```sql 187 | SELECT * FROM table; 188 | SELECT * FROM table; 189 | SELECT * FROM table; 190 | ``` 191 | 192 | The end. 193 | -------------------------------------------------------------------------------- /poly-markdown.el: -------------------------------------------------------------------------------- 1 | ;;; poly-markdown.el --- Polymode for markdown-mode -*- lexical-binding: t -*- 2 | ;; 3 | ;; Author: Vitalie Spinu 4 | ;; Maintainer: Vitalie Spinu 5 | ;; Copyright (C) 2018 6 | ;; Version: 0.2.2 7 | ;; Package-Requires: ((emacs "25") (polymode "0.2.2") (markdown-mode "2.3")) 8 | ;; URL: https://github.com/polymode/poly-markdown 9 | ;; Keywords: emacs 10 | ;; 11 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 | ;; 13 | ;; This file is *NOT* part of GNU Emacs. 14 | ;; 15 | ;; This program is free software; you can redistribute it and/or 16 | ;; modify it under the terms of the GNU General Public License as 17 | ;; published by the Free Software Foundation; either version 3, or 18 | ;; (at your option) any later version. 19 | ;; 20 | ;; This program is distributed in the hope that it will be useful, 21 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 | ;; General Public License for more details. 24 | ;; 25 | ;; You should have received a copy of the GNU General Public License 26 | ;; along with this program; see the file COPYING. If not, write to 27 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 28 | ;; Floor, Boston, MA 02110-1301, USA. 29 | ;; 30 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 31 | ;; 32 | ;;; Commentary: 33 | ;; 34 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 35 | ;; 36 | ;;; Code: 37 | 38 | (require 'polymode) 39 | (require 'poly-lock) 40 | (require 'markdown-mode) 41 | 42 | ;; Declarations 43 | (defvar markdown-enable-math) 44 | 45 | (define-obsolete-variable-alias 'pm-host/markdown 'poly-markdown-hostmode "v0.2") 46 | (define-obsolete-variable-alias 'pm-inner/markdown-yaml-metadata 'poly-markdown-yaml-metadata-innermode "v0.2") 47 | (define-obsolete-variable-alias 'pm-inner/markdown-fenced-code 'poly-markdown-fenced-code-innermode "v0.2") 48 | (define-obsolete-variable-alias 'pm-inner/markdown-inline-code 'poly-markdown-inline-code-innermode "v0.2") 49 | (define-obsolete-variable-alias 'pm-inner/markdown-displayed-math 'poly-markdown-displayed-math-innermode "v0.2") 50 | (define-obsolete-variable-alias 'pm-inner/markdown-inline-math 'poly-markdown-inline-math-innermode "v0.2") 51 | (define-obsolete-variable-alias 'pm-poly/markdown 'poly-markdown-polymode "v0.2") 52 | 53 | (defgroup poly-markdown nil 54 | "Settings for ‘poly-markdown’." 55 | :group 'polymode) 56 | 57 | (defcustom poly-markdown-enable-latex-math t 58 | "When non-nil, enable LaTeX-related innermodes in poly-markdown: 59 | - inline/display math regions ($...$, $$...$$, \\(...\\), \\[...\\]) 60 | - fenced code blocks with language \"latex\" or \"tex\"." 61 | :type 'boolean 62 | :group 'poly-markdown) 63 | 64 | (define-hostmode poly-markdown-hostmode 65 | :mode 'markdown-mode 66 | :init-functions '(poly-markdown-remove-markdown-hooks)) 67 | 68 | (define-innermode poly-markdown-root-innermode 69 | :mode nil 70 | :fallback-mode 'host 71 | :head-mode 'host 72 | :tail-mode 'host) 73 | 74 | (define-innermode poly-markdown-yaml-metadata-innermode poly-markdown-root-innermode 75 | :mode 'yaml-mode 76 | :head-matcher (pm-make-text-property-matcher 'markdown-yaml-metadata-begin :inc-end) 77 | :tail-matcher (pm-make-text-property-matcher 'markdown-yaml-metadata-end)) 78 | 79 | ;; allow extra . before language name https://github.com/polymode/polymode/issues/296 80 | ;; allow extra = before language name https://github.com/polymode/poly-markdown/issues/22 81 | (define-auto-innermode poly-markdown-fenced-code-innermode poly-markdown-root-innermode 82 | :head-matcher (cons "^[ \t]*\\(```[ \t]*{?[[:alpha:].=].*\n\\)" 1) 83 | :tail-matcher (cons "^[ \t]*\\(```\\)[ \t]*$" 1) 84 | :mode-matcher (cons "```[ \t]*{?[.=]?\\(?:lang *= *\\)?\\([^ \t\n;=,}]+\\)" 1)) 85 | 86 | ;; Intended to be inherited from by more specialized innermodes. 87 | ;; FIXME: Some font-lock issues on deletion. 88 | ;; TOTHINK: Can be added to the default configuration for the sake of the 89 | ;; default fallback, but it's problematic given that inline code blocks often 90 | ;; have arbitrary non-code or language specific content. 91 | (define-innermode poly-markdown-inline-code-innermode poly-markdown-root-innermode 92 | :head-matcher (cons "[^`]\\(`\\)[[:alpha:]+-&({*[]" 1) 93 | :tail-matcher (cons "[^`]\\(`\\)[^`]" 1) 94 | ;; :mode-matcher (cons "`[ \t]*{?\\(?:lang *= *\\)?\\([[:alpha:]+-]+\\)" 1) 95 | :allow-nested nil) 96 | 97 | (defun poly-markdown-displayed-math-head-matcher (count) 98 | (when markdown-enable-math 99 | (when (re-search-forward "\\\\\\[\\|^[ \t]*\\(\\$\\$\\)." nil t count) 100 | (if (match-beginning 1) 101 | (cons (match-beginning 1) (match-end 1)) 102 | (cons (match-beginning 0) (match-end 0)))))) 103 | 104 | (defun poly-markdown-displayed-math-tail-matcher (_count) 105 | (when markdown-enable-math 106 | (if (match-beginning 1) 107 | ;; head matched an $$..$$ block 108 | (when (re-search-forward "[^$]\\(\\$\\$\\)[^$[:alnum:]]" nil t) 109 | (cons (match-beginning 1) (match-end 1))) 110 | ;; head matched an \[..\] block 111 | (when (re-search-forward "\\\\\\]" nil t) 112 | (cons (match-beginning 0) (match-end 0)))))) 113 | 114 | (define-innermode poly-markdown-displayed-math-innermode poly-markdown-root-innermode 115 | "Displayed math $$..$$ innermode. 116 | Tail must be flowed by a new line but head need not (a space or 117 | comment character would do)." 118 | :mode 'LaTeX-mode ; AucTeX mode by default 119 | :fallback-mode 'latex-mode 120 | :head-matcher #'poly-markdown-displayed-math-head-matcher 121 | :tail-matcher #'poly-markdown-displayed-math-tail-matcher 122 | :allow-nested nil) 123 | 124 | (defun poly-markdown-inline-math-head-matcher (count) 125 | (when markdown-enable-math 126 | (when (re-search-forward "\\\\(\\|[ \t\n]\\(\\$\\)[^ $\t[:digit:]]" nil t count) 127 | (if (match-beginning 1) 128 | (cons (match-beginning 1) (match-end 1)) 129 | (cons (match-beginning 0) (match-end 0)))))) 130 | 131 | (defun poly-markdown-inline-math-tail-matcher (_count) 132 | (when markdown-enable-math 133 | (if (match-beginning 1) 134 | ;; head matched an $..$ block 135 | (when (re-search-forward "[^ $\\\t]\\(\\$\\)[^$[:alnum:]]" nil t) 136 | (cons (match-beginning 1) (match-end 1))) 137 | ;; head matched an \(..\) block 138 | (when (re-search-forward "\\\\)" nil t) 139 | (cons (match-beginning 0) (match-end 0)))))) 140 | 141 | (define-innermode poly-markdown-inline-math-innermode poly-markdown-root-innermode 142 | "Inline math $..$ block. 143 | First $ must be preceded by a white-space character and followed 144 | by a non-whitespace/digit character. The closing $ must be 145 | preceded by a non-whitespace and not followed by an alphanumeric 146 | character." 147 | :mode 'LaTeX-mode 148 | :fallback-mode 'latex-mode 149 | :head-matcher #'poly-markdown-inline-math-head-matcher 150 | :tail-matcher #'poly-markdown-inline-math-tail-matcher 151 | :allow-nested nil) 152 | 153 | ;;;###autoload (autoload 'poly-markdown-mode "poly-markdown") 154 | (define-polymode poly-markdown-mode 155 | :hostmode 'poly-markdown-hostmode 156 | :innermodes (append 157 | '(poly-markdown-fenced-code-innermode 158 | poly-markdown-yaml-metadata-innermode) 159 | (when poly-markdown-enable-latex-math 160 | '(poly-markdown-inline-math-innermode 161 | poly-markdown-displayed-math-innermode)))) 162 | 163 | ;;;###autoload 164 | (add-to-list 'auto-mode-alist '("\\.md\\'" . poly-markdown-mode)) 165 | 166 | ;;; FIXES: 167 | (defun poly-markdown-remove-markdown-hooks (_) 168 | ;; get rid of aggressive hooks (VS[02-09-2018]: probably no longer necessary) 169 | (remove-hook 'window-configuration-change-hook 'markdown-fontify-buffer-wiki-links t) 170 | (remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t)) 171 | 172 | ;;; Polymode for GitHub Flavored Markdown 173 | (define-hostmode poly-gfm-hostmode poly-markdown-hostmode 174 | :mode 'gfm-mode) 175 | 176 | ;;;###autoload (autoload 'poly-gfm-mode "poly-markdown") 177 | (define-polymode poly-gfm-mode poly-markdown-mode 178 | :hostmode 'poly-gfm-hostmode) 179 | 180 | (provide 'poly-markdown) 181 | -------------------------------------------------------------------------------- /tests/poly-markdown-tests.el: -------------------------------------------------------------------------------- 1 | (require 'markdown-mode) 2 | (require 'poly-markdown) 3 | (require 'polymode-test-utils) 4 | (require 'yaml-mode) 5 | ;; ada mode auto loading breaks without this 6 | (require 'speedbar) 7 | 8 | ;; fixme: add tests when after change spans wrongly temporally cover other spans 9 | 10 | (setq python-indent-offset 4 11 | python-indent-guess-indent-offset nil) 12 | 13 | (defun poly-markdown-tests-set-protected (protect) 14 | (let ((mode (if protect 'poly-head-tail-mode 'host))) 15 | (oset pm-host/markdown :protect-syntax protect) 16 | (oset pm-host/markdown :protect-font-lock protect) 17 | (oset pm-inner/markdown-inline-code :head-mode mode) 18 | (oset pm-inner/markdown-inline-code :tail-mode mode) 19 | (oset pm-inner/markdown-fenced-code :head-mode mode) 20 | (oset pm-inner/markdown-fenced-code :tail-mode mode))) 21 | 22 | (ert-deftest poly-markdown/spans-at-borders () 23 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 24 | (pm-map-over-spans 25 | (lambda (span) 26 | (let* ((sbeg (nth 1 span)) 27 | (send (nth 2 span)) 28 | (range1 (pm-innermost-range sbeg)) 29 | (range2 (pm-innermost-range send))) 30 | (should (eq sbeg (car range1))) 31 | (should (eq send (cdr range1))) 32 | (unless (eq send (point-max)) 33 | (should (eq send (car range2))))))))) 34 | 35 | (ert-deftest poly-markdown/spans-at-narrowed-borders () 36 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 37 | (pm-map-over-spans 38 | (lambda (span) 39 | (pm-with-narrowed-to-span span 40 | (let* ((range1 (pm-innermost-range (point-min))) 41 | (range2 (pm-innermost-range (point-max)))) 42 | (should (eq (car range1) (point-min))) 43 | (should (eq (cdr range1) (point-max))) 44 | (should (eq (car range2) (point-min))) 45 | (should (eq (cdr range2) (point-max))))))))) 46 | 47 | (ert-deftest poly-markdown/narrowed-spans () 48 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 49 | (narrow-to-region 60 200) 50 | (let ((span (pm-innermost-span (point-min)))) 51 | (should (eq (car span) nil)) 52 | (should (= (nth 1 span) 60)) 53 | (should (= (nth 2 span) 200))) 54 | (widen) 55 | (narrow-to-region 60 500) 56 | (let ((span (pm-innermost-span (point-min)))) 57 | (should (= (nth 1 span) 60)) 58 | ;; 223 should be when inline-innermode is in 59 | ;; (should (= (nth 2 span) 223)) 60 | (should (= (nth 2 span) 380))))) 61 | 62 | (ert-deftest poly-markdown/spans-at-point-max () 63 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 64 | (goto-char (point-max)) 65 | (pm-switch-to-buffer) 66 | 67 | (let ((span (pm-innermost-span (point-max)))) 68 | (should (eq (car span) nil)) 69 | (should (eq (nth 2 span) (point-max))) 70 | (delete-region (nth 1 span) (nth 2 span))) 71 | 72 | (let ((span (pm-innermost-span (point-max)))) 73 | (should (eq (car span) 'tail)) 74 | (should (eq (nth 2 span) (point-max))) 75 | (delete-region (nth 1 span) (nth 2 span))) 76 | 77 | (let ((span (pm-innermost-span (point-max)))) 78 | (should (eq (car span) 'body)) 79 | (should (eq (nth 2 span) (point-max))) 80 | (delete-region (nth 1 span) (nth 2 span))) 81 | 82 | (let ((span (pm-innermost-span (point-max)))) 83 | (should (eq (car span) 'head)) 84 | (should (eq (nth 2 span) (point-max))) 85 | (delete-region (nth 1 span) (nth 2 span))) 86 | 87 | (let ((span (pm-innermost-span (point-max)))) 88 | (should (eq (car span) nil)) 89 | (should (eq (nth 2 span) (point-max))) 90 | (delete-region (nth 1 span) (nth 2 span))))) 91 | 92 | ;; on emacs25 in batch mode these tests fail 93 | (when (or pm--emacs>26 (not noninteractive)) 94 | (ert-deftest poly-markdown/headings () 95 | (poly-markdown-tests-set-protected nil) 96 | (pm-test-poly-lock poly-markdown-mode "markdown.md" 97 | ((insert-1 ("^## Intro" beg)) 98 | (insert " ") 99 | (pm-test-faces) 100 | (delete-backward-char 1)) 101 | ((delete-2 "^2. Blockquotes") 102 | (pm-test-faces) 103 | (backward-kill-word 1)) 104 | ((insert-new-line-3 ("^3. Two Inline" end)) 105 | (insert "\n") 106 | (pm-test-faces) 107 | (delete-backward-char 1)))) 108 | (ert-deftest poly-markdown/headings-protected () 109 | (poly-markdown-tests-set-protected t) 110 | (pm-test-poly-lock poly-markdown-mode "markdown.md" 111 | ((insert-1 ("^## Intro" beg)) 112 | (insert " ") 113 | (pm-test-faces) 114 | (delete-backward-char 1)) 115 | ((delete-2 "^2. Blockquotes") 116 | (backward-kill-word 1)) 117 | ((insert-new-line-3 ("^3. Two Inline" end)) 118 | (insert "\n") 119 | (pm-test-faces) 120 | (delete-backward-char 1))))) 121 | 122 | (ert-deftest poly-markdown/fenced-code () 123 | (poly-markdown-tests-set-protected nil) 124 | (pm-test-poly-lock poly-markdown-mode "markdown.md" 125 | ((delete-fence (38)) 126 | ;; (switch-to-buffer (current-buffer)) 127 | (delete-forward-char 1) 128 | ;; (polymode-syntax-propertize (point-min) (point-max)) 129 | (pm-test-faces) 130 | (insert "`")) 131 | ((delete-fortran-print (23)) 132 | (forward-word) 133 | (delete-backward-char 1)) 134 | ((insert-perl-hello (51)) 135 | (insert "\"hello!\"\n") 136 | (indent-for-tab-command)) 137 | ((insert-lisp-arg "&rest forms") 138 | (backward-sexp 2) 139 | (insert "first-arg ")) 140 | ((python-kill-line (145)) 141 | (kill-line 3)) 142 | ((elisp-kill-sexp ("(while (setq retail" beg)) 143 | (kill-sexp)) 144 | ((elisp-kill-defun ("(defun delete-dups" beg)) 145 | (kill-sexp)))) 146 | 147 | (ert-deftest poly-markdown/fenced-code-protected () 148 | (poly-markdown-tests-set-protected t) 149 | (pm-test-poly-lock poly-markdown-mode "markdown.md" 150 | ((delete-fortran-print (23)) 151 | (forward-word) 152 | (delete-backward-char 1)) 153 | ((insert-ada-hello (51)) 154 | (insert "\"hello!\"\n") 155 | (indent-for-tab-command)) 156 | ((insert-lisp-arg "&rest forms") 157 | (backward-sexp 2) 158 | (insert "first-arg ")) 159 | ((python-kill-line (130)) 160 | (kill-line 3)) 161 | ((elisp-kill-sexp ("(while (setq retail" beg)) 162 | (kill-sexp)) 163 | ((elisp-kill-defun ("(defun delete-dups" beg)) 164 | (kill-sexp)))) 165 | 166 | (ert-deftest poly-markdown/inline-math () 167 | (ert-skip "FIXME: failing test") 168 | (let ((markdown-enable-math t)) 169 | (pm-test-run-on-string 'poly-markdown-mode 170 | "Some text with $\\text{inner math}$, formulas $E=mc^2$ 171 | $E=mc^2$, and more formulas $E=mc^2$; 172 | ```pascal 173 | Some none-sense (formula $E=mc^2$) 174 | ```" 175 | (switch-to-buffer (current-buffer)) 176 | (goto-char 17) 177 | (pm-switch-to-buffer) 178 | (should (eq major-mode 'latex-mode)) 179 | (goto-char 35) 180 | (pm-switch-to-buffer) 181 | (should (eq major-mode 'markdown-mode)) 182 | (goto-char 47) 183 | (pm-switch-to-buffer) 184 | (should (eq major-mode 'latex-mode)) 185 | (goto-char 54) 186 | (pm-switch-to-buffer) 187 | (should (eq major-mode 'markdown-mode)) 188 | (goto-char 56) 189 | (pm-switch-to-buffer) 190 | (should (eq major-mode 'latex-mode)) 191 | (goto-char 84) 192 | (pm-switch-to-buffer) 193 | (should (eq major-mode 'latex-mode)) 194 | (goto-char 91) 195 | (pm-switch-to-buffer) 196 | (should (eq major-mode 'markdown-mode)) 197 | (goto-char 127) 198 | (pm-switch-to-buffer) 199 | (should (eq major-mode 'pascal-mode))))) 200 | 201 | (ert-deftest poly-markdown/inline-math-both () 202 | (let ((markdown-enable-math t)) 203 | (pm-test-run-on-string 'poly-markdown-mode 204 | "Some text with $\\text{inner math}$, formulas $E=mc^2$, 205 | \\(E=mc^2\\), $E=mc^2$, and more formulas \\(E=mc^2\\) 206 | 207 | ```pascal 208 | Some none-sense (formula $E=mc^2$ \\(dddd\\)) 209 | ```" 210 | ;; (switch-to-buffer (current-buffer)) 211 | (goto-char 17) 212 | (pm-switch-to-buffer) 213 | (should (eq major-mode 'latex-mode)) 214 | (goto-char 35) 215 | (pm-switch-to-buffer) 216 | (should (eq major-mode 'markdown-mode)) 217 | (goto-char 48) 218 | (pm-switch-to-buffer) 219 | (should (eq major-mode 'latex-mode)) 220 | (goto-char 54) 221 | (pm-switch-to-buffer) 222 | (should (eq major-mode 'markdown-mode)) 223 | (goto-char 57) 224 | (pm-switch-to-buffer) 225 | (should (eq major-mode 'markdown-mode)) 226 | (goto-char 58) 227 | (pm-switch-to-buffer) 228 | (should (eq major-mode 'latex-mode)) 229 | (goto-char 64) 230 | (pm-switch-to-buffer) 231 | (should (eq major-mode 'markdown-mode)) 232 | (goto-char 69) 233 | (pm-switch-to-buffer) 234 | (should (eq major-mode 'latex-mode)) 235 | (goto-char 97) 236 | (pm-switch-to-buffer) 237 | (should (eq major-mode 'markdown-mode)) 238 | (goto-char 98) 239 | (pm-switch-to-buffer) 240 | (should (eq major-mode 'latex-mode)) 241 | (goto-char 155) 242 | (pm-switch-to-buffer) 243 | (should (eq major-mode 'pascal-mode))))) 244 | 245 | (ert-deftest poly-markdown/displayed-math () 246 | (let ((markdown-enable-math t)) 247 | (pm-test-run-on-string 'poly-markdown-mode 248 | "Some text with 249 | $$\\text{displayed math}$$, formulas 250 | $$E=mc^2$$ 251 | $$E=mc^2$$, and $343 more $$$ formulas $$$ and $3 252 | $$ E=mc^2 $$; 253 | ```pascal 254 | Some none-sense (formula 255 | $$E=mc^2$$ ) 256 | ```" 257 | ;; (switch-to-buffer (current-buffer)) 258 | (goto-char 18) 259 | (pm-switch-to-buffer) 260 | (should (eq major-mode 'latex-mode)) 261 | (goto-char 42) 262 | (pm-switch-to-buffer) 263 | (should (eq major-mode 'markdown-mode)) 264 | (goto-char 55) 265 | (pm-switch-to-buffer) 266 | (should (eq major-mode 'latex-mode)) 267 | (goto-char 64) 268 | (pm-switch-to-buffer) 269 | (should-not (eq major-mode 'latex-mode)) 270 | (goto-char 66) 271 | (pm-switch-to-buffer) 272 | (should (eq major-mode 'latex-mode)) 273 | (goto-char 84) 274 | (pm-switch-to-buffer) 275 | (should (eq major-mode 'markdown-mode)) 276 | (goto-char 102) 277 | (pm-switch-to-buffer) 278 | (should (eq major-mode 'markdown-mode)) 279 | (goto-char 118) 280 | (pm-switch-to-buffer) 281 | (should (eq major-mode 'latex-mode)) 282 | (goto-char 169) 283 | (pm-switch-to-buffer) 284 | (should (eq major-mode 'pascal-mode))))) 285 | 286 | (ert-deftest poly-markdown/inline-code-in-host-mode () 287 | (pm-test-run-on-string 'poly-markdown-mode 288 | "aaa `non-mode bbb` cccc" 289 | ;; (switch-to-buffer (current-buffer)) 290 | (goto-char 14) 291 | (pm-switch-to-buffer) 292 | (should (eq major-mode 'markdown-mode)) 293 | (should (eq (car (get-text-property (point) 'face)) 294 | 'markdown-inline-code-face)))) 295 | 296 | (ert-deftest poly-markdown/displayed-math-both () 297 | (let ((markdown-enable-math t)) 298 | (pm-test-run-on-string 'poly-markdown-mode 299 | "Some text with 300 | $$\\text{displayed math}$$, formulas \\[E=mc^2\\] 301 | \\[E=mc^2\\] 302 | $$E=mc^2$$, and $343 more $$$ formulas $$$ and $3 303 | \\[ E=mc^2 304 | \\]; 305 | ```pascal 306 | Some none-sense (formula 307 | \\[E=mc^2\\] ) 308 | ```" 309 | ;; (switch-to-buffer (current-buffer)) 310 | (goto-char 18) 311 | (pm-switch-to-buffer) 312 | (should (eq major-mode 'latex-mode)) 313 | (goto-char 42) 314 | (pm-switch-to-buffer) 315 | (should (eq major-mode 'markdown-mode)) 316 | (goto-char 55) 317 | (pm-switch-to-buffer) 318 | (should (eq major-mode 'latex-mode)) 319 | (goto-char 64) 320 | (pm-switch-to-buffer) 321 | (should (eq major-mode 'markdown-mode)) 322 | (goto-char 65) 323 | (pm-switch-to-buffer) 324 | (should (eq major-mode 'latex-mode)) 325 | (goto-char 59) 326 | (pm-switch-to-buffer) 327 | (should (eq major-mode 'latex-mode)) 328 | (goto-char 76) 329 | (pm-switch-to-buffer) 330 | (should (eq major-mode 'latex-mode)) 331 | (goto-char 104) 332 | (pm-switch-to-buffer) 333 | (should (eq major-mode 'markdown-mode)) 334 | (goto-char 122) 335 | (pm-switch-to-buffer) 336 | (should (eq major-mode 'markdown-mode)) 337 | (goto-char 126) 338 | (pm-switch-to-buffer) 339 | (should (eq major-mode 'markdown-mode)) 340 | (goto-char 127) 341 | (pm-switch-to-buffer) 342 | (should (eq major-mode 'latex-mode)) 343 | (goto-char 135) 344 | (pm-switch-to-buffer) 345 | (should (eq major-mode 'latex-mode)) 346 | (goto-char 136) 347 | (pm-switch-to-buffer) 348 | (should (eq major-mode 'markdown-mode)) 349 | (goto-char 178) 350 | (pm-switch-to-buffer) 351 | (should (eq major-mode 'pascal-mode))))) 352 | 353 | (ert-deftest poly-markdown/nested-$-body () 354 | "Test for #6" 355 | (ert-skip "FIXME: failing test") 356 | (oset poly-markdown-inline-math-innermode :allow-nested t) 357 | (pm-test-spans 'poly-markdown-mode 358 | "```python 359 | $ 360 | python-mode-here 361 | ``` 362 | ") 363 | (pm-test-run-on-string 'poly-markdown-mode 364 | "```python 365 | $ 366 | python-mode-here 367 | ``` 368 | " 369 | (switch-to-buffer (current-buffer)) 370 | (goto-char 14) 371 | (pm-switch-to-buffer) 372 | (should (eq major-mode 'python-mode)) 373 | (should (equal (pm-innermost-range 31 'no-cache) 374 | (cons 31 34)))) 375 | (oset poly-markdown-inline-math-innermode :allow-nested nil)) 376 | 377 | (ert-deftest poly-markdown/nested-$-head () 378 | "Test for #6" 379 | (ert-skip "FIXME: failing test") 380 | (oset poly-markdown-inline-math-innermode :allow-nested t) 381 | (pm-test-spans 'poly-markdown-mode 382 | "```{python $ } 383 | python-mode-here 384 | ``` 385 | ") 386 | (pm-test-run-on-string 'poly-markdown-mode 387 | "```{python $ } 388 | python-mode-here 389 | ``` 390 | " 391 | (switch-to-buffer (current-buffer)) 392 | (goto-char 16) 393 | (pm-switch-to-buffer) 394 | (should (eq major-mode 'python-mode)) 395 | (should (equal (pm-innermost-range 16 'no-cache) 396 | (cons 16 33)))) 397 | (oset poly-markdown-inline-math-innermode :allow-nested nil)) 398 | 399 | (ert-deftest poly-markdown/nested-$-general () 400 | "Test for #6" 401 | (ert-skip "FIXME: failing test") 402 | (pm-test-spans 'poly-markdown-mode 403 | "```{python} 404 | sf $sfsfdsfsfd 405 | ``` 406 | 407 | ```{python test1} 408 | python-here 409 | a + 3 $ 410 | python-here 411 | ``` 412 | ") 413 | (pm-test-run-on-string 'poly-markdown-mode 414 | "```{python} 415 | sf $sfsfdsfsfd 416 | ``` 417 | 418 | ```{python test1} 419 | python-here 420 | a + 3 $ 421 | python-here 422 | ``` 423 | " 424 | (switch-to-buffer (current-buffer)) 425 | (pm-switch-to-buffer (goto-char 40)) 426 | (should (or (eq major-mode 'markdown-mode) 427 | (eq major-mode 'poly-head-tail-mode))) 428 | (pm-switch-to-buffer (goto-char 71)) 429 | (should (eq major-mode 'python-mode)))) 430 | 431 | (ert-deftest poly-markdown/yaml-block () 432 | (pm-test-run-on-file poly-markdown-mode "markdown-with-yaml.md" 433 | (goto-char (point-min)) 434 | (pm-switch-to-buffer) 435 | (should (eq major-mode 'markdown-mode)) 436 | (forward-line 1) 437 | (pm-switch-to-buffer) 438 | (should (eq major-mode 'yaml-mode)) 439 | (forward-line 2) 440 | (pm-switch-to-buffer) 441 | (should (eq major-mode 'markdown-mode)))) 442 | 443 | ;; useless :( #163 shows only when `kill-buffer` is called interactively 444 | (ert-deftest poly-markdown/kill-buffer () 445 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 446 | (let ((base-buff (buffer-base-buffer))) 447 | (re-search-forward "defmacro") 448 | (pm-switch-to-buffer) 449 | (let (kill-buffer-query-functions) 450 | (kill-buffer)) 451 | (should-not (buffer-live-p base-buff))))) 452 | 453 | (ert-deftest poly-markdown/indentation () 454 | (pm-test-indentation poly-markdown-mode "markdown.md")) 455 | 456 | (ert-deftest infra/poly-markdown/clone-indirect-buffer () 457 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 458 | 459 | ;; preserve ib in the host mode 460 | (let ((bname "cloned-buffer") 461 | (cbuff (current-buffer)) 462 | (inhibit-message t)) 463 | (goto-char 1) 464 | (clone-indirect-buffer bname t) 465 | (switch-to-buffer bname) 466 | (should (equal bname (buffer-name))) 467 | (should (eq 'markdown-mode major-mode)) 468 | (goto-line 22) ;; fortran location 469 | (should (eq 'markdown-mode major-mode)) 470 | (pm-switch-to-buffer) 471 | (should (eq 'markdown-mode major-mode)) 472 | (should (equal bname (buffer-name))) 473 | (kill-buffer) 474 | (should (buffer-live-p cbuff)) 475 | (switch-to-buffer cbuff)) 476 | 477 | ;; also host from the innermode 478 | (let ((bname "cloned-buffer") 479 | (cbuff (current-buffer)) 480 | (inhibit-message t)) 481 | (goto-line 22) 482 | (pm-switch-to-buffer) 483 | (clone-indirect-buffer bname t) 484 | (switch-to-buffer bname) 485 | (should (equal bname (buffer-name))) 486 | (should (eq 'markdown-mode major-mode)) 487 | (pm-switch-to-buffer) 488 | (should (eq 'markdown-mode major-mode)) 489 | (should (equal bname (buffer-name))) 490 | (kill-buffer) 491 | (should (buffer-live-p cbuff))))) 492 | 493 | (ert-deftest infra/poly-markdown/pm-map-over-modes () 494 | (pm-test-map-over-modes poly-markdown-mode "markdown.md")) 495 | 496 | 497 | (ert-deftest infra/poly-markdown/pm-map-over-modes-regions () 498 | (let ((pm-use-cache t) 499 | (ref-regions '((markdown-mode nil nil 1 391) 500 | (fortran-mode body body 391 872) 501 | (markdown-mode tail nil 872 1043) 502 | (perl-mode body body 1043 1597) 503 | (markdown-mode tail nil 1597 1849) 504 | (emacs-lisp-mode body body 1849 2239) 505 | (markdown-mode tail nil 2239 2368) 506 | (pascal-mode body body 2368 2587) 507 | (markdown-mode tail nil 2587 2897) 508 | (python-mode body body 2897 3502) 509 | (markdown-mode tail nil 3502 3587) 510 | (emacs-lisp-mode body body 3587 4377) 511 | (markdown-mode tail nil 4377 4469) 512 | (sql-mode body body 4469 4532) 513 | (markdown-mode tail nil 4532 4546))) 514 | acc) 515 | 516 | (cl-labels ((sub-ref (beg &optional end) 517 | (setq end (or end (point-max))) 518 | (let ((acc ref-regions) 519 | out) 520 | (while (and acc (< (nth 3 (car acc)) beg)) 521 | (pop acc)) 522 | (while (and acc (< (nth 3 (car acc)) end)) 523 | (push (pop acc) out)) 524 | (reverse out))) 525 | (req (beg end) 526 | (let ((span (pm-innermost-span beg))) 527 | (push (list (pm-span-mode span) (car span) (pm-true-span-type span) 528 | beg end) 529 | acc))) 530 | (run (beg end) 531 | (setq acc nil) 532 | (pm-map-over-modes #'req beg end) 533 | (sort acc (lambda (x y) (< (nth 3 x) (nth 3 y)))))) 534 | 535 | ;; ;; exact span 536 | ;; (pm-test-run-on-file poly-markdown-mode "markdown.md" 537 | ;; (let ((ref '((fortran-mode body body 391 872)))) 538 | ;; (should (equal ref (run 391 872))) 539 | ;; (should (equal ref (run 391 872))) 540 | ;; (let ((pm-use-cache nil)) 541 | ;; (should (equal ref (run 391 872)))))) 542 | 543 | ;; (pm-test-run-on-file poly-markdown-mode "markdown.md" 544 | ;; (let ((ref '((markdown-mode head nil 380 391)))) 545 | ;; (should (equal ref (run 380 391))) 546 | ;; (should (equal ref (run 380 391))) 547 | ;; (let ((pm-use-cache nil)) 548 | ;; (should (equal ref (run 380 391)))))) 549 | 550 | ;; (pm-test-run-on-file poly-markdown-mode "markdown.md" 551 | ;; (let ((ref '((markdown-mode tail nil 872 875)))) 552 | ;; (let ((pm-use-cache nil)) 553 | ;; (should (equal ref (run 872 875)))) 554 | ;; (should (equal ref (run 872 875))))) 555 | 556 | ;; multi spans 557 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 558 | (let ((pm-use-cache nil)) 559 | (should (equal ref-regions (run 1 (point-max))))) 560 | (should (equal ref-regions (run 1 (point-max))))) 561 | 562 | 563 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 564 | (should (equal ref-regions (run 200 (point-max)))) 565 | (let ((pm-use-cache nil)) 566 | (should (equal ref-regions (run 200 (point-max))))) 567 | (should (equal ref-regions (run 200 (point-max))))) 568 | 569 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 570 | (let ((ref (sub-ref 872))) 571 | (let ((pm-use-cache nil)) 572 | (should (equal ref (run 872 (point-max))))) 573 | (should (equal ref (run 872 (point-max)))))) 574 | 575 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 576 | (let ((ref (sub-ref 1843))) 577 | (should (equal ref (run 2221 (point-max)))) 578 | (let ((pm-use-cache nil)) 579 | (should (equal ref (run 2221 (point-max))))) 580 | (should (equal ref (run 2221 (point-max)))))) 581 | 582 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 583 | (let ((ref (sub-ref 2239))) 584 | (let ((pm-use-cache nil)) 585 | (should (equal ref (run 2239 (point-max))))) 586 | (should (equal ref (run 2239 (point-max)))))) 587 | 588 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 589 | (let ((ref '((markdown-mode tail nil 2239 2358)))) 590 | (should (equal ref (run 2239 2256))) 591 | (let ((pm-use-cache nil)) 592 | (should (equal ref (run 2239 2256)))))) 593 | 594 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 595 | (let ((ref '((markdown-mode tail nil 2239 2358)))) 596 | (should (equal ref (run 2239 2347))) 597 | (let ((pm-use-cache nil)) 598 | (should (equal ref (run 2239 2347)))))) 599 | 600 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 601 | (let ((ref (sub-ref 4532))) 602 | (should (equal ref (run 4533 (point-max)))) 603 | (let ((pm-use-cache nil)) 604 | (should (equal ref (run 4533 (point-max))))))) 605 | 606 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 607 | (let ((ref '((markdown-mode tail nil 4532 4546)))) 608 | (should (equal ref (run 4532 (point-max)))) 609 | (let ((pm-use-cache nil)) 610 | (should (equal ref (run 4532 (point-max))))))) 611 | 612 | (pm-test-run-on-file poly-markdown-mode "markdown.md" 613 | (let ((ref (cons '(emacs-lisp-mode body body 3587 4377) 614 | (sub-ref 4369)))) 615 | (should (equal ref (run 4369 (point-max)))) 616 | (let ((pm-use-cache nil)) 617 | (should (equal ref (run 4369 (point-max))))))) 618 | 619 | )) 620 | ) 621 | 622 | 623 | --------------------------------------------------------------------------------