Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

688 lines
27 KiB

  1. ;;; polymode.el --- Extensible framework for multiple major modes -*- lexical-binding: t -*-
  2. ;;
  3. ;; Author: Vitalie Spinu
  4. ;; Maintainer: Vitalie Spinu
  5. ;; Copyright (C) 2013-2019, Vitalie Spinu
  6. ;; Version: 0.2.2
  7. ;; Package-Requires: ((emacs "25"))
  8. ;; URL: https://github.com/polymode/polymode
  9. ;; Keywords: languages, multi-modes, processes
  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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
  27. ;;
  28. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  29. ;;
  30. ;;; Commentary:
  31. ;;
  32. ;; Documentation at https://polymode.github.io
  33. ;;
  34. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  35. ;;
  36. ;;; Code:
  37. (require 'polymode-core)
  38. (require 'polymode-classes)
  39. (require 'polymode-methods)
  40. (require 'polymode-compat)
  41. (require 'polymode-export)
  42. (require 'polymode-weave)
  43. (require 'polymode-base)
  44. (require 'poly-lock)
  45. (require 'easymenu)
  46. (require 'derived)
  47. (defvar polymode-prefix-key nil
  48. "[Obsoleted] Prefix key for the polymode mode keymap.
  49. Not effective after loading the polymode library.")
  50. (make-obsolete-variable 'polymode-prefix-key "Unbind in `polymode-mode-map'" "v0.1.6")
  51. (defvar polymode-map
  52. (let ((map (define-prefix-command 'polymode-map)))
  53. ;; eval
  54. (define-key map "v" 'polymode-eval-map)
  55. ;; navigation
  56. (define-key map "\C-n" 'polymode-next-chunk)
  57. (define-key map "\C-p" 'polymode-previous-chunk)
  58. (define-key map "\C-\M-n" 'polymode-next-chunk-same-type)
  59. (define-key map "\C-\M-p" 'polymode-previous-chunk-same-type)
  60. ;; chunk manipulation
  61. (define-key map "\M-k" 'polymode-kill-chunk)
  62. (define-key map "\M-m" 'polymode-mark-or-extend-chunk)
  63. (define-key map "\C-t" 'polymode-toggle-chunk-narrowing)
  64. ;; backends
  65. (define-key map "e" 'polymode-export)
  66. (define-key map "E" 'polymode-set-exporter)
  67. (define-key map "w" 'polymode-weave)
  68. (define-key map "W" 'polymode-set-weaver)
  69. (define-key map "t" 'polymode-tangle)
  70. (define-key map "T" 'polymode-set-tangler)
  71. (define-key map "$" 'polymode-show-process-buffer)
  72. map)
  73. "Polymode prefix map.
  74. Lives on `polymode-prefix-key' in polymode buffers.")
  75. (defvaralias 'polymode-mode-map 'polymode-minor-mode-map)
  76. (defvar polymode-minor-mode-map
  77. (let ((map (make-sparse-keymap)))
  78. (define-key map (or polymode-prefix-key "\M-n") 'polymode-map)
  79. map)
  80. "The minor mode keymap which is inherited by all polymodes.")
  81. (easy-menu-define polymode-menu polymode-minor-mode-map
  82. "Menu for polymode."
  83. '("Polymode"
  84. ["Next chunk" polymode-next-chunk]
  85. ["Previous chunk" polymode-previous-chunk]
  86. ["Next chunk same type" polymode-next-chunk-same-type]
  87. ["Previous chunk same type" polymode-previous-chunk-same-type]
  88. ["Mark or extend chunk" polymode-mark-or-extend-chunk]
  89. ["Kill chunk" polymode-kill-chunk]
  90. "--"
  91. ["Weave" polymode-weave]
  92. ["Set Weaver" polymode-set-weaver]
  93. "--"
  94. ["Export" polymode-export]
  95. ["Set Exporter" polymode-set-exporter]))
  96. ;;; NAVIGATION
  97. (defun polymode-next-chunk (&optional N)
  98. "Go N chunks forwards.
  99. Return the number of actually moved over chunks. This command is
  100. a \"cycling\" command (see `polymode-next-chunk-same-type' for an
  101. example)."
  102. (interactive "p")
  103. (pm-goto-span-of-type '(nil body) N)
  104. ;; If head/tail end before eol we move to the next line
  105. (when (looking-at "\\s *$")
  106. (forward-line 1))
  107. (pm--set-transient-map (list #'polymode-previous-chunk
  108. #'polymode-next-chunk)))
  109. ;;fixme: problme with long chunks .. point is recentered
  110. ;;todo: merge into next-chunk
  111. (defun polymode-previous-chunk (&optional N)
  112. "Go N chunks backwards.
  113. This command is a \"cycling\" command (see
  114. `polymode-next-chunk-same-type' for an example). Return the
  115. number of chunks jumped over."
  116. (interactive "p")
  117. (polymode-next-chunk (- N)))
  118. (defun polymode-next-chunk-same-type (&optional N)
  119. "Go to next N chunk.
  120. Return the number of chunks of the same type moved over. This
  121. command is a \"cycling\" command in the sense that you can repeat
  122. the basic key without the prefix multiple times to invoke the
  123. command multiple times."
  124. (interactive "p")
  125. (let* ((sofar 0)
  126. (back (< N 0))
  127. (beg (if back (point-min) (point)))
  128. (end (if back (point) (point-max)))
  129. (N (if back (- N) N))
  130. (orig-pos (point))
  131. (pos (point))
  132. this-type this-name)
  133. (condition-case-unless-debug nil
  134. (pm-map-over-spans
  135. (lambda (span)
  136. (unless (memq (car span) '(head tail))
  137. (when (and (equal this-name
  138. (eieio-object-name-string (nth 3 span)))
  139. (eq this-type (car span)))
  140. (setq pos (nth 1 span))
  141. (setq sofar (1+ sofar)))
  142. (unless this-name
  143. (setq this-name (eieio-object-name-string (nth 3 span))
  144. this-type (car span)))
  145. (when (>= sofar N)
  146. (signal 'quit nil))))
  147. beg end nil back)
  148. (quit (when (looking-at "\\s *$")
  149. (forward-line))))
  150. (goto-char pos)
  151. (when (or (eobp) (bobp) (eq pos orig-pos))
  152. (message "No more chunks of type %s" this-name)
  153. (ding))
  154. (pm--set-transient-map (list #'polymode-previous-chunk-same-type
  155. #'polymode-next-chunk-same-type))
  156. sofar))
  157. (defun polymode-previous-chunk-same-type (&optional N)
  158. "Go to previous N chunk.
  159. Return the number of chunks of the same type moved over."
  160. (interactive "p")
  161. (polymode-next-chunk-same-type (- N)))
  162. ;;; KILL and NARROWING
  163. (defun pm--kill-span (types)
  164. (let ((span (pm-innermost-span)))
  165. (when (memq (car span) types)
  166. (delete-region (nth 1 span) (nth 2 span)))))
  167. (defun polymode-kill-chunk ()
  168. "Kill current chunk."
  169. (interactive)
  170. (pcase (pm-innermost-span)
  171. (`(,(or `nil `host) ,beg ,end ,_) (delete-region beg end))
  172. (`(body ,beg ,_ ,_)
  173. (goto-char beg)
  174. (pm--kill-span '(body))
  175. (pm--kill-span '(head tail))
  176. (pm--kill-span '(head tail)))
  177. (`(tail ,beg ,end ,_)
  178. (if (eq beg (point-min))
  179. (delete-region beg end)
  180. (goto-char (1- beg))
  181. (polymode-kill-chunk)))
  182. (`(head ,_ ,end ,_)
  183. (goto-char end)
  184. (polymode-kill-chunk))
  185. (_ (error "Canoot find chunk to kill"))))
  186. (defun polymode-toggle-chunk-narrowing ()
  187. "Toggle narrowing of the body of current chunk."
  188. (interactive)
  189. (if (buffer-narrowed-p)
  190. (progn (widen) (recenter))
  191. (pcase (pm-innermost-span)
  192. (`(head ,_ ,end ,_)
  193. (goto-char end)
  194. (pm-narrow-to-span))
  195. (`(tail ,beg ,_ ,_)
  196. (if (eq beg (point-min))
  197. (error "Invalid chunk")
  198. (goto-char (1- beg))
  199. (pm-narrow-to-span)))
  200. (_ (pm-narrow-to-span)))))
  201. (defun pm-chunk-range (&optional pos)
  202. "Return a range (BEG . END) for a chunk at POS."
  203. (setq pos (or pos (point)))
  204. (let ((span (pm-innermost-span pos))
  205. (pmin (point-min))
  206. (pmax (point-max)))
  207. (cl-case (car span)
  208. ((nil) (pm-span-to-range span))
  209. (body (cons (if (= pmin (nth 1 span))
  210. pmin
  211. (nth 1 (pm-innermost-span (1- (nth 1 span)))))
  212. (if (= pmax (nth 2 span))
  213. pmax
  214. (nth 2 (pm-innermost-span (nth 2 span))))))
  215. (head (if (= pmax (nth 2 span))
  216. (pm-span-to-range span)
  217. (pm-chunk-range (nth 2 span))))
  218. (tail (if (= pmin (nth 1 span))
  219. (pm-span-to-range span)
  220. (pm-chunk-range (1- (nth 1 span))))))))
  221. (defun polymode-mark-or-extend-chunk ()
  222. "DWIM command to repeatedly mark chunk or extend region.
  223. When no region is active, mark the current span if in body of a
  224. chunk or the whole chunk if in head or tail. On repeated
  225. invocation extend the region either forward or backward. You need
  226. not use the prefix key on repeated invocation. For example
  227. assuming we are in the body of the inner chunk and this command
  228. is bound on M\\=-n M\\=-m (the default)
  229. [M\\=-n M\\=-m M\\=-m M\\=-m] selects body, expand selection to chunk then
  230. expand selection to previous chunk
  231. [M\\=-n M\\=-m C\\=-x C\\=-x M\\=-m] selects body, expand selection to chunk,
  232. then reverse point and mark, then extend the
  233. selection to the following chunk"
  234. (interactive)
  235. (let ((span (pm-innermost-span)))
  236. (if (region-active-p)
  237. (if (< (mark) (point))
  238. ;; forward extension
  239. (if (eobp)
  240. (user-error "End of buffer")
  241. (if (eq (car span) 'head)
  242. (goto-char (cdr (pm-chunk-range)))
  243. (goto-char (nth 2 span))
  244. ;; special dwim when extending from body
  245. (when (and (eq (car span) 'tail)
  246. (not (= (point-min) (nth 1 span))))
  247. (let ((body-span (pm-innermost-span (1- (nth 1 span)))))
  248. (when (and (= (nth 1 body-span) (mark))
  249. (not (= (nth 1 body-span) (point-min))))
  250. (let ((head-span (pm-innermost-span (1- (nth 1 body-span)))))
  251. (when (eq (car head-span) 'head)
  252. (set-mark (nth 1 head-span)))))))))
  253. ;; backward extension
  254. (if (bobp)
  255. (user-error "Beginning of buffer")
  256. (goto-char (car (if (= (point) (nth 1 span))
  257. (pm-chunk-range (1- (point)))
  258. (pm-chunk-range (point)))))
  259. ;; special dwim when extending from body
  260. (when (and (eq (car span) 'body)
  261. (= (nth 2 span) (mark)))
  262. (let ((tail-span (pm-innermost-span (nth 2 span))))
  263. (when (eq (car tail-span) 'tail)
  264. (set-mark (nth 2 tail-span)))))))
  265. (let ((range (if (memq (car span) '(nil body))
  266. (pm-span-to-range span)
  267. (pm-chunk-range))))
  268. (set-mark (cdr range))
  269. (goto-char (car range)))))
  270. (let ((map (make-sparse-keymap)))
  271. (define-key map (vector last-command-event) #'polymode-mark-or-extend-chunk)
  272. (define-key map (car (where-is-internal #'exchange-point-and-mark)) #'exchange-point-and-mark)
  273. (let ((ev (event-basic-type last-command-event)))
  274. (define-key map (vector ev) #'polymode-mark-or-extend-chunk))
  275. (set-transient-map map (lambda () (eq this-command 'exchange-point-and-mark)))))
  276. (defun polymode-show-process-buffer ()
  277. "Show the process buffer used by weaving and exporting programs."
  278. (interactive)
  279. (let ((buf (cl-loop for b being the buffers
  280. if (buffer-local-value 'pm--process-buffer b)
  281. return b)))
  282. (if buf
  283. (pop-to-buffer buf `(nil . ((inhibit-same-window . ,pop-up-windows))))
  284. (message "No polymode process buffers found."))))
  285. ;;; EVALUATION
  286. (defvar polymode-eval-map
  287. (let (polymode-eval-map)
  288. (define-prefix-command 'polymode-eval-map)
  289. (define-key polymode-eval-map "v" #'polymode-eval-region-or-chunk)
  290. (define-key polymode-eval-map "b" #'polymode-eval-buffer)
  291. (define-key polymode-eval-map "u" #'polymode-eval-buffer-from-beg-to-point)
  292. (define-key polymode-eval-map "d" #'polymode-eval-buffer-from-point-to-end)
  293. (define-key polymode-eval-map (kbd "<up>") #'polymode-eval-buffer-from-beg-to-point)
  294. (define-key polymode-eval-map (kbd "<down>") #'polymode-eval-buffer-from-point-to-end)
  295. polymode-eval-map)
  296. "Keymap for polymode evaluation commands.")
  297. (defvar-local polymode-eval-region-function nil
  298. "Function taking three arguments which does mode specific evaluation.
  299. First two arguments are BEG and END of the region. The third
  300. argument is the message describing the evaluation type. If the
  301. value of this variable is non-nil in the host mode then all inner
  302. spans are evaluated within the host buffer and values of this
  303. variable for the inner modes are ignored.")
  304. (defun polymode-eval-region (beg end &optional msg)
  305. "Eval all spans within region defined by BEG and END.
  306. MSG is a message to be passed to `polymode-eval-region-function';
  307. defaults to \"Eval region\"."
  308. (interactive "r")
  309. (save-excursion
  310. (let* ((base (pm-base-buffer))
  311. (host-fun (buffer-local-value 'polymode-eval-region-function base))
  312. (msg (or msg "Eval region"))
  313. evalled mapped)
  314. (if host-fun
  315. (pm-map-over-spans
  316. (lambda (span)
  317. (when (eq (car span) 'body)
  318. (with-current-buffer base
  319. (funcall host-fun (max beg (nth 1 span)) (min end (nth 2 span)) msg))))
  320. beg end)
  321. (pm-map-over-spans
  322. (lambda (span)
  323. (when (eq (car span) 'body)
  324. (setq mapped t)
  325. (when polymode-eval-region-function
  326. (setq evalled t)
  327. (funcall polymode-eval-region-function
  328. (max beg (nth 1 span))
  329. (min end (nth 2 span))
  330. msg))))
  331. beg end)
  332. (unless mapped
  333. (user-error "No inner spans in the region"))
  334. (unless evalled
  335. (user-error "None of the inner spans have `polymode-eval-region-function' defined"))))))
  336. (defun polymode-eval-chunk (span-or-pos &optional no-error)
  337. "Eval the body span of the inner chunk at point.
  338. SPAN-OR-POS is either a span or a point. When NO-ERROR is
  339. non-nil, don't throw if `polymode-eval-region-function' is nil."
  340. (interactive "d")
  341. (let* ((span (if (number-or-marker-p span-or-pos)
  342. (pm-innermost-span span-or-pos)
  343. span-or-pos))
  344. (body-span (pcase (car span)
  345. ('head (pm-innermost-span (nth 2 span)))
  346. ('tail (pm-innermost-span (1- (nth 1 span))))
  347. ('body span)
  348. (_ (user-error "Not in an inner chunk"))))
  349. (base (pm-base-buffer))
  350. (host-fun (buffer-local-value 'polymode-eval-region-function base))
  351. (msg "Eval chunk"))
  352. (save-excursion
  353. (pm-set-buffer body-span)
  354. (if host-fun
  355. (with-current-buffer base
  356. (funcall host-fun (nth 1 body-span) (nth 2 body-span) msg))
  357. (if polymode-eval-region-function
  358. (funcall polymode-eval-region-function (nth 1 body-span) (nth 2 body-span) msg)
  359. (unless no-error
  360. (error "Undefined `polymode-eval-region-function' in buffer %s" (current-buffer))))))))
  361. (defun polymode-eval-region-or-chunk ()
  362. "Eval all inner chunks in region if active, or current chunk otherwise."
  363. (interactive)
  364. (if (use-region-p)
  365. (polymode-eval-region (region-beginning) (region-end))
  366. (polymode-eval-chunk (point))))
  367. (defun polymode-eval-buffer ()
  368. "Eval all inner chunks in the buffer."
  369. (interactive)
  370. (polymode-eval-region (point-min) (point-max) "Eval buffer"))
  371. (defun polymode-eval-buffer-from-beg-to-point ()
  372. "Eval all inner chunks from beginning of buffer till point."
  373. (interactive)
  374. (polymode-eval-region (point-min) (point) "Eval buffer till point"))
  375. (defun polymode-eval-buffer-from-point-to-end ()
  376. "Eval all inner chunks from point to the end of buffer."
  377. (interactive)
  378. (polymode-eval-region (point) (point-max) "Eval buffer till end"))
  379. ;;; DEFINE
  380. (defun pm--config-name (symbol &optional must-exist)
  381. (let* ((poly-name (replace-regexp-in-string "pm-poly/\\|poly-\\|-mode\\|-polymode\\|-minor-mode" ""
  382. (symbol-name symbol)))
  383. (config-name
  384. (if (and (boundp symbol)
  385. (symbol-value symbol)
  386. (object-of-class-p (symbol-value symbol) 'pm-polymode))
  387. symbol
  388. (intern (concat "poly-" poly-name "-polymode")))))
  389. (when must-exist
  390. (unless (boundp config-name)
  391. (let ((old-config-name (intern (concat "pm-poly/" poly-name))))
  392. (if (boundp old-config-name)
  393. (setq config-name old-config-name)
  394. (error "No pm-polymode config object with name `%s'" config-name))))
  395. (unless (object-of-class-p (symbol-value config-name) 'pm-polymode)
  396. (error "`%s' is not a `pm-polymode' config object" config-name)))
  397. config-name))
  398. (defun pm--get-keylist.keymap-from-parent (keymap parent-conf)
  399. (let ((keylist (copy-sequence keymap))
  400. (pi parent-conf)
  401. (parent-map))
  402. (while pi
  403. (let ((map (and (slot-boundp pi :keylist)
  404. (eieio-oref pi 'keylist))))
  405. (when map
  406. (if (and (symbolp map)
  407. (keymapp (symbol-value map)))
  408. ;; if one of the parent's :keylist is a keymap, use it as our
  409. ;; parent-map and stop further descent
  410. (setq parent-map map
  411. pi nil)
  412. ;; list, descend to next parent and append the key list to keylist
  413. (setq pi (and (slot-boundp pi :parent-instance)
  414. (eieio-oref pi 'parent-instance))
  415. keylist (append map keylist))))))
  416. (when (and parent-map (symbolp parent-map))
  417. (setq parent-map (symbol-value parent-map)))
  418. (cons (reverse keylist)
  419. (or parent-map polymode-minor-mode-map))))
  420. ;;;###autoload
  421. (defmacro define-polymode (mode &optional parent doc &rest body)
  422. "Define a new polymode MODE.
  423. This macro defines command MODE and an indicator variable MODE
  424. which becomes t when MODE is active and nil otherwise.
  425. MODE command can be used as both major and minor mode. Using
  426. polymodes as minor modes makes sense when :hostmode (see below)
  427. is not specified, in which case polymode installs only inner
  428. modes and doesn't touch current major mode.
  429. Standard hook MODE-hook is run at the end of the initialization
  430. of each polymode buffer (both indirect and base buffers).
  431. This macro also defines the MODE-map keymap from the :keymap
  432. argument and PARENT-map (see below) and poly-[MODE-NAME]-polymode
  433. variable which holds an object of class `pm-polymode' which holds
  434. the entire configuration for this polymode.
  435. PARENT is either the polymode configuration object or a polymode
  436. mode (there is 1-to-1 correspondence between config
  437. objects (`pm-polymode') and mode functions). The new polymode
  438. MODE inherits alll the behavior from PARENT except for the
  439. overwrites specified by the keywords (see below). The new MODE
  440. runs all the hooks from the PARENT-mode and inherits its MODE-map
  441. from PARENT-map.
  442. DOC is an optional documentation string. If present PARENT must
  443. be provided, but can be nil.
  444. BODY is executed after the complete initialization of the
  445. polymode but before MODE-hook. It is executed once for each
  446. polymode buffer - host buffer on initialization and every inner
  447. buffer subsequently created.
  448. Before the BODY code keyword arguments (i.e. alternating keywords
  449. and values) are allowed. The following special keywords
  450. controlling the behavior of the new MODE are supported:
  451. :lighter Optional LIGHTER is displayed in the mode line when the
  452. mode is on. If omitted, it defaults to the :lighter slot of
  453. CONFIG object.
  454. :keymap If nil, a new MODE-map keymap is created what directly
  455. inherits from the PARENT's keymap. The last keymap in the
  456. inheritance chain is always `polymode-minor-mode-map'. If a
  457. keymap it is used directly as it is. If a list of binding of
  458. the form (KEY . BINDING) it is merged the bindings are added to
  459. the newly create keymap.
  460. :after-hook A single form which is evaluated after the mode hooks
  461. have been run. It should not be quoted.
  462. Other keywords are added to the `pm-polymode' configuration
  463. object and should be valid slots in PARENT config object or the
  464. root config `pm-polymode' object if PARENT is nil. By far the
  465. most frequently used slots are:
  466. :hostmode Symbol pointing to a `pm-host-chunkmode' object
  467. specifying the behavior of the hostmode. If missing or nil,
  468. MODE will behave as a minor-mode in the sense that it will
  469. reuse the currently installed major mode and will install only
  470. the inner modes.
  471. :innermodes List of symbols pointing to `pm-inner-chunkmode'
  472. objects which specify the behavior of inner modes (or submodes)."
  473. (declare
  474. (doc-string 3)
  475. (debug (&define name
  476. [&optional [&not keywordp] name]
  477. [&optional stringp]
  478. [&rest [keywordp sexp]]
  479. def-body)))
  480. (let* ((last-message (make-symbol "last-message"))
  481. (mode-name (symbol-name mode))
  482. (config-name (pm--config-name mode))
  483. (root-name (replace-regexp-in-string "poly-\\|-mode" "" mode-name))
  484. (keymap-name (intern (concat mode-name "-map")))
  485. keymap keylist slots after-hook keyw lighter)
  486. (if (keywordp parent)
  487. (progn
  488. (push doc body)
  489. (push parent body)
  490. (setq doc nil
  491. parent nil))
  492. (unless (stringp doc)
  493. (push doc body)
  494. (setq doc (format "Polymode for %s." root-name))))
  495. (unless (symbolp parent)
  496. (error "PARENT must be a name of a `pm-polymode' config or a polymode mode function"))
  497. ;; Check keys
  498. (while (keywordp (setq keyw (car body)))
  499. (setq body (cdr body))
  500. (pcase keyw
  501. (:lighter (setq lighter (purecopy (pop body))))
  502. (:keymap (setq keymap (pop body)))
  503. (:after-hook (setq after-hook (pop body)))
  504. (:keylist (setq keylist (pop body)))
  505. (_ (push (pop body) slots) (push keyw slots))))
  506. `(progn
  507. ;; Define the variable to enable or disable the mode.
  508. (defvar-local ,mode nil ,(format "Non-nil if `%s' polymode is enabled." mode))
  509. (let* ((parent ',parent)
  510. (keymap ,keymap)
  511. (keylist ,keylist)
  512. (parent-conf-name (and parent (pm--config-name parent 'must-exist)))
  513. (parent-conf (and parent-conf-name (symbol-value parent-conf-name))))
  514. ;; define the minor-mode's keymap
  515. (makunbound ',keymap-name)
  516. (defvar ,keymap-name
  517. (if (keymapp keymap)
  518. keymap
  519. (let ((parent-map (unless (keymapp keymap)
  520. ;; keymap is either nil or a list
  521. (cond
  522. ;; 1. if parent is config object, merge all list
  523. ;; keymaps from parents
  524. ((eieio-object-p (symbol-value parent))
  525. (let ((klist.kmap (pm--get-keylist.keymap-from-parent
  526. keymap (symbol-value parent))))
  527. (setq keymap (append keylist (car klist.kmap)))
  528. (cdr klist.kmap)))
  529. ;; 2. If parent is polymode function, take the
  530. ;; minor-mode from the parent config
  531. (parent
  532. (symbol-value
  533. (derived-mode-map-name
  534. (eieio-oref parent-conf '-minor-mode))))
  535. ;; 3. nil
  536. (t polymode-minor-mode-map)))))
  537. (easy-mmode-define-keymap keymap nil nil (list :inherit parent-map))))
  538. ,(format "Keymap for %s." mode-name))
  539. ,@(unless (eq parent config-name)
  540. `((makunbound ',config-name)
  541. (defvar ,config-name
  542. (if parent-conf-name
  543. (clone parent-conf
  544. :name ,(symbol-name config-name)
  545. '-minor-mode ',mode
  546. ,@slots)
  547. (pm-polymode :name ,(symbol-name config-name)
  548. '-minor-mode ',mode
  549. ,@slots))
  550. ,(format "Configuration object for `%s' polymode." mode))))
  551. ;; The actual mode function:
  552. (defun ,mode (&optional arg)
  553. ,(format "%s\n\n\\{%s}"
  554. ;; fixme: add inheretance info here and warning if body is
  555. ;; non-nil (like in define-mirror-mode)
  556. doc keymap-name)
  557. (interactive)
  558. (let ((,last-message (current-message))
  559. (state (cond
  560. ((numberp arg) (> arg 0))
  561. (arg t)
  562. ((not ,mode)))))
  563. (setq ,mode state)
  564. (if state
  565. (unless (buffer-base-buffer)
  566. ;; Call in host (base) buffers only.
  567. (when ,mode
  568. (let ((obj (clone ,config-name)))
  569. ;; (eieio-oset obj '-minor-mode ',mode)
  570. (pm-initialize obj))
  571. ;; when host mode is reset in pm-initialize we end up with new
  572. ;; minor mode in hosts
  573. (setq ,mode t)))
  574. (let ((base (pm-base-buffer)))
  575. (pm-turn-polymode-off t)
  576. (switch-to-buffer base)))
  577. ;; `body` and `hooks` are executed in all buffers; pm/polymode has been set
  578. ,@body
  579. (when state
  580. (pm--run-derived-mode-hooks)
  581. ,@(when after-hook `(,after-hook)))
  582. (unless (buffer-base-buffer)
  583. ;; Avoid overwriting a message shown by the body,
  584. ;; but do overwrite previous messages.
  585. (when (and (called-interactively-p 'any)
  586. (or (null (current-message))
  587. (not (equal ,last-message
  588. (current-message)))))
  589. (message ,(concat root-name " polymode %s")
  590. (if state "enabled" "disabled"))))
  591. (force-mode-line-update))
  592. ;; Return the new state
  593. ,mode)
  594. (add-minor-mode ',mode ,(or lighter " PM") ,keymap-name)))))
  595. (define-minor-mode polymode-minor-mode
  596. "Polymode minor mode, used to make everything work."
  597. nil " PM")
  598. (define-derived-mode poly-head-tail-mode prog-mode "HeadTail"
  599. "Default major mode for polymode head and tail spans."
  600. (let ((base (pm-base-buffer)))
  601. ;; (#119) hideshow needs comment regexp and throws if not found. We are
  602. ;; using these values from the host mode which should have been installed
  603. ;; already.
  604. (setq-local comment-start (buffer-local-value 'comment-start base))
  605. (setq-local comment-end (buffer-local-value 'comment-end base))))
  606. (define-derived-mode poly-fallback-mode prog-mode "FallBack"
  607. ;; fixme:
  608. ;; 1. doesn't work as fallback for hostmode
  609. ;; 2. highlighting is lost (Rnw with inner fallback)
  610. "Default major mode for modes which were not found.
  611. This is better than fundamental-mode because it allows running
  612. globalized minor modes and can run user hooks.")
  613. ;; indulge elisp font-lock (FIXME: check if this is needed; why host/inner defs work?)
  614. (dolist (mode '(emacs-lisp-mode lisp-interaction-mode))
  615. (font-lock-add-keywords
  616. mode
  617. '(("(\\(define-polymode\\)\\s +\\(\\(\\w\\|\\s_\\)+\\)"
  618. (1 font-lock-keyword-face)
  619. (2 font-lock-variable-name-face)))))
  620. (provide 'polymode)
  621. ;;; polymode.el ends here