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.

488 lines
16 KiB

  1. ;;; polymode-classes.el --- Core polymode classes -*- lexical-binding: t -*-
  2. ;;
  3. ;; Copyright (C) 2013-2019, Vitalie Spinu
  4. ;; Author: Vitalie Spinu
  5. ;; URL: https://github.com/polymode/polymode
  6. ;;
  7. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. ;;
  9. ;; This file is *NOT* part of GNU Emacs.
  10. ;;
  11. ;; This program is free software; you can redistribute it and/or
  12. ;; modify it under the terms of the GNU General Public License as
  13. ;; published by the Free Software Foundation; either version 3, or
  14. ;; (at your option) any later version.
  15. ;;
  16. ;; This program is distributed in the hope that it will be useful,
  17. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. ;; General Public License for more details.
  20. ;;
  21. ;; You should have received a copy of the GNU General Public License
  22. ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
  23. ;;
  24. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  25. ;;
  26. ;;; Commentary:
  27. ;;
  28. ;;; Code:
  29. (require 'eieio)
  30. (require 'eieio-base)
  31. (require 'eieio-custom)
  32. (defvar pm--object-counter 0)
  33. (defun pm--filter-slots (slots)
  34. (delq nil (mapcar (lambda (slot)
  35. (unless (or (= (elt (symbol-name slot) 0) ?-)
  36. (eq slot 'parent-instance)
  37. (eq slot 'name))
  38. (intern (concat ":" (symbol-name slot)))))
  39. slots)))
  40. (defclass pm-root (eieio-instance-inheritor)
  41. ((name
  42. :initarg :name
  43. :initform "UNNAMED"
  44. :type string
  45. :custom string
  46. :documentation
  47. "Name of the object used to for display and info.")
  48. (-props
  49. :initform '()
  50. :type list
  51. :documentation
  52. "[Internal] Plist used to store various extra metadata such as user history.
  53. Use `pm--prop-get' and `pm--prop-put' to place key value pairs
  54. into this list."))
  55. "Root polymode class.")
  56. (cl-defmethod eieio-object-name-string ((obj pm-root))
  57. (eieio-oref obj 'name))
  58. (cl-defmethod clone ((obj pm-root) &rest params)
  59. (let ((new-obj (cl-call-next-method obj)))
  60. ;; Emacs bug: clone method for eieio-instance-inheritor instantiates all
  61. ;; slots for cloned objects. We want them unbound to allow for the healthy
  62. ;; inheritance.
  63. (pm--complete-clonned-object new-obj obj params)))
  64. (defun pm--complete-clonned-object (new-obj old-obj params)
  65. (let ((old-name (eieio-oref old-obj 'name)))
  66. (when (equal old-name (eieio-oref new-obj 'name))
  67. (let ((new-name (concat old-name ":")))
  68. (eieio-oset new-obj 'name new-name))))
  69. (dolist (descriptor (eieio-class-slots (eieio-object-class old-obj)))
  70. (let ((slot (eieio-slot-descriptor-name descriptor)))
  71. (unless (memq slot '(parent-instance name))
  72. (slot-makeunbound new-obj slot))))
  73. (when params
  74. (shared-initialize new-obj params))
  75. new-obj)
  76. (defun pm--safe-clone (end-class obj &rest params)
  77. "Clone to an object of END-CLASS.
  78. If END-CLASS is same as class of OBJ then just call `clone'.
  79. Otherwise do a bit more work by setting extra slots of the
  80. end-class. PARAMS are passed to clone or constructor functions."
  81. (if (eq end-class (eieio-object-class obj))
  82. (apply #'clone obj params)
  83. (let ((new-obj (pm--complete-clonned-object
  84. (apply end-class params)
  85. obj params)))
  86. (eieio-oset new-obj 'parent-instance obj)
  87. new-obj)))
  88. (defclass pm-polymode (pm-root)
  89. ((hostmode
  90. :initarg :hostmode
  91. :initform nil
  92. :type symbol
  93. :custom symbol
  94. :documentation
  95. "Symbol pointing to a `pm-host-chunkmode' object.
  96. When nil, any host-mode will be matched (suitable for
  97. poly-minor-modes. ")
  98. (innermodes
  99. :initarg :innermodes
  100. :type list
  101. :initform nil
  102. :custom (repeat symbol)
  103. :documentation
  104. "List of inner-mode names (symbols) associated with this polymode.
  105. A special marker :inherit in this list is replaced with the
  106. innermodes of the parent. This allows for a simple way to add
  107. innermodes to the child without explicitly listing all the
  108. innermodes of the parent.")
  109. (exporters
  110. :initarg :exporters
  111. :initform '(pm-exporter/pandoc)
  112. :custom (repeat symbol)
  113. :documentation
  114. "List of names of polymode exporters available for this polymode.")
  115. (exporter
  116. :initarg :exporter
  117. :initform nil
  118. :type symbol
  119. :custom symbol
  120. :documentation
  121. "Current exporter name.
  122. If non-nil should be the name of the default exporter for this
  123. polymode. Can be set with `polymode-set-exporter' command.")
  124. (weavers
  125. :initarg :weavers
  126. :initform '()
  127. :type list
  128. :custom (repeat symbol)
  129. :documentation
  130. "List of names of polymode weavers available for this polymode.")
  131. (weaver
  132. :initarg :weaver
  133. :initform nil
  134. :type symbol
  135. :custom symbol
  136. :documentation
  137. "Current weaver name.
  138. If non-nil this is the default weaver for this polymode. Can be
  139. dynamically set with `polymode-set-weaver'")
  140. (switch-buffer-functions
  141. :initarg :switch-buffer-functions
  142. :initform '()
  143. :type list
  144. :custom (repeat symbol)
  145. :documentation
  146. "List of functions to run at polymode buffer switch.
  147. Each function is run with two arguments, OLD-BUFFER and
  148. NEW-BUFFER.")
  149. (keylist
  150. :initarg :keylist
  151. :initform 'polymode-minor-mode-map
  152. :type (or symbol list)
  153. :custom (choice (symbol :tag "Keymap")
  154. (repeat (cons string symbol)))
  155. :documentation
  156. "A list of elements of the form (KEY . BINDING).
  157. This slot is reserved for building hierarchies through cloning
  158. and should not be used in `define-polymode'.")
  159. (keep-in-mode
  160. :initarg :keep-in-mode
  161. :initform nil
  162. :type symbol
  163. :custom symbol
  164. :documentation
  165. ;; NB: Using major-modes instead of innermode symbols for the sake of
  166. ;; simplicity of the implementation and to allow for auto-modes.
  167. "Major mode to keep in when polymode switches implementation buffers.
  168. When a special symbol 'host, keep in hostmode. The buffer with
  169. this major mode must be installed by one of the innermodes or the
  170. hostmode. If multiple innermodes installed buffers of this mode,
  171. the first buffer is used.")
  172. (-minor-mode
  173. :initform 'polymode-minor-mode
  174. :initarg -minor-mode
  175. :type symbol
  176. :documentation
  177. "[Internal] Symbol pointing to minor-mode function.")
  178. (-hostmode
  179. :type (or null pm-chunkmode)
  180. :documentation
  181. "[Dynamic] Dynamically populated `pm-chunkmode' object.")
  182. (-innermodes
  183. :type list
  184. :initform '()
  185. :documentation
  186. "[Dynamic] List of chunkmodes objects.")
  187. (-auto-innermodes
  188. :type list
  189. :initform '()
  190. :documentation
  191. "[Dynamic] List of auto chunkmodes.")
  192. (-buffers
  193. :initform '()
  194. :type list
  195. :documentation
  196. "[Dynamic] Holds all buffers associated with current buffer."))
  197. "Polymode Configuration object.
  198. Each polymode buffer holds a local variable `pm/polymode'
  199. instantiated from this class or a subclass of this class.")
  200. (defclass pm-chunkmode (pm-root)
  201. ((mode
  202. :initarg :mode
  203. :initform nil
  204. :type symbol
  205. :custom symbol
  206. :documentation
  207. "Emacs major mode for the chunk's body.
  208. If :mode slot is nil (anonymous chunkmodes), use the value of
  209. `polymode-default-inner-mode' is when set, or use the value of
  210. the slot :fallback-mode. A special value 'host means to use the
  211. host mode (useful auto-chunkmodes only).")
  212. (fallback-mode
  213. :initarg :fallback-mode
  214. :initform 'poly-fallback-mode
  215. :type symbol
  216. :custom symbol
  217. :documentation
  218. "Mode to use when mode lookup fails for various reasons. Can
  219. take a special value 'host. Note that, when set,
  220. `polymode-default-inner-mode' takes precedence over this
  221. value.")
  222. (allow-nested
  223. :initarg :allow-nested
  224. :initform t
  225. :type symbol
  226. :custom symbol
  227. :documentation
  228. "Non-nil if other inner-modes are allowed to nest within this
  229. inner-mode.")
  230. (indent-offset
  231. :initarg :indent-offset
  232. :initform 2
  233. :type (or number symbol)
  234. :custom (choice number symbol)
  235. :documentation
  236. "Indentation offset for this mode.
  237. Currently this is only used in +indent and -indent cookies which
  238. when placed on a line cause manual shift in indentation with
  239. respect to how polymode would normally indent a line. Should be
  240. used in cases when indentation of the line is incorrect. Can be a
  241. number, a variable name or a function name to be called with no
  242. arguments.")
  243. (pre-indent-offset
  244. :initarg :pre-indent-offset
  245. :initform 0
  246. :type (or number function)
  247. :custom (choice number function)
  248. :documentation
  249. "Function to compute the offset first line of this chunk.
  250. Offset is relative to how the host mode would indent it. Called
  251. with no-arguments with the point at the begging of the chunk.")
  252. (post-indent-offset
  253. :initarg :post-indent-offset
  254. :initform 0
  255. :type (or number function)
  256. :custom (choice number function)
  257. :documentation
  258. "Function to compute the offset of the following line after this chunk.
  259. Offset is relative to how the host mode would indent it. Called
  260. without arguments with point at the end of the chunk but before
  261. the trailing white spaces if any.")
  262. (protect-indent
  263. :initarg :protect-indent
  264. :initform nil
  265. :type boolean
  266. :custom boolean
  267. :documentation
  268. "Whether to narrowing to current span before indent.")
  269. (protect-font-lock
  270. :initarg :protect-font-lock
  271. :initform nil
  272. :type boolean
  273. :custom boolean
  274. :documentation
  275. "Whether to narrow to span during font lock.")
  276. (protect-syntax
  277. :initarg :protect-syntax
  278. :initform nil
  279. :type boolean
  280. :custom boolean
  281. :documentation
  282. "Whether to narrow to span when calling `syntax-propertize-function'.")
  283. (adjust-face
  284. :initarg :adjust-face
  285. :initform nil
  286. :type (or number face list)
  287. :custom (choice number face sexp)
  288. :documentation
  289. "Fontification adjustment for the body of the chunk.
  290. It should be either, nil, number, face or a list of text
  291. properties as in `put-text-property' specification. If nil or 0
  292. no highlighting occurs. If a face, use that face. If a number, it
  293. is a percentage by which to lighten/darken the default chunk
  294. background. If positive - lighten the background on dark themes
  295. and darken on light thems. If negative - darken in dark thems and
  296. lighten in light thems.")
  297. (init-functions
  298. :initarg :init-functions
  299. :initform '()
  300. :type list
  301. :custom hook
  302. :documentation
  303. "List of functions called after the initialization.
  304. Functions are called with one argument TYPE in the buffer
  305. associated with this chunkmode's span. TYPE is either 'host,
  306. 'head, 'body or 'tail. All init-functions in the inheritance
  307. chain are called in parent-first order. Either customize this
  308. slot or use `object-add-to-list' function.")
  309. (switch-buffer-functions
  310. :initarg :switch-buffer-functions
  311. :initform '()
  312. :type list
  313. :custom hook
  314. :documentation
  315. "List of functions to run at polymode buffer switch.
  316. Each function is run with two arguments, OLD-BUFFER and
  317. NEW-BUFFER. In contrast to identically named slot in
  318. `pm-polymode' class, these functions are run only when NEW-BUFFER
  319. is of this chunkmode.")
  320. (keep-in-mode
  321. :initarg :keep-in-mode
  322. :initform nil
  323. :type symbol
  324. :custom symbol
  325. :documentation
  326. "Major mode to keep in when polymode switches implementation buffers.
  327. When a special symbol 'host, keep in hostmode. The buffer with
  328. this major mode must be installed by one of the innermodes or the
  329. hostmode. If multiple innermodes installed buffers of this mode,
  330. the first buffer is used.")
  331. (-buffer
  332. :type (or null buffer)
  333. :initform nil))
  334. "Generic chunkmode object.
  335. Please note that by default :protect-xyz slots are nil in
  336. hostmodes and t in innermodes.")
  337. (defclass pm-host-chunkmode (pm-chunkmode)
  338. ((allow-nested
  339. ;; currently ignored in code as it doesn't make sense to not allow
  340. ;; innermodes in hosts
  341. :initform 'always))
  342. "This chunkmode doesn't know how to compute spans and takes
  343. over all the other space not claimed by other chunkmodes in the
  344. buffer.")
  345. (defclass pm-inner-chunkmode (pm-chunkmode)
  346. ((protect-font-lock
  347. :initform t)
  348. (protect-syntax
  349. :initform t)
  350. (protect-indent
  351. :initform t)
  352. (body-indent-offset
  353. :initarg :body-indent-offset
  354. :initform 0
  355. :type (or number symbol function)
  356. :custom (choice number symbol)
  357. :documentation
  358. "Indentation offset of the body span relative to the head.
  359. Can be a number, symbol holding a number or a function. When a
  360. function, it is called with no arguments at the beginning of the
  361. body span.")
  362. (can-nest
  363. :initarg :can-nest
  364. :initform nil
  365. :type boolean
  366. :custom boolean
  367. :documentation
  368. "Non-nil if this inner-mode can nest within other inner-modes.
  369. All chunks can nest within the host-mode.")
  370. (can-overlap
  371. :initarg :can-overlap
  372. :initform nil
  373. :type boolean
  374. :custom boolean
  375. :documentation
  376. "Non-nil if chunks of this type can overlap with other chunks of the same type.
  377. See noweb for an example.")
  378. (head-mode
  379. :initarg :head-mode
  380. :initform 'poly-head-tail-mode
  381. :type symbol
  382. :custom symbol
  383. :documentation
  384. "Chunk's head mode.
  385. If set to 'host or 'body use host or body's mode respectively.")
  386. (tail-mode
  387. :initarg :tail-mode
  388. :initform 'poly-head-tail-mode
  389. :type symbol
  390. :custom (choice (const nil :tag "From Head")
  391. function)
  392. :documentation
  393. "Chunk's tail mode.
  394. If set to 'host or 'body use host or body's mode respectively.")
  395. (head-matcher
  396. :initarg :head-matcher
  397. :type (or string cons function)
  398. :custom (choice string (cons string integer) function)
  399. :documentation
  400. "A regexp, a cons (REGEXP . SUB-MATCH) or a function.
  401. When a function, the matcher must accept one argument that can
  402. take either values 1 (forwards search) or -1 (backward search)
  403. and behave similarly to how search is performed by
  404. `re-search-forward' function. This function must return either
  405. nil (no match) or a (cons BEG END) representing the span of the
  406. head or tail respectively. See the code of `pm-fun-matcher' for a
  407. simple example.")
  408. (tail-matcher
  409. :initarg :tail-matcher
  410. :type (or string cons function)
  411. :custom (choice string (cons string integer) function)
  412. :documentation
  413. "A regexp, a cons (REGEXP . SUB-MATCH) or a function.
  414. Like :head-matcher but for the chunk's tail. Currently, it is
  415. always called with the point at the end of the matched head and
  416. with the positive argument (aka match forward).")
  417. (adjust-face
  418. :initform 2)
  419. (head-adjust-face
  420. :initarg :head-adjust-face
  421. :initform 'bold
  422. :type (or number face list)
  423. :custom (choice number face sexp)
  424. :documentation
  425. "Head's face adjustment.
  426. Can be a number, a list of properties or a face.")
  427. (tail-adjust-face
  428. :initarg :tail-adjust-face
  429. :initform nil
  430. :type (or null number face list)
  431. :custom (choice (const :tag "From Head" nil)
  432. number face sexp)
  433. :documentation
  434. "Tail's face adjustment.
  435. A number, a list of properties, a face or nil. When nil, take the
  436. configuration from :head-adjust-face.")
  437. (-head-buffer
  438. :type (or null buffer)
  439. :initform nil
  440. :documentation
  441. "[Internal] This buffer is set automatically to -buffer if
  442. :head-mode is 'body, and to base-buffer if :head-mode is 'host.")
  443. (-tail-buffer
  444. :initform nil
  445. :type (or null buffer)
  446. :documentation
  447. "[Internal] Same as -head-buffer, but for tail span."))
  448. "Inner-chunkmodes represent innermodes (or sub-modes) within a
  449. buffer. Chunks are commonly delimited by head and tail markup but
  450. can be delimited by some other logic (e.g. indentation). In the
  451. latter case, heads or tails have zero length and are not
  452. physically present in the buffer.")
  453. (defclass pm-inner-auto-chunkmode (pm-inner-chunkmode)
  454. ((mode-matcher
  455. :initarg :mode-matcher
  456. :type (or string cons function)
  457. :custom (choice string (cons string integer) function)
  458. :documentation
  459. "Matcher used to retrieve the mode's symbol from the chunk's head.
  460. Can be either a regexp string, cons of the form (REGEXP .
  461. SUBEXPR) or a function to be called with no arguments. If a
  462. function, it must return a string name of the mode. Function is
  463. called at the beginning of the head span."))
  464. "Inner chunkmodes with unknown (at definition time) mode of the
  465. body span. The body mode is determined dynamically by retrieving
  466. the name with the :mode-matcher.")
  467. (provide 'polymode-classes)
  468. ;;; polymode-classes.el ends here