Klimi's new dotfiles with stow.
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.

392 lines
18 KiB

5 years ago
  1. ;;; ess-noweb-font-lock-mode.el --- edit noweb files with GNU Emacs
  2. ;; Copyright (C) 1999 by Adnan Yaqub (AYaqub@orga.com)
  3. ;; and Mark Lunt (mark.lunt@mrc-bsu.cam.ac.uk
  4. ;; Copyright (C) 2002 by A.J. Rossini <rossini@u.washington.edu>
  5. ;; Copyright (C) 2003--2004 A.J. Rossini, Richard M. Heiberger, Martin
  6. ;; Maechler, Kurt Hornik, Rodney Sparapani, and Stephen Eglen.
  7. ;; Maintainer: ESS-core <ESS-core@r-project.org>
  8. ;; This program is free software; you can redistribute it and/or modify
  9. ;; it under the terms of the GNU General Public License as published by
  10. ;; the Free Software Foundation; either version 2, or (at your option)
  11. ;; any later version.
  12. ;;
  13. ;; This program is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;;
  18. ;; A copy of the GNU General Public License is available at
  19. ;; https://www.r-project.org/Licenses/
  20. ;;
  21. ;;; Commentary:
  22. ;; Code-dependent highlighting
  23. ;; *****
  24. ;;
  25. ;; Adding highlighting to ess-noweb-mode.el
  26. ;;
  27. ;; Here is a description of how one can add highlighting via the
  28. ;; font-lock package to noweb buffers. It uses the hooks provided by
  29. ;; ess-noweb-mode.el. The solution provides the following features:
  30. ;; 1) The documentation chunks are highlighted in the ess-noweb-doc-mode
  31. ;; (e.g., LaTeX).
  32. ;; 2) The code chunks without mode comments (-*- mode -*-) are
  33. ;; highlighted in the ess-noweb-code-mode.
  34. ;; 3) The code chunks with mode comments (-*- mode -*-) on the first
  35. ;; line of the first chunk with this name are highlighted in the mode
  36. ;; in the comment.
  37. ;;
  38. ;; For example, given the file:
  39. ;;
  40. ;; % -*- mode: Noweb; ess-noweb-code-mode: c-mode -*-
  41. ;;
  42. ;; \begin{itemize}
  43. ;; \item a main routine written in C,
  44. ;; \item a log configuration file parser written in YACC, and
  45. ;; \item a lexical analyzer written in Lex.
  46. ;; \end{itemize}
  47. ;;
  48. ;; <<warning c comment>>=
  49. ;; /* DO NOT EDIT ME! */
  50. ;; /* This file was automatically generated from %W% (%G%). */
  51. ;; @
  52. ;;
  53. ;; <<warning nroff comment>>=
  54. ;; .\" -*- nroff -*-
  55. ;; .\" DO NOT EDIT ME!
  56. ;; .\" This file was automatically generated from %W% (%G%).
  57. ;; @
  58. ;;
  59. ;; The LaTeX list is highlighted in latex-mode (the default noweb doc
  60. ;; mode), the chunk <<warning c comment>> is highlighted in c-mode (the
  61. ;; default noweb code mode), and the chunk <<warning nroff comment>> is
  62. ;; highlighted in nroff-mode due to the "-*- nroff -*-" comment.
  63. ;;
  64. ;; Chunks are highlighted each time point moves into them from a
  65. ;; different mode. They are also fontified 'on the fly', but this is
  66. ;; less reliable, since the syntax can depend on the context. It's as
  67. ;; good as you would get outside ess-noweb-mode, though.
  68. ;;
  69. ;; To use it, you must add
  70. ;; (require 'ess-noweb-font-lock-mode) to your .emacs file.
  71. ;; Then, if you use either global-font-lock or turn-on-font-lock
  72. ;; statements, any ess-noweb-mode buffers will be fontified
  73. ;; appropriately. (We have to redefine turn-on-font-lock, but it
  74. ;; saves breaking other packages (in particular ESS, which I use a
  75. ;; lot), that assume that turn-on-font-lock is the way to turn on
  76. ;; font locking.
  77. ;; Alternatively, you can turn ess-noweb-font-lock-mode on and off by
  78. ;; using M-x ess-noweb-font-lock-mode. However, turning
  79. ;; ess-noweb-font-lock-mode off when global-font-lock-mode is t makes it
  80. ;; impossible to use font-locking in that buffer subsequently, other
  81. ;; than by turning ess-noweb-font-lock-mode back on.
  82. ;; 2) The highlighting sometimes get confused, but this is no longer
  83. ;; a noweb problem. Highlighting should work as well within a chunk
  84. ;; as it does without ess-noweb-mode.
  85. ;; There are some problems with, for example latex-mode: a `$' in a
  86. ;; verbatim environment with throw the font-locking out.
  87. ;; One slight blemish is that code-quotes are highlighted as comments
  88. ;; as they are being entered. They are only highlighted correctly
  89. ;; after `ess-noweb-font-lock-fontify-chunk' has been run, either as a
  90. ;; command or through changing to a different chunk and back again
  91. ;; (unless they lie on a single line, in which case they are
  92. ;; fontified correctly once they are completed).
  93. ;;; Code:
  94. (require 'ess-noweb-mode)
  95. (require 'font-lock)
  96. (defvar ess-noweb-font-lock-mode nil
  97. "Buffer local variable, t iff this buffer is using `ess-noweb-font-lock-mode'.")
  98. (defvar ess-noweb-use-font-lock-mode t
  99. "DO NOT CHANGE THIS VARIABLE
  100. If you use `nw-turn-on-font-lock' to turn on font-locking, then turn it
  101. off again, it would come back on again of its own accord when you
  102. changed major-mode. This variable is used internally to stop it.")
  103. (defvar ess-noweb-font-lock-mode-hook nil
  104. "Hook that is run after entering ess-noweb-font-lock mode.")
  105. (defvar ess-noweb-font-lock-max-initial-chunks 30
  106. "Maximum number of chunks to fontify initially.
  107. If nil, will fontify the entire buffer when
  108. ess-noweb-font-lock-initial-fontify-buffer is called" )
  109. ;; (defvar old-beginning-of-syntax nil
  110. ;; "Stores the function used to find the beginning of syntax in the
  111. ;; current major mode. ess-noweb-font-lock-mode needs a different one." )
  112. (defvar ess-noweb-font-lock-doc-start-face font-lock-reference-face
  113. "Face to use to highlight the `@' at the start of each doc chunk")
  114. (defvar ess-noweb-font-lock-brackets-face font-lock-reference-face
  115. "Face to use to highlight `<<', `>>' `[[' and `]]' ")
  116. (defvar ess-noweb-font-lock-chunk-name-face font-lock-keyword-face
  117. "Face to use to highlight the between `<<' and `>>'")
  118. (defvar ess-noweb-font-lock-code-quote-face font-lock-keyword-face
  119. "Face to use to highlight the between `[[' and `]]'")
  120. ;; Now we add [[ess-noweb-font-lock-mode]] to the list of existing minor
  121. ;; modes. The string ``NWFL'' will be added to the mode-line: ugly, but
  122. ;; brief.
  123. (if (not (assq 'ess-noweb-font-lock-mode minor-mode-alist))
  124. (setq minor-mode-alist (append minor-mode-alist
  125. (list '(ess-noweb-font-lock-mode " NWFL")))))
  126. ;; An ugly kludge to get around problems with global-font-lock, which
  127. ;; fontifies the entire buffer in the new major mode every time you
  128. ;; change mode, which is time-consuming and makes a pigs trotters of
  129. ;; it. Trying to stop it looks tricky, but using this function as your
  130. ;; `font-lock-fontify-buffer' function stops it wasting your time
  131. (defalias 'nwfl-donowt #'ignore)
  132. ;; The following function is just a wrapper for ess-noweb-font-lock-mode,
  133. ;; enabling it to be called as ess-noweb-font-lock-minor-mode instead.
  134. (define-obsolete-function-alias 'ess-noweb-font-lock-minor-mode
  135. #'ess-noweb-font-lock-mode "ESS-16.11")
  136. ;; Here we get to the meat of the problem
  137. ;;;###autoload
  138. (defun ess-noweb-font-lock-mode ( &optional arg)
  139. ;; FIXME: Don't define a new minor mode. Instead, arrange for normal
  140. ;; font-lock to call our functions such as
  141. ;; ess-noweb-font-lock-fontify-chunk-by-number.
  142. "Minor mode for syntax highlighting when using `ess-noweb-mode' to edit noweb files.
  143. Each chunk is fontified in accordance with its own mode."
  144. (interactive "P")
  145. (if (or ess-noweb-mode ess-noweb-font-lock-mode)
  146. (progn
  147. ;; This bit is tricky: copied almost verbatim from bib-cite-mode.el
  148. ;; It seems to ensure that the variable ess-noweb-font-lock-mode is made
  149. ;; local to this buffer. It then sets ess-noweb-font-lock-mode to `t' if
  150. ;; 1) It was called with a prefix argument greater than 0
  151. ;; or 2) It was called with no argument, and ess-noweb-font-lock-mode is
  152. ;; currently nil
  153. ;; ess-noweb-font-lock-mode is nil if the prefix argument was <= 0 or there
  154. ;; was no prefix argument and ess-noweb-font-lock-mode is currently `t'
  155. (set (make-local-variable 'ess-noweb-font-lock-mode)
  156. (if arg
  157. (> (prefix-numeric-value arg) 0)
  158. (not ess-noweb-font-lock-mode)))
  159. ;; Now, if ess-noweb-font-lock-mode is true, we want to turn
  160. ;; ess-noweb-font-lock-mode on
  161. (cond
  162. (ess-noweb-font-lock-mode ;Setup the minor-mode
  163. (when (and (boundp 'global-font-lock-mode) global-font-lock-mode)
  164. (mapc #'ess-noweb-make-variable-permanent-local
  165. '(font-lock-fontify-buffer-function
  166. font-lock-unfontify-buffer-function))
  167. (setq font-lock-fontify-buffer-function #'nwfl-donowt)
  168. (setq font-lock-unfontify-buffer-function #'nwfl-donowt))
  169. (mapc #'ess-noweb-make-variable-permanent-local
  170. '(ess-noweb-font-lock-mode
  171. font-lock-dont-widen
  172. ;; font-lock-beginning-of-syntax-function
  173. syntax-begin-function
  174. ess-noweb-use-font-lock-mode
  175. after-change-functions))
  176. (setq ess-noweb-font-lock-mode t
  177. font-lock-dont-widen t)
  178. (add-hook 'after-change-functions
  179. #'font-lock-after-change-function nil t)
  180. ;; FIXME: Why add ess-noweb-font-lock-mode-fn to our own hook,
  181. ;; instead of just calling ess-noweb-font-lock-mode-fn directly?
  182. (add-hook 'ess-noweb-font-lock-mode-hook #'ess-noweb-font-lock-mode-fn)
  183. (add-hook 'ess-noweb-changed-chunk-hook
  184. #'ess-noweb-font-lock-fontify-this-chunk)
  185. (run-hooks 'ess-noweb-font-lock-mode-hook)
  186. (message "ess-noweb-font-lock mode: use `M-x ess-noweb-font-lock-describe-mode' for more info"))
  187. ;; If we didn't do the above, then we want to turn ess-noweb-font-lock-mode
  188. ;; off, no matter what (hence the condition `t')
  189. (t
  190. (when (and (boundp 'global-font-lock-mode) global-font-lock-mode)
  191. ;; (setq font-lock-fontify-buffer-function
  192. ;; 'font-lock-default-fontify-buffer)
  193. ;; Get back our unfontify buffer function
  194. (setq font-lock-unfontify-buffer-function
  195. #'font-lock-default-unfontify-buffer))
  196. (remove-hook 'ess-noweb-font-lock-mode-hook #'ess-noweb-font-lock-mode-fn)
  197. (remove-hook 'ess-noweb-changed-chunk-hook
  198. #'ess-noweb-font-lock-fontify-this-chunk)
  199. (remove-hook 'after-change-functions
  200. #'font-lock-after-change-function )
  201. (font-lock-default-unfontify-buffer)
  202. (setq ess-noweb-use-font-lock-mode nil)
  203. (message "ess-noweb-font-lock-mode removed"))))
  204. (message "ess-noweb-font-lock-mode can only be used with ess-noweb-mode")))
  205. (defun ess-noweb-start-of-syntax ()
  206. "Go to the place to start fontifying from"
  207. (interactive)
  208. (goto-char (car (ess-noweb-chunk-region))))
  209. (defvar font-latex-extend-region-functions)
  210. (defvar syntax-begin-function)
  211. (defun ess-noweb-font-lock-fontify-chunk-by-number ( chunk-num )
  212. "Fontify chunk CHUNK-NUM based on the current major mode."
  213. (save-excursion
  214. (font-lock-set-defaults)
  215. ;; (setq old-beginning-of-syntax font-lock-beginning-of-syntax-function)
  216. (setq font-lock-keywords
  217. ;; (append font-lock-keywords
  218. ;; '(("\\(\\[\\[\\)\\([^]]*\\]*\\)\\(\\]\\]\\|\\$\\)"
  219. ;; (1 ess-noweb-font-lock-brackets-face prepend )
  220. ;; (2 ess-noweb-font-lock-code-quote-face prepend)
  221. ;; (3 ess-noweb-font-lock-brackets-face prepend))
  222. ;; ("^[ \t\n]*\\(<<\\)\\([^>]*\\)\\(>>=?\\)"
  223. ;; (1 ess-noweb-font-lock-brackets-face prepend )
  224. ;; (2 ess-noweb-font-lock-chunk-name-face prepend)
  225. ;; (3 ess-noweb-font-lock-brackets-face prepend))
  226. ;; ("^@[ \t\n]+"
  227. ;; (0 ess-noweb-font-lock-doc-start-face prepend )))))
  228. (append font-lock-keywords
  229. '(("^[ \t\n]*\\(<<\\)\\([^>]*\\)\\(>>=?\\)"
  230. ;; FIXME: Why not use ess-noweb-font-lock-brackets-face?
  231. (1 font-lock-reference-face prepend )
  232. ;; FIXME: Why not use ess-noweb-font-lock-chunk-name-face?
  233. (2 font-lock-keyword-face prepend)
  234. (3 font-lock-reference-face prepend))
  235. ("^@[ \t\n]+"
  236. (0 font-lock-reference-face prepend )))))
  237. (let ((r (cons (marker-position (cdr (aref ess-noweb-chunk-vector
  238. chunk-num)))
  239. (marker-position (cdr (aref ess-noweb-chunk-vector
  240. (1+ chunk-num))))))
  241. (font-latex-extend-region-functions nil);; don't extend anything
  242. (font-lock-extend-region-functions nil)) ;; this infloops :(
  243. (save-restriction
  244. (narrow-to-region (car r) (cdr r))
  245. ;; (sit-for 3)
  246. (font-lock-fontify-region (car r) (cdr r)))
  247. t)))
  248. (defun ess-noweb-font-lock-fontify-this-chunk ()
  249. "Fontify this chunk according to its own major mode.
  250. Since we are in the chunk, the major mode will already have been set
  251. by ess-noweb-mode.el"
  252. (interactive)
  253. (ess-noweb-font-lock-fontify-chunk-by-number (ess-noweb-find-chunk-index-buffer)))
  254. (defun ess-noweb-font-lock-initial-fontify-buffer ()
  255. "Applies syntax highlighting to some or all chunks in a noweb buffer.
  256. The number of chunks is set by `ess-noweb-font-lock-max-initial-chunks': if
  257. this is nil, the entire buffer is fontified.
  258. It is intended to be called when first entering `ess-noweb-font-lock-mode'.
  259. For other purposes, use `ess-noweb-font-lock-fontify-chunks'."
  260. (interactive)
  261. ;; This will be tricky. It will be very slow to go throught the chunks
  262. ;; in order, switching major modes all the time.
  263. ;; So, we will do the documentation in one pass, the code in a second
  264. ;; pass. This could still be a little slow if we have to swap between
  265. ;; different code modes regularly, but it should be bearable. It should
  266. ;; only happen when the file is first read in, anyway
  267. (save-excursion
  268. (let (start-chunk end-chunk this-chunk)
  269. (setq this-chunk (ess-noweb-find-chunk-index-buffer))
  270. (if ess-noweb-font-lock-max-initial-chunks
  271. (progn
  272. (setq start-chunk
  273. (max 0
  274. (- this-chunk
  275. (/ ess-noweb-font-lock-max-initial-chunks 2))))
  276. ;; Don't you just love hairy lisp syntax ? The above means set the
  277. ;; starting chunk to the current chunk minus half of
  278. ;; ess-noweb-font-lock-max-initial-chunks, unless that is negative in
  279. ;; which case set it to 0
  280. (setq end-chunk (+ start-chunk ess-noweb-font-lock-max-initial-chunks))
  281. (if (> end-chunk (- (length ess-noweb-chunk-vector) 2))
  282. (setq end-chunk (- (length ess-noweb-chunk-vector) 2))))
  283. ;; If ess-noweb-font-lock-max-initial-chunks is nil, do the whole buffer
  284. (progn
  285. (setq start-chunk 0)
  286. (setq end-chunk (- (length ess-noweb-chunk-vector) 2))))
  287. (ess-noweb-font-lock-fontify-chunks start-chunk end-chunk))))
  288. (defun ess-noweb-font-lock-fontify-buffer ()
  289. "This function will fontify each chunk in the buffer appropriately."
  290. (interactive)
  291. (let ((start-chunk 0)
  292. (end-chunk (- (length ess-noweb-chunk-vector) 2)))
  293. (ess-noweb-font-lock-fontify-chunks start-chunk end-chunk)))
  294. (defun ess-noweb-font-lock-fontify-chunks (start-chunk end-chunk)
  295. "Fontify a noweb file from START-CHUNK to END-CHUNK."
  296. (interactive)
  297. (let ((chunk-counter start-chunk))
  298. (save-excursion
  299. (message "Fontifying from %d to %d" start-chunk end-chunk)
  300. ;; Want to set DOC mode for the first Doc chunk, not for the others
  301. (while (stringp (car (aref ess-noweb-chunk-vector chunk-counter)))
  302. (setq chunk-counter (+ chunk-counter 1)))
  303. (goto-char (cdr (aref ess-noweb-chunk-vector chunk-counter)))
  304. (ess-noweb-select-mode)
  305. ;; Now go through the chunks, fontifying the documentation ones.
  306. (while (<= chunk-counter end-chunk)
  307. (if (not (stringp (car (aref ess-noweb-chunk-vector chunk-counter))))
  308. (ess-noweb-font-lock-fontify-chunk-by-number chunk-counter))
  309. (message "Fontifying documentation chunks: chunk %d" chunk-counter)
  310. (setq chunk-counter (+ 1 chunk-counter)))
  311. ;; Go back to the start and go through the chunks, fontifying the code ones.
  312. (setq chunk-counter start-chunk)
  313. (message "About to do code chunks")
  314. (while (<= chunk-counter end-chunk)
  315. (when (stringp (car (aref ess-noweb-chunk-vector chunk-counter)))
  316. ;; It's a code chunk: goto it to set the correct code mode, then
  317. ;; fontify it.
  318. (message "Fontifying code chunks: chunk %d" chunk-counter)
  319. (goto-char (cdr (aref ess-noweb-chunk-vector chunk-counter)))
  320. (ess-noweb-select-mode)
  321. (ess-noweb-font-lock-fontify-this-chunk))
  322. (setq chunk-counter (1+ chunk-counter))))
  323. (ess-noweb-select-mode)))
  324. (defun ess-noweb-font-lock-mode-fn()
  325. "Function that is intended to be attached to ess-noweb-font-lock-mode-hook."
  326. (ess-noweb-font-lock-initial-fontify-buffer))
  327. ;; This is a wee bit of a hack. If people attach `turn-on-font-lock'
  328. ;; to their major mode hook, it will play hell with
  329. ;; ess-noweb-font-lock-mode. I had hoped that providing a replacement
  330. ;; `nw-turn-on-font-lock' would solve the problem, but it didn't
  331. ;; (sometimes turn-on-font-lock appears in places other than
  332. ;; `.emacs', such as in ESS). So rather than have it fall over if
  333. ;; turn-on-lock was around, I redefined turn-on-font-lock to do the
  334. ;; right thing.
  335. (define-obsolete-function-alias 'nw-turn-on-font-lock
  336. #'turn-on-font-lock "ESS-16.11")
  337. (defadvice turn-on-font-lock (around ess-noweb-font-lock activate)
  338. ;; FIXME: An advice on turn-on-font-lock is definitely not sufficient,
  339. ;; since font-lock can be enabled without going through turn-on-font-lock!
  340. (if (not ess-noweb-mode)
  341. ad-do-it
  342. (if (and (not ess-noweb-font-lock-mode) ess-noweb-use-font-lock-mode)
  343. (ess-noweb-font-lock-mode))))
  344. (provide 'ess-noweb-font-lock-mode)
  345. ;; *****
  346. ;;
  347. ;; Adnan Yaqub (AYaqub@orga.com)
  348. ;; ORGA Kartensysteme GmbH // An der Kapelle 2 // D-33104 Paderborn // Germany
  349. ;; Tel. +49 5254 991-823 //Fax. +49 5254 991-749
  350. ;;; ess-noweb-font-lock-mode.el ends here