├── README.md ├── doc ├── demo.el ├── demo.png └── lisp-extra-font-lock-screenshot-setup.el ├── lisp-extra-font-lock.el └── test ├── files ├── basics.el ├── basics.el.faceup ├── demo.el ├── demo.el.faceup ├── exclude.el ├── exclude.el.faceup ├── hash-quote.el └── hash-quote.el.faceup ├── lisp-extra-font-lock-test-files.el └── lisp-extra-font-lock-test-setup.el /README.md: -------------------------------------------------------------------------------- 1 | # lisp-extra-font-lock - Highlight bound variables and quoted exprs 2 | 3 | *Author:* Anders Lindgren
4 | *Version:* 0.0.6
5 | *URL:* [https://github.com/Lindydancer/lisp-extra-font-lock](https://github.com/Lindydancer/lisp-extra-font-lock)
6 | 7 | This package highlight the location where local variables is 8 | created (bound, for example by `let`) as well as quoted and 9 | backquoted constant expressions. 10 | 11 | ## Example 12 | 13 | Below, `^` is used indicate highlighted normal variables and 14 | constant expressions. `*` is used to show highlighting of special 15 | variables (i.e. those defined by `defvar`) and of the backquote and 16 | comma operators. 17 | 18 | (defun my-function (next) 19 | ^^^^ <- Parameters 20 | (let ((numbers '(one two three)) 21 | ^^^^^^^ ^^^^^^^^^^^^^^^ <- Var bound by `let` and quoted expr. 22 | (buffer-read-only t)) 23 | **************** <- Special variable (different color) 24 | `(,@numbers and ,next))) 25 | *^** ^^^ * ^ <- Backquote and comma 26 | 27 | ### Screenshot 28 | 29 | ![See doc/demo.png for screenshot](doc/demo.png) 30 | 31 | ## What is highlighted 32 | 33 | * Parameters in functions and lambdas 34 | * Variables bound by specal constructs like `let`, `dolist`, 35 | `condition-case`, and `pcase-let` 36 | * Normal variables and variables declared as globals using `defvar` 37 | are highlighted in different colors, as a warning 38 | * Quoted expressions 39 | * Backquoted expressions. Subexpressions using the "," or ",@" are 40 | not highlighted (as they are evaluted and thus not constant). 41 | Also, the backquote and the comma operators themselves are 42 | highlighted using a bright color as a warning. 43 | * Hash-quoted symbols. 44 | 45 | ## Installation 46 | 47 | Place this package in a directory in the load-path. To activate it, 48 | use *customize* or place the following lines in a suitable init 49 | file: 50 | 51 | (require 'lisp-extra-font-lock) 52 | (lisp-extra-font-lock-global-mode 1) 53 | 54 | ## Customization 55 | 56 | You can modify the following lists to add more functions that are 57 | recognized: 58 | 59 | * `lisp-extra-font-lock-let-functions` -- List of function with the 60 | same syntax as `let` 61 | * `lisp-extra-font-lock-defun-functions` -- List of function with 62 | the same syntax as `defun` 63 | * `lisp-extra-font-lock-lambda-functions` -- List of function with 64 | the same syntax as `lambda` 65 | * `lisp-extra-font-lock-dolist-functions` -- List of function with 66 | the same syntax as `dolist` 67 | * `lisp-extra-font-lock-bind-first-functions` -- List of function 68 | that bind their first argument, like `condition-case`. 69 | * `lisp-extra-font-lock-loop-functions` -- List of functions with 70 | the same syntax as `cl-loop`. 71 | 72 | The following faces are used when highlighting. You can either 73 | redefine the face (e.g. using a theme), or you can rebind the 74 | corresponding variable. 75 | 76 | * Local variables are highlighted using the standard face 77 | `font-lock-variable-name-face` 78 | * Special (global) variables that are rebound are highlighted using 79 | the face bound to the variable 80 | `lisp-extra-font-lock-special-variable-name-face` (by default 81 | `lisp-extra-font-lock-special-variable-name`, which inherits from 82 | `font-lock-warning-face`) 83 | * Quoted expressions use the face bound to the variable 84 | `lisp-extra-font-lock-quoted-face` (by default 85 | `lisp-extra-font-lock-quoted`, which inherits from 86 | `font-lock-constant-face`) 87 | * The backquote and comma operators use the face bound to the 88 | variable `lisp-extra-font-lock-backquote-face` (by default 89 | `lisp-extra-font-lock-backquote`, which inherits from 90 | `font-lock-warning-face`). 91 | * Named arguments to `cl-loop` are highlighted using 92 | `font-lock-builtin-face`. 93 | 94 | ### Example 95 | 96 | To set the face used to highlight quoted expressions to a gray 97 | color, you can use: 98 | 99 | (custom-set-faces 100 | '(lisp-extra-font-lock-quoted ((t :foreground "grey50")))) 101 | 102 | 103 | --- 104 | Converted from `lisp-extra-font-lock.el` by [*el2markdown*](https://github.com/Lindydancer/el2markdown). 105 | -------------------------------------------------------------------------------- /doc/demo.el: -------------------------------------------------------------------------------- 1 | ;;; demo.el --- Demonstration program for `lisp-extra-font-lock'. 2 | 3 | ;; The package `lisp-extra-font-lock' highlights variables bound by 4 | ;; `defun', `lambda', `let', `dolist', etc. It also highlight quoted 5 | ;; and backquoted expressions -- excluding any comma operator 6 | ;; expressions. 7 | 8 | (defun my-function (next) ; <- Parameters 9 | (let ((numbers '(one two three)) ; <- `let' and quoted expr 10 | (buffer-read-only t)) ; <- Special variable 11 | `(,@numbers and ,next))) ; <- Backquote and comma 12 | 13 | ;; (my-function 'four) => (one two three and four) 14 | 15 | ;;; demo.el ends here 16 | -------------------------------------------------------------------------------- /doc/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lindydancer/lisp-extra-font-lock/4605eccbe1a7fcbd3cacf5b71249435413b4db4f/doc/demo.png -------------------------------------------------------------------------------- /doc/lisp-extra-font-lock-screenshot-setup.el: -------------------------------------------------------------------------------- 1 | ;; lisp-extra-font-lock-screenshot-setup.el --- prepare Emacs for screenshot. 2 | 3 | ;; Usage: 4 | ;; 5 | ;; emacs -q -l lisp-extra-font-lock-screenshot-setup.el 6 | ;; 7 | ;; Take screenshot. OS X: Cmd-Shift-4 SPC click on window. 8 | 9 | (setq inhibit-startup-screen t) 10 | 11 | (blink-cursor-mode -1) 12 | 13 | (defvar lisp-extra-font-lock-screenshot-dir 14 | (or (and load-file-name 15 | (file-name-directory load-file-name)) 16 | default-directory)) 17 | 18 | (load (concat lisp-extra-font-lock-screenshot-dir 19 | "../lisp-extra-font-lock.el")) 20 | (lisp-extra-font-lock-global-mode 1) 21 | (find-file (concat lisp-extra-font-lock-screenshot-dir "demo.el")) 22 | 23 | (set-frame-size (selected-frame) 80 20) 24 | 25 | (message "") 26 | 27 | ;; lisp-extra-font-lock-screenshot-setup.el ends here 28 | -------------------------------------------------------------------------------- /lisp-extra-font-lock.el: -------------------------------------------------------------------------------- 1 | ;;; lisp-extra-font-lock.el --- Highlight bound variables and quoted exprs. 2 | 3 | ;; Copyright (C) 2014-2018 Anders Lindgren 4 | 5 | ;; Author: Anders Lindgren 6 | ;; Keywords: languages, faces 7 | ;; Created: 2014-11-22 8 | ;; Version: 0.0.6 9 | ;; URL: https://github.com/Lindydancer/lisp-extra-font-lock 10 | 11 | ;; This program is free software: you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, 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 19 | ;; GNU General Public License for more details. 20 | ;; 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; This package highlight the location where local variables is 27 | ;; created (bound, for example by `let') as well as quoted and 28 | ;; backquoted constant expressions. 29 | 30 | ;; Example: 31 | ;; 32 | ;; Below, `^' is used indicate highlighted normal variables and 33 | ;; constant expressions. `*' is used to show highlighting of special 34 | ;; variables (i.e. those defined by `defvar') and of the backquote and 35 | ;; comma operators. 36 | ;; 37 | ;; (defun my-function (next) 38 | ;; ^^^^ <- Parameters 39 | ;; (let ((numbers '(one two three)) 40 | ;; ^^^^^^^ ^^^^^^^^^^^^^^^ <- Var bound by `let' and quoted expr. 41 | ;; (buffer-read-only t)) 42 | ;; **************** <- Special variable (different color) 43 | ;; `(,@numbers and ,next))) 44 | ;; *^** ^^^ * ^ <- Backquote and comma 45 | ;; 46 | ;; Screenshot: 47 | ;; 48 | ;; ![See doc/demo.png for screenshot](doc/demo.png) 49 | 50 | ;; What is highlighted: 51 | ;; 52 | ;; * Parameters in functions and lambdas 53 | ;; 54 | ;; * Variables bound by specal constructs like `let', `dolist', 55 | ;; `condition-case', and `pcase-let' 56 | ;; 57 | ;; * Normal variables and variables declared as globals using `defvar' 58 | ;; are highlighted in different colors, as a warning 59 | ;; 60 | ;; * Quoted expressions 61 | ;; 62 | ;; * Backquoted expressions. Subexpressions using the "," or ",@" are 63 | ;; not highlighted (as they are evaluted and thus not constant). 64 | ;; Also, the backquote and the comma operators themselves are 65 | ;; highlighted using a bright color as a warning. 66 | ;; 67 | ;; * Hash-quoted symbols. 68 | 69 | ;; Installation: 70 | ;; 71 | ;; Place this package in a directory in the load-path. To activate it, 72 | ;; use *customize* or place the following lines in a suitable init 73 | ;; file: 74 | ;; 75 | ;; (require 'lisp-extra-font-lock) 76 | ;; (lisp-extra-font-lock-global-mode 1) 77 | 78 | ;; Customization: 79 | ;; 80 | ;; You can modify the following lists to add more functions that are 81 | ;; recognized: 82 | ;; 83 | ;; * `lisp-extra-font-lock-let-functions' -- List of function with the 84 | ;; same syntax as `let' 85 | ;; 86 | ;; * `lisp-extra-font-lock-defun-functions' -- List of function with 87 | ;; the same syntax as `defun' 88 | ;; 89 | ;; * `lisp-extra-font-lock-lambda-functions' -- List of function with 90 | ;; the same syntax as `lambda' 91 | ;; 92 | ;; * `lisp-extra-font-lock-dolist-functions' -- List of function with 93 | ;; the same syntax as `dolist' 94 | ;; 95 | ;; * `lisp-extra-font-lock-bind-first-functions' -- List of function 96 | ;; that bind their first argument, like `condition-case'. 97 | ;; 98 | ;; * `lisp-extra-font-lock-loop-functions' -- List of functions with 99 | ;; the same syntax as `cl-loop'. 100 | ;; 101 | ;; The following faces are used when highlighting. You can either 102 | ;; redefine the face (e.g. using a theme), or you can rebind the 103 | ;; corresponding variable. 104 | ;; 105 | ;; * Local variables are highlighted using the standard face 106 | ;; `font-lock-variable-name-face' 107 | ;; 108 | ;; * Special (global) variables that are rebound are highlighted using 109 | ;; the face bound to the variable 110 | ;; `lisp-extra-font-lock-special-variable-name-face' (by default 111 | ;; `lisp-extra-font-lock-special-variable-name', which inherits from 112 | ;; `font-lock-warning-face') 113 | ;; 114 | ;; * Quoted expressions use the face bound to the variable 115 | ;; `lisp-extra-font-lock-quoted-face' (by default 116 | ;; `lisp-extra-font-lock-quoted', which inherits from 117 | ;; `font-lock-constant-face') 118 | ;; 119 | ;; * The backquote and comma operators use the face bound to the 120 | ;; variable `lisp-extra-font-lock-backquote-face' (by default 121 | ;; `lisp-extra-font-lock-backquote', which inherits from 122 | ;; `font-lock-warning-face'). 123 | ;; 124 | ;; * Named arguments to `cl-loop' are highlighted using 125 | ;; `font-lock-builtin-face'. 126 | ;; 127 | ;; Example: 128 | ;; 129 | ;; To set the face used to highlight quoted expressions to a gray 130 | ;; color, you can use: 131 | ;; 132 | ;; (custom-set-faces 133 | ;; '(lisp-extra-font-lock-quoted ((t :foreground "grey50")))) 134 | 135 | ;;; Code: 136 | 137 | ;; ------------------------------ 138 | ;; Customizable variables 139 | ;; 140 | 141 | 142 | (defgroup lisp-extra-font-lock nil 143 | "Highlight bound variables and quoted expressions in lisp." 144 | :group 'faces) 145 | 146 | 147 | ;;;###autoload 148 | (defcustom lisp-extra-font-lock-modes '(emacs-lisp-mode lisp-mode) 149 | "List of modes where Lisp Extra Font Lock Global mode should be enabled." 150 | :type '(repeat symbol) 151 | :group 'lisp-extra-font-lock) 152 | 153 | 154 | ;; ---------- 155 | ;; Faces and corresponding variable. 156 | ;; 157 | 158 | (defface lisp-extra-font-lock-backquote 159 | '((t :inherit font-lock-warning-face)) 160 | "The default face used to highlight backquotes and the comma operator." 161 | :group 'lisp-extra-font-lock) 162 | 163 | 164 | (defcustom lisp-extra-font-lock-backquote-face 'lisp-extra-font-lock-backquote 165 | "The face used to highlight backquotes and the comma operator. 166 | To disable this highlighting, set this to nil." 167 | :type '(choice (const nil) 168 | face) 169 | :group 'lisp-extra-font-lock) 170 | 171 | 172 | (defface lisp-extra-font-lock-quoted 173 | '((t :inherit font-lock-constant-face)) 174 | "The default face used to highlight quoted expressions." 175 | :group 'lisp-extra-font-lock) 176 | 177 | 178 | (defcustom lisp-extra-font-lock-quoted-face 'lisp-extra-font-lock-quoted 179 | "The face used to highlight quoted expressions. 180 | To disable this highlighting, set this to nil." 181 | :type '(choice (const nil) 182 | face) 183 | :group 'lisp-extra-font-lock) 184 | 185 | 186 | (defface lisp-extra-font-lock-quoted-function 187 | '((t :inherit font-lock-function-name-face)) 188 | "The default face used to highlight #'-quoted function symbols." 189 | :group 'lisp-extra-font-lock) 190 | 191 | 192 | (defcustom lisp-extra-font-lock-quoted-function-face 193 | 'lisp-extra-font-lock-quoted-function 194 | "The face used to highlight #'-quoted function symbols. 195 | To disable this highlighting, set this to nil." 196 | :type '(choice (const nil) 197 | face) 198 | :group 'lisp-extra-font-lock) 199 | 200 | 201 | (defface lisp-extra-font-lock-special-variable-name 202 | '((t :inherit font-lock-warning-face)) 203 | "The default face used to highlight special variables bound by `let'." 204 | :group 'lisp-extra-font-lock) 205 | 206 | 207 | (defcustom lisp-extra-font-lock-special-variable-name-face 208 | 'lisp-extra-font-lock-special-variable-name 209 | "The face used to highlight special variables bound by `let'. 210 | 211 | A special variable is a global variable defined by `defvar'. See 212 | `special-variable-p' for details. 213 | 214 | To disable this highlighting, set this to nil. To highlight 215 | special variables like plain variables, set this to 216 | `font-lock-variable-name-face'." 217 | :type '(choice (const nil) 218 | face) 219 | :group 'lisp-extra-font-lock) 220 | 221 | 222 | ;; ---------- 223 | ;; Function lists 224 | ;; 225 | 226 | (defcustom lisp-extra-font-lock-let-functions 227 | '("let" 228 | "let*" 229 | "letf" 230 | "letf*" 231 | "lexical-let" 232 | "lexical-let*" 233 | "multiple-value-bind" 234 | "pcase-let" ; Highlights entire UPAT:s. 235 | "pcase-let*" 236 | "cl-letf" 237 | "cl-letf*" 238 | "cl-multiple-value-bind") 239 | "List of function using same syntax as `let' to bind variables." 240 | :type '(repeat string) 241 | :group 'lisp-extra-font-lock) 242 | 243 | 244 | (defcustom lisp-extra-font-lock-defun-functions 245 | '("defun" 246 | "defun*" 247 | "defmacro" 248 | "defmacro*" 249 | "defsubst" 250 | "cl-defun" 251 | "cl-defmacro" 252 | "cl-defsubst") 253 | "List of function using same syntax as `defun' to bind variables." 254 | :type '(repeat string) 255 | :group 'lisp-extra-font-lock) 256 | 257 | 258 | (defcustom lisp-extra-font-lock-lambda-functions 259 | '("lambda") 260 | "List of function using same syntax as `lambda' to bind variables." 261 | :type '(repeat string) 262 | :group 'lisp-extra-font-lock) 263 | 264 | 265 | (defcustom lisp-extra-font-lock-dolist-functions 266 | '("dolist" 267 | "dotimes" 268 | "cl-dolist" 269 | "cl-dotimes") 270 | "List of function using same syntax as `dolist' to bind variables." 271 | :type '(repeat string) 272 | :group 'lisp-extra-font-lock) 273 | 274 | 275 | (defcustom lisp-extra-font-lock-bind-first-functions 276 | '("condition-case") 277 | "List of function that bind their first argument." 278 | :type '(repeat string) 279 | :group 'lisp-extra-font-lock) 280 | 281 | 282 | (defcustom lisp-extra-font-lock-loop-functions 283 | '("loop" 284 | "cl-loop") 285 | "List of functions using same syntax as `loop' to bind variables.." 286 | :type '(repeat string) 287 | :group 'lisp-extra-font-lock) 288 | 289 | 290 | ;; ------------------------------ 291 | ;; The modes 292 | ;; 293 | 294 | ;;;###autoload 295 | (define-minor-mode lisp-extra-font-lock-mode 296 | "Minor mode that highlights bound variables and quoted expressions in lisp." 297 | :group 'lisp-extra-font-lock 298 | (if lisp-extra-font-lock-mode 299 | (lisp-extra-font-lock-add-keywords) 300 | (lisp-extra-font-lock-remove-keywords)) 301 | ;; As of Emacs 24.4, `font-lock-fontify-buffer' is not legal to 302 | ;; call, instead `font-lock-flush' should be used. 303 | (if (fboundp 'font-lock-flush) 304 | (font-lock-flush) 305 | (when font-lock-mode 306 | (with-no-warnings 307 | (font-lock-fontify-buffer))))) 308 | 309 | 310 | ;;;###autoload 311 | (define-global-minor-mode lisp-extra-font-lock-global-mode 312 | lisp-extra-font-lock-mode 313 | (lambda () 314 | (when (apply 'derived-mode-p lisp-extra-font-lock-modes) 315 | (lisp-extra-font-lock-mode 1))) 316 | :group 'lisp-extra-font-lock) 317 | 318 | 319 | (defun lisp-extra-font-lock-variable-face-form (name) 320 | "A form suitable for a font-lock face expression. 321 | 322 | NAME is a form that should evalute to the name of the symbol, as a string." 323 | `(if (ignore-errors (let ((symbol (intern-soft ,name))) 324 | (and symbol 325 | (special-variable-p symbol)))) 326 | lisp-extra-font-lock-special-variable-name-face 327 | font-lock-variable-name-face)) 328 | 329 | (defun lisp-extra-font-lock-keywords () 330 | "Font-lock keywords used by `lisp-extra-font-lock'. 331 | The keywords highlight variable bindings and quoted expressions." 332 | `(;; Function and lambda parameters 333 | (,(concat "(" 334 | "\\(?:" 335 | (regexp-opt lisp-extra-font-lock-defun-functions) 336 | "[ \t\n]+\\_<\\(?:\\sw\\|\\s_\\)+\\_>" 337 | "\\|" 338 | (regexp-opt lisp-extra-font-lock-lambda-functions) 339 | "\\)" 340 | "[ \t\n]+(") 341 | (lisp-extra-font-lock-match-argument-list 342 | ;; Pre-match form 343 | (progn 344 | (goto-char (match-end 0)) 345 | ;; Search limit 346 | (save-excursion 347 | (backward-char) ; Position point before "(". 348 | (lisp-extra-font-lock-end-position))) 349 | ;; Post-match form 350 | nil 351 | (0 ,(lisp-extra-font-lock-variable-face-form '(match-string 0)) 352 | nil t))) 353 | ;; Variables bound by `let'. 354 | (,(concat "(" 355 | (regexp-opt lisp-extra-font-lock-let-functions) 356 | "[ \t]+(") 357 | (lisp-extra-font-lock-match-let 358 | ;; Pre-match form 359 | (progn 360 | (goto-char (match-end 0)) 361 | ;; Search limit 362 | (save-excursion 363 | (backward-char) ; Position point before "(". 364 | (lisp-extra-font-lock-end-position))) 365 | ;; Post-match form 366 | (goto-char (match-end 0)) 367 | (0 ,(lisp-extra-font-lock-variable-face-form '(match-string 0))))) 368 | ;; Variables bound by `cl-dolist' etc. 369 | (,(concat "(" 370 | (regexp-opt lisp-extra-font-lock-dolist-functions) 371 | "[ \t]+(\\(\\(?:\\sw\\|\\s_\\)+\\)\\_>") 372 | (1 ,(lisp-extra-font-lock-variable-face-form '(match-string 1)))) 373 | ;; Bind first argument like `condition-case'. 374 | (,(concat "(" 375 | (regexp-opt lisp-extra-font-lock-bind-first-functions) 376 | "[ \t]+\\_<\\(\\(?:\\sw\\|\\s_\\)+\\)\\_>") 377 | (1 (and (not (string= (match-string 1) "nil")) 378 | ,(lisp-extra-font-lock-variable-face-form '(match-string 1))))) 379 | ;; Bind variables and named arguments to `cl-loop'. 380 | (,(concat "(" 381 | (regexp-opt lisp-extra-font-lock-loop-functions) 382 | "\\_>") 383 | (lisp-extra-font-lock-match-loop-keywords 384 | ;; Pre-match form. Value of expression is limit for submatcher. 385 | (progn 386 | (goto-char (match-end 0)) 387 | (save-excursion 388 | (goto-char (match-beginning 0)) 389 | (lisp-extra-font-lock-end-position))) 390 | ;; Post-match form. 391 | (goto-char (match-end 0)) 392 | (1 font-lock-builtin-face) 393 | (2 ,(lisp-extra-font-lock-variable-face-form '(match-string 2)) nil t))) 394 | (;; Quote and backquote. 395 | ;; 396 | ;; Matcher: Set match-data 1 if backquote. 397 | lisp-extra-font-lock-match-quote-and-backquote 398 | (1 lisp-extra-font-lock-backquote-face nil t) 399 | (;; Submatcher, match part of quoted expression or comma. 400 | lisp-extra-font-lock-match-quoted-content 401 | ;; Pre-match form. Value of expression is limit for submatcher. 402 | (progn 403 | (goto-char (match-end 0)) 404 | ;; Search limit 405 | (lisp-extra-font-lock-end-position)) 406 | ;; Post-match form 407 | (goto-char (match-end 0)) 408 | ;; Highlight rules for submatcher. 409 | (1 lisp-extra-font-lock-quoted-face append) 410 | (2 lisp-extra-font-lock-backquote-face nil t))) 411 | ;; Function read syntax 412 | ("#'\\(\\(?:\\sw\\|\\s_\\)+\\)\\_>" 413 | 1 lisp-extra-font-lock-quoted-function-face))) 414 | 415 | 416 | (defvar lisp-extra-font-lock--installed-keywords nil) 417 | 418 | (defun lisp-extra-font-lock-add-keywords () 419 | "Add extra font-lock keywords to lisp." 420 | (set (make-local-variable 'font-lock-multiline) t) 421 | (when (local-variable-p 'lisp-extra-font-lock--installed-keywords) 422 | (font-lock-remove-keywords nil lisp-extra-font-lock--installed-keywords)) 423 | (let ((keywords (lisp-extra-font-lock-keywords))) 424 | (set (make-local-variable 'lisp-extra-font-lock--installed-keywords) 425 | keywords) 426 | (font-lock-add-keywords nil keywords 'append))) 427 | 428 | 429 | (defun lisp-extra-font-lock-remove-keywords () 430 | "Remove font-lock keywords for extra lisp highlithing." 431 | (font-lock-remove-keywords nil lisp-extra-font-lock--installed-keywords)) 432 | 433 | 434 | ;; ---------------------------------------- 435 | ;; Matcher functions 436 | ;; 437 | 438 | (defun lisp-extra-font-lock-end-position () 439 | "Suitable end position of expression after point. 440 | If expression is open-ended, the beginning of the next top-level 441 | form is used, or `point-max' if none is found." 442 | (save-match-data 443 | (save-excursion 444 | (or (condition-case nil 445 | (progn 446 | (forward-sexp) 447 | (point)) 448 | (error nil)) 449 | (and (re-search-forward "^(" nil t) 450 | (match-beginning 0)) 451 | (point-max))))) 452 | 453 | (defun lisp-extra-font-lock-match-argument-list (limit) 454 | (forward-comment (buffer-size)) 455 | (and (< (point) limit) 456 | (let ((res (looking-at "\\_<\\(?:\\sw\\|\\s_\\)+\\_>"))) 457 | (when res 458 | (goto-char (match-end 0))) 459 | res))) 460 | 461 | 462 | (defun lisp-extra-font-lock-match-let (limit) 463 | "Match next variable introduced by `let'-like constructs." 464 | (forward-comment (buffer-size)) 465 | (let ((p (point))) 466 | (cond ((eq (following-char) ?\( ) 467 | ;; Match "(var initial-valoue)" 468 | (forward-char) 469 | (forward-comment (buffer-size)) 470 | (and 471 | (< (point) limit) 472 | (let ((res (looking-at "\\(?:\\sw\\|\\s_\\)+\\_>"))) 473 | (when res 474 | (goto-char p) 475 | (condition-case nil 476 | (forward-sexp) 477 | (error (goto-char limit)))) 478 | res))) 479 | ((looking-at "\\(?:\\sw\\|\\s_\\)+\\_>") 480 | ;; Match "var" 481 | (goto-char (match-end 0)) 482 | (<= (point) limit)) 483 | (t 484 | nil)))) 485 | 486 | 487 | (defun lisp-extra-font-lock-is-in-comment-or-string (pos) 488 | "Return non-nil if POS is in a comment, string, constant, or reader macro. 489 | 490 | This assumes that Font Lock is active and has fontified comments 491 | and strings." 492 | (or (nth 8 (save-excursion 493 | (syntax-ppss pos))) ; In comment or string. 494 | ;; Plain character constant ?. 495 | (eq (char-before pos) ??) 496 | ;; Escaped character constant ?\. 497 | (and (eq (char-before pos) ?\\) 498 | (eq (char-before (- pos 1)) ??)) 499 | ;; Reader macro like #'. 500 | (eq (char-before pos) ?#))) 501 | 502 | 503 | (defun lisp-extra-font-lock-match-quote-and-backquote (limit) 504 | "Search for quote and backquote in in code. 505 | Set match data 1 if character matched is backquote." 506 | (let (res) 507 | (while 508 | (progn (setq res (re-search-forward "\\(?:\\(`\\)\\|'\\)" limit t)) 509 | (and res 510 | (lisp-extra-font-lock-is-in-comment-or-string 511 | (match-beginning 0))))) 512 | res)) 513 | 514 | 515 | (defun lisp-extra-font-lock-match-quoted-content (limit) 516 | "Match next part of a quoted content. 517 | 518 | Match up to next comma operator or quoted subexpression, or to 519 | the end of the quoted expression." 520 | (and (< (point) limit) 521 | (let ((p (point)) 522 | res) 523 | (while 524 | (progn 525 | (setq res (re-search-forward "\\(,@?\\|[`']\\)" limit t)) 526 | (and res 527 | (lisp-extra-font-lock-is-in-comment-or-string 528 | (match-beginning 0))))) 529 | (if res 530 | ;; Match up to next quoted subpart or comma operator. 531 | (let ((is-comma (eq (char-after (match-beginning 0)) ?,))) 532 | (set-match-data (list 533 | ;; Match data 0: Full match. 534 | p (match-end 0) 535 | ;; Match data 1: Part of the quoted expression 536 | p 537 | (match-beginning 0) 538 | ;; Match data 2; Comma operator (if present) 539 | (and is-comma (match-beginning 0)) 540 | (and is-comma (match-end 0)))) 541 | (condition-case nil 542 | (forward-sexp) 543 | (error (goto-char limit)))) 544 | ;; Match to the end of the quoted expression. 545 | (set-match-data (list p limit 546 | p limit)) 547 | (goto-char limit)) 548 | t))) 549 | 550 | (defvar lisp-extra-font-lock-loop-keywords 551 | '("=" "above" "across" "across-ref" "always" "and" "append" "as" 552 | "being" "below" "buffer" "buffers" "by" 553 | "collect" "collecting" "concat" "count" 554 | "do" "doing" "downfrom" "downto" 555 | "each" "element" "elements" "else" "end" 556 | "extent" "extents" "external-symbol" "external-symbols" 557 | "finally" "frames" "from" 558 | "hash-key" "hash-keys" "hash-value" "hash-values" 559 | "if" "in" "in-ref" "initially" "interval" "intervals" 560 | "key-binding" "key-bindings" "key-code" "key-codes" "key-seq" "key-seqs" 561 | "maximize" "minimize" 562 | "named" "nconc" "nconcing" "never" 563 | "of" "of-ref" "on" "overlay" "overlays" 564 | "present-symbol" "present-symbols" "property" 565 | "repeat" "return" 566 | "screen" "screens" "sum" "symbol" "symbols" 567 | "the" "then" "thereis" "to" 568 | "unless" "until" "upfrom" "upto" "using" 569 | "vconcat" 570 | "when" "while" "windows") 571 | "List of `cl-loop' named parameters, excluding variable binding ones.") 572 | 573 | (defvar lisp-extra-font-lock-loop-keywords-with-var '("for" 574 | "index" 575 | "into" 576 | "with") 577 | "List of `cl-loop' named variable binding parameters.") 578 | 579 | 580 | ;; Match named loop keywords, and (optionally) any bound variables. 581 | ;; 582 | ;; Note, does not support "destructuring", i.e. binding several 583 | ;; variables using pattern matching. If this is used, the entire 584 | ;; expression is highlighted as a variable. 585 | (defun lisp-extra-font-lock-match-loop-keywords (limit) 586 | "Match named keyword of `loop' and highlight variable arguments." 587 | (while 588 | (progn 589 | (forward-comment (buffer-size)) 590 | (and (< (point) limit) 591 | (not (looking-at 592 | (concat 593 | "\\_<" 594 | "\\(" 595 | (regexp-opt (append 596 | lisp-extra-font-lock-loop-keywords-with-var 597 | lisp-extra-font-lock-loop-keywords)) 598 | "\\)" 599 | "\\_>"))))) 600 | (condition-case nil 601 | (forward-sexp) 602 | (error (goto-char limit)))) 603 | (if (not (< (point) limit)) 604 | nil 605 | (goto-char (match-end 0)) 606 | (when (member (match-string 1) lisp-extra-font-lock-loop-keywords-with-var) 607 | (forward-comment (buffer-size)) 608 | (let ((var-start (point))) 609 | (when (condition-case nil 610 | (progn 611 | (forward-sexp) 612 | t) 613 | (error nil)) 614 | (set-match-data (list 615 | (match-beginning 0) 616 | (point) 617 | (match-beginning 1) 618 | (match-end 1) 619 | var-start 620 | (point)))))) 621 | t)) 622 | 623 | (provide 'lisp-extra-font-lock) 624 | 625 | ;;; lisp-extra-font-lock.el ends here. 626 | -------------------------------------------------------------------------------- /test/files/basics.el: -------------------------------------------------------------------------------- 1 | ;; A plain function. 2 | (defun my-function1 (arg) 3 | (let ((tmp 'value)) 4 | nil)) 5 | 6 | ;; &optional should not be affected, but subsequent arguments should 7 | ;; be highlighted. 8 | (defun my-function2 (arg &optional arg2) 9 | nil) 10 | 11 | ;; Reserved words should be highlighted. 12 | (defun my-function3 (arg mode-name) 13 | (let ((major-mode 'what) 14 | (my-major-mode 'better)) 15 | nil)) 16 | 17 | ;; `dolist'. 18 | (defun my-dlist () 19 | (dolist (arg '(1 2 3)) 20 | nil)) 21 | 22 | ;; `loop'. 23 | (defun my-loop (my-list) 24 | (cl-loop for (key . value) in my-list 25 | collect key 26 | collect value)) 27 | -------------------------------------------------------------------------------- /test/files/basics.el.faceup: -------------------------------------------------------------------------------- 1 | «m:;; »«x:A plain function. 2 | »(«k:defun» «f:my-function1» («v:arg») 3 | («k:let» ((«v:tmp» '«:lisp-extra-font-lock-quoted:value»)) 4 | nil)) 5 | 6 | «m:;; »«x:&optional should not be affected, but subsequent arguments should 7 | »«m:;; »«x:be highlighted. 8 | »(«k:defun» «f:my-function2» («v:arg» «t:&optional» «v:arg2») 9 | nil) 10 | 11 | «m:;; »«x:Reserved words should be highlighted. 12 | »(«k:defun» «f:my-function3» («v:arg» «:lisp-extra-font-lock-special-variable-name:mode-name») 13 | («k:let» ((«:lisp-extra-font-lock-special-variable-name:major-mode» '«:lisp-extra-font-lock-quoted:what») 14 | («v:my-major-mode» '«:lisp-extra-font-lock-quoted:better»)) 15 | nil)) 16 | 17 | «m:;; »«x:`«c:dolist»'. 18 | »(«k:defun» «f:my-dlist» () 19 | («k:dolist» («v:arg» '«:lisp-extra-font-lock-quoted:(1 2 3)») 20 | nil)) 21 | 22 | «m:;; »«x:`«c:loop»'. 23 | »(«k:defun» «f:my-loop» («v:my-list») 24 | («k:cl-loop» «b:for» «v:(key . value)» «b:in» my-list 25 | «b:collect» key 26 | «b:collect» value)) 27 | -------------------------------------------------------------------------------- /test/files/demo.el: -------------------------------------------------------------------------------- 1 | ;;; demo.el --- Demonstration program for `lisp-extra-font-lock'. 2 | 3 | ;; The package `lisp-extra-font-lock' highlights variables bound by 4 | ;; `defun', `lambda', `let', `dolist', etc. It also highlight quoted 5 | ;; and backquoted expressions -- excluding any comma operator 6 | ;; expressions. 7 | 8 | (defun my-function (next) ; <- Parameters 9 | (let ((numbers '(one two three)) ; <- `let' and quoted expr 10 | (buffer-read-only t)) ; <- Special variable 11 | `(,@numbers and ,next))) ; <- Backquote and comma 12 | 13 | ;; (my-function 'four) => (one two three and four) 14 | 15 | ;;; demo.el ends here 16 | -------------------------------------------------------------------------------- /test/files/demo.el.faceup: -------------------------------------------------------------------------------- 1 | «m:;;; »«x:demo.el --- Demonstration program for `«c:lisp-extra-font-lock»'. 2 | » 3 | «m:;; »«x:The package `«c:lisp-extra-font-lock»' highlights variables bound by 4 | »«m:;; »«x:`«c:defun»', `«c:lambda»', `«c:let»', `«c:dolist»', etc. It also highlight quoted 5 | »«m:;; »«x:and backquoted expressions -- excluding any comma operator 6 | »«m:;; »«x:expressions. 7 | » 8 | («k:defun» «f:my-function» («v:next») «m:; »«x:<- Parameters 9 | » («k:let» ((«v:numbers» '«:lisp-extra-font-lock-quoted:(one two three)») «m:; »«x:<- `«c:let»' and quoted expr 10 | » («:lisp-extra-font-lock-special-variable-name:buffer-read-only» t)) «m:; »«x:<- Special variable 11 | » «:lisp-extra-font-lock-backquote:`»«:lisp-extra-font-lock-quoted:(»«:lisp-extra-font-lock-backquote:,@»numbers«:lisp-extra-font-lock-quoted: and »«:lisp-extra-font-lock-backquote:,»next«:lisp-extra-font-lock-quoted:)»)) «m:; »«x:<- Backquote and comma 12 | » 13 | «m:;; »«x:(my-function 'four) => (one two three and four) 14 | » 15 | «m:;;; »«x:demo.el ends here 16 | » -------------------------------------------------------------------------------- /test/files/exclude.el: -------------------------------------------------------------------------------- 1 | ;; Don't highlight quotes in certain contexts. 2 | 3 | ;; Don't highlight 'no in a comment. 4 | 5 | "Don't highlight 'no in a string." 6 | 7 | ;; Not in a character constant: 8 | 9 | (setq exclude-quote (cons ?' 'yes)) 10 | 11 | ;; Not in an escapted character constant: 12 | 13 | (setq exclude-quote (cons ?\' 'yes)) 14 | 15 | ;; exclude.el ends here. 16 | -------------------------------------------------------------------------------- /test/files/exclude.el.faceup: -------------------------------------------------------------------------------- 1 | «m:;; »«x:Don't highlight quotes in certain contexts. 2 | » 3 | «m:;; »«x:Don't highlight 'no in a comment. 4 | » 5 | «s:"Don't highlight 'no in a string."» 6 | 7 | «m:;; »«x:Not in a character constant: 8 | » 9 | («k:setq» exclude-quote (cons ?' '«:lisp-extra-font-lock-quoted:yes»)) 10 | 11 | «m:;; »«x:Not in an escapted character constant: 12 | » 13 | («k:setq» exclude-quote (cons ?\' '«:lisp-extra-font-lock-quoted:yes»)) 14 | 15 | «m:;; »«x:exclude.el ends here. 16 | » -------------------------------------------------------------------------------- /test/files/hash-quote.el: -------------------------------------------------------------------------------- 1 | ;; A #' should not trigger the normal quoting system. 2 | 3 | ;; Special case for hash-quoted symbols. 4 | #'symbol 5 | 6 | ;; Ensure the lambda isn't highlighted as a constant. 7 | (mapcar #'(lambda (x) (+ x 1)) '(1 2 3 4)) 8 | 9 | ;; Embedded hash-quoted lambda. 10 | '(alpha #'(lambda (x))) 11 | -------------------------------------------------------------------------------- /test/files/hash-quote.el.faceup: -------------------------------------------------------------------------------- 1 | «m:;; »«x:A #' should not trigger the normal quoting system. 2 | » 3 | «m:;; »«x:Special case for hash-quoted symbols. 4 | »#'«:lisp-extra-font-lock-quoted-function:symbol» 5 | 6 | «m:;; »«x:Ensure the lambda isn't highlighted as a constant. 7 | »(mapcar #'(lambda («v:x») (+ x 1)) '«:lisp-extra-font-lock-quoted:(1 2 3 4)») 8 | 9 | «m:;; »«x:Embedded hash-quoted lambda. 10 | »'«:lisp-extra-font-lock-quoted:(alpha #'(lambda («v:x»)))» 11 | -------------------------------------------------------------------------------- /test/lisp-extra-font-lock-test-files.el: -------------------------------------------------------------------------------- 1 | ;;; lisp-extra-font-lock-test-files.el --- Test for lisp-extra-font-lock. 2 | 3 | ;; Copyright (C) 2016 Anders Lindgren 4 | 5 | ;; Author: Anders Lindgren 6 | ;; Keywords: faces languages 7 | 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 3 of the License, or 11 | ;; (at your option) 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 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Regression test of `lisp-extra-font-lock', a package providing 24 | ;; additional font-lock rules for lisp languages. This module verifies 25 | ;; fontification of a number of files. This is done by keeing a text 26 | ;; representation of the fontification using `faceup' markup, in 27 | ;; addition to the original files. 28 | ;; 29 | ;; The actual check is performed using `ert', with font-lock test 30 | ;; function provided by `faceup'. 31 | 32 | ;;; Code: 33 | 34 | (require 'faceup) 35 | 36 | (defvar lisp-extra-font-lock-test-dir (faceup-this-file-directory)) 37 | 38 | (defun lisp-extra-font-lock-test-file (file) 39 | "Test that FILE is fontified as the .faceup file describes. 40 | 41 | FILE is interpreted as relative to this source directory." 42 | (faceup-test-font-lock-file '(emacs-lisp-mode 43 | lisp-extra-font-lock-mode) 44 | (concat 45 | lisp-extra-font-lock-test-dir 46 | file))) 47 | (faceup-defexplainer lisp-extra-font-lock-test-file) 48 | 49 | 50 | (ert-deftest lisp-extra-font-lock-test-files () 51 | (should (lisp-extra-font-lock-test-file "files/basics.el")) 52 | (should (lisp-extra-font-lock-test-file "files/demo.el")) 53 | (should (lisp-extra-font-lock-test-file "files/hash-quote.el")) 54 | (should (lisp-extra-font-lock-test-file "files/exclude.el"))) 55 | 56 | (provide 'lisp-extra-font-lock-test-files) 57 | 58 | ;; lisp-extra-font-lock-test-files.el ends here. 59 | -------------------------------------------------------------------------------- /test/lisp-extra-font-lock-test-setup.el: -------------------------------------------------------------------------------- 1 | ;;; lisp-extra-font-lock-test-setup.el --- Setup and execute all tests. 2 | 3 | ;;; Commentary: 4 | 5 | ;; This package sets up a suitable enviroment for testing 6 | ;; lisp-extra-font-lock, and executes the tests. 7 | ;; 8 | ;; Usage: 9 | ;; 10 | ;; emacs -q -l lisp-extra-font-lock-test-setup.el 11 | ;; 12 | ;; Note that this package assumes that some packages are located in 13 | ;; specific locations. 14 | ;; 15 | ;; Note that different Emacs versions highlight things slightly 16 | ;; differently. The corresponding .faceup file was generated using 17 | ;; Emacs 24.3. 18 | 19 | ;;; Code: 20 | 21 | (setq inhibit-startup-screen t) 22 | (prefer-coding-system 'utf-8) 23 | 24 | (defvar lisp-extra-font-lock-test-setup-dir 25 | (if load-file-name 26 | (file-name-directory load-file-name) 27 | default-directory)) 28 | 29 | (dolist (dir '("." ".." "../../faceup")) 30 | (add-to-list 'load-path (concat lisp-extra-font-lock-test-setup-dir dir))) 31 | 32 | ;; Emacs 25.1 contains a bug which prevents if from highlighting 33 | ;; "lambda" as a keyword, this may cause false errors. 34 | ;; 35 | ;; See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=23465 36 | (when (and (eq emacs-major-version 25) 37 | (eq emacs-minor-version 1)) 38 | (font-lock-add-keywords 'emacs-lisp-mode 39 | '(("\\_" (0 font-lock-keyword-face))))) 40 | 41 | (require 'lisp-extra-font-lock) 42 | (require 'lisp-extra-font-lock-test-files) 43 | 44 | (ert t) 45 | 46 | ;;; lisp-extra-font-lock-test-setup.el ends here 47 | --------------------------------------------------------------------------------