* lisp/hi-lock.el (hi-lock-set-pattern): Check for `font-lock-specified-p'.
[bpt/emacs.git] / lisp / hi-lock.el
CommitLineData
853c1ffc 1;;; hi-lock.el --- minor mode for interactive automatic highlighting -*- lexical-binding: t -*-
abb2db1c 2
ab422c4d 3;; Copyright (C) 2000-2013 Free Software Foundation, Inc.
abb2db1c 4
4c120732 5;; Author: David M. Koppelman <koppel@ece.lsu.edu>
abb2db1c
GM
6;; Keywords: faces, minor-mode, matching, display
7
8;; This file is part of GNU Emacs.
9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
abb2db1c 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
abb2db1c
GM
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
abb2db1c 22
e8af40ee 23;;; Commentary:
71296446 24;;
abb2db1c
GM
25;; With the hi-lock commands text matching interactively entered
26;; regexp's can be highlighted. For example, `M-x highlight-regexp
27;; RET clearly RET RET' will highlight all occurrences of `clearly'
28;; using a yellow background face. New occurrences of `clearly' will
29;; be highlighted as they are typed. `M-x unhighlight-regexp RET'
30;; will remove the highlighting. Any existing face can be used for
31;; highlighting and a set of appropriate faces is provided. The
32;; regexps can be written into the current buffer in a form that will
c898bef7
CY
33;; be recognized the next time the corresponding file is read (when
34;; file patterns is turned on).
abb2db1c
GM
35;;
36;; Applications:
37;;
38;; In program source code highlight a variable to quickly see all
39;; places it is modified or referenced:
e5e4a942 40;; M-x highlight-regexp RET ground_contact_switches_closed RET RET
abb2db1c
GM
41;;
42;; In a shell or other buffer that is showing lots of program
43;; output, highlight the parts of the output you're interested in:
e5e4a942 44;; M-x highlight-regexp RET Total execution time [0-9]+ RET hi-blue-b RET
abb2db1c
GM
45;;
46;; In buffers displaying tables, highlight the lines you're interested in:
e5e4a942 47;; M-x highlight-lines-matching-regexp RET January 2000 RET hi-black-b RET
abb2db1c
GM
48;;
49;; When writing text, highlight personal cliches. This can be
50;; amusing.
e5e4a942 51;; M-x highlight-phrase RET as can be seen RET RET
abb2db1c 52;;
108ee42b 53;; Setup:
abb2db1c 54;;
865fe16f 55;; Put the following code in your init file. This turns on
108ee42b 56;; hi-lock mode and adds a "Regexp Highlighting" entry
abb2db1c
GM
57;; to the edit menu.
58;;
71060bdd 59;; (global-hi-lock-mode 1)
71296446 60;;
c898bef7 61;; To enable the use of patterns found in files (presumably placed
865fe16f 62;; there by hi-lock) include the following in your init file:
c898bef7
CY
63;;
64;; (setq hi-lock-file-patterns-policy 'ask)
65;;
66;; If you get tired of being asked each time a file is loaded replace
06b1a5ef 67;; `ask' with a function that returns t if patterns should be read.
c898bef7 68;;
abb2db1c
GM
69;; You might also want to bind the hi-lock commands to more
70;; finger-friendly sequences:
71
72;; (define-key hi-lock-map "\C-z\C-h" 'highlight-lines-matching-regexp)
73;; (define-key hi-lock-map "\C-zi" 'hi-lock-find-patterns)
74;; (define-key hi-lock-map "\C-zh" 'highlight-regexp)
108ee42b 75;; (define-key hi-lock-map "\C-zp" 'highlight-phrase)
abb2db1c
GM
76;; (define-key hi-lock-map "\C-zr" 'unhighlight-regexp)
77;; (define-key hi-lock-map "\C-zb" 'hi-lock-write-interactive-patterns))
78
79;; See the documentation for hi-lock-mode `C-h f hi-lock-mode' for
80;; additional instructions.
81
82;; Sample file patterns:
83
84; Hi-lock: (("^;;; .*" (0 (quote hi-black-hb) t)))
85; Hi-lock: ( ("make-variable-buffer-\\(local\\)" (0 font-lock-keyword-face)(1 'italic append)))))
86; Hi-lock: end
87
88;;; Code:
89
befe199d 90(require 'font-lock)
abb2db1c 91
f43eaaef 92(defgroup hi-lock nil
abb2db1c 93 "Interactively add and remove font-lock patterns for highlighting text."
f43eaaef
JL
94 :link '(custom-manual "(emacs)Highlight Interactively")
95 :group 'font-lock)
abb2db1c 96
abb2db1c
GM
97(defcustom hi-lock-file-patterns-range 10000
98 "Limit of search in a buffer for hi-lock patterns.
8b730780 99When a file is visited and hi-lock mode is on, patterns starting
abb2db1c
GM
100up to this limit are added to font-lock's patterns. See documentation
101of functions `hi-lock-mode' and `hi-lock-find-patterns'."
102 :type 'integer
f43eaaef 103 :group 'hi-lock)
abb2db1c 104
81dc5714
JL
105(defcustom hi-lock-highlight-range 200000
106 "Size of area highlighted by hi-lock when font-lock not active.
107Font-lock is not active in buffers that do their own highlighting,
108such as the buffer created by `list-colors-display'. In those buffers
109hi-lock patterns will only be applied over a range of
110`hi-lock-highlight-range' characters. If font-lock is active then
111highlighting will be applied throughout the buffer."
112 :type 'integer
113 :group 'hi-lock)
114
abb2db1c
GM
115(defcustom hi-lock-exclude-modes
116 '(rmail-mode mime/viewer-mode gnus-article-mode)
117 "List of major modes in which hi-lock will not run.
118For security reasons since font lock patterns can specify function
119calls."
f5bd8092 120 :type '(repeat symbol)
f43eaaef 121 :group 'hi-lock)
abb2db1c 122
cf995676 123(defcustom hi-lock-file-patterns-policy 'ask
c898bef7 124 "Specify when hi-lock should use patterns found in file.
06b1a5ef 125If `ask', prompt when patterns found in buffer; if bound to a function,
c898bef7 126use patterns when function returns t (function is called with patterns
06b1a5ef 127as first argument); if nil or `never' or anything else, don't use file
176eb1f1
GM
128patterns."
129 :type '(choice (const :tag "Do not use file patterns" never)
130 (const :tag "Ask about file patterns" ask)
131 (function :tag "Function to check file patterns"))
132 :group 'hi-lock
133 :version "22.1")
134
135;; It can have a function value.
136(put 'hi-lock-file-patterns-policy 'risky-local-variable t)
abb2db1c 137
b85aec93
J
138(defcustom hi-lock-auto-select-face nil
139 "Non-nil if highlighting commands should not prompt for face names.
140When non-nil, each hi-lock command will cycle through faces in
853c1ffc 141`hi-lock-face-defaults' without prompting."
b85aec93
J
142 :type 'boolean
143 :version "24.4")
144
abb2db1c
GM
145(defgroup hi-lock-faces nil
146 "Faces for hi-lock."
f43eaaef
JL
147 :group 'hi-lock
148 :group 'faces)
abb2db1c
GM
149
150(defface hi-yellow
e0d815a2 151 '((((min-colors 88) (background dark))
ea81d57e
DN
152 (:background "yellow1" :foreground "black"))
153 (((background dark)) (:background "yellow" :foreground "black"))
154 (((min-colors 88)) (:background "yellow1"))
a0b8c939 155 (t (:background "yellow")))
abb2db1c
GM
156 "Default face for hi-lock mode."
157 :group 'hi-lock-faces)
158
159(defface hi-pink
16cdf141 160 '((((background dark)) (:background "pink" :foreground "black"))
a0b8c939 161 (t (:background "pink")))
abb2db1c
GM
162 "Face for hi-lock mode."
163 :group 'hi-lock-faces)
164
165(defface hi-green
e0d815a2 166 '((((min-colors 88) (background dark))
501158bc 167 (:background "light green" :foreground "black"))
ea81d57e 168 (((background dark)) (:background "green" :foreground "black"))
501158bc 169 (((min-colors 88)) (:background "light green"))
a0b8c939 170 (t (:background "green")))
abb2db1c
GM
171 "Face for hi-lock mode."
172 :group 'hi-lock-faces)
173
174(defface hi-blue
16cdf141 175 '((((background dark)) (:background "light blue" :foreground "black"))
a0b8c939 176 (t (:background "light blue")))
abb2db1c
GM
177 "Face for hi-lock mode."
178 :group 'hi-lock-faces)
179
180(defface hi-black-b
181 '((t (:weight bold)))
182 "Face for hi-lock mode."
183 :group 'hi-lock-faces)
184
185(defface hi-blue-b
ea81d57e
DN
186 '((((min-colors 88)) (:weight bold :foreground "blue1"))
187 (t (:weight bold :foreground "blue")))
abb2db1c
GM
188 "Face for hi-lock mode."
189 :group 'hi-lock-faces)
190
191(defface hi-green-b
ea81d57e
DN
192 '((((min-colors 88)) (:weight bold :foreground "green1"))
193 (t (:weight bold :foreground "green")))
abb2db1c
GM
194 "Face for hi-lock mode."
195 :group 'hi-lock-faces)
196
197(defface hi-red-b
ea81d57e
DN
198 '((((min-colors 88)) (:weight bold :foreground "red1"))
199 (t (:weight bold :foreground "red")))
abb2db1c
GM
200 "Face for hi-lock mode."
201 :group 'hi-lock-faces)
202
203(defface hi-black-hb
c3b27206 204 '((t (:weight bold :height 1.67 :inherit variable-pitch)))
abb2db1c
GM
205 "Face for hi-lock mode."
206 :group 'hi-lock-faces)
207
2e4ad7e5 208(defvar-local hi-lock-file-patterns nil
abb2db1c 209 "Patterns found in file for hi-lock. Should not be changed.")
2e4ad7e5 210(put 'hi-lock-file-patterns 'permanent-local t)
abb2db1c 211
2e4ad7e5 212(defvar-local hi-lock-interactive-patterns nil
abb2db1c 213 "Patterns provided to hi-lock by user. Should not be changed.")
2e4ad7e5 214(put 'hi-lock-interactive-patterns 'permanent-local t)
abb2db1c 215
a52c0aa0
SM
216(define-obsolete-variable-alias 'hi-lock-face-history
217 'hi-lock-face-defaults "23.1")
ef705f25
JL
218(defvar hi-lock-face-defaults
219 '("hi-yellow" "hi-pink" "hi-green" "hi-blue" "hi-black-b"
220 "hi-blue-b" "hi-red-b" "hi-green-b" "hi-black-hb")
221 "Default faces for hi-lock interactive functions.")
abb2db1c 222
ef705f25
JL
223(define-obsolete-variable-alias 'hi-lock-regexp-history
224 'regexp-history
225 "23.1")
abb2db1c
GM
226
227(defvar hi-lock-file-patterns-prefix "Hi-lock"
c898bef7 228 "Search target for finding hi-lock patterns at top of file.")
abb2db1c 229
71060bdd 230(defvar hi-lock-archaic-interface-message-used nil
90756087 231 "True if user alerted that `global-hi-lock-mode' is now the global switch.
8b730780 232Earlier versions of hi-lock used `hi-lock-mode' as the global switch;
90756087 233the message is issued if it appears that `hi-lock-mode' is used assuming
71060bdd
EZ
234that older functionality. This variable avoids multiple reminders.")
235
236(defvar hi-lock-archaic-interface-deduce nil
90756087
JL
237 "If non-nil, sometimes assume that `hi-lock-mode' means `global-hi-lock-mode'.
238Assumption is made if `hi-lock-mode' used in the *scratch* buffer while
71060bdd
EZ
239a library is being loaded.")
240
b016851c
SM
241(defvar hi-lock-menu
242 (let ((map (make-sparse-keymap "Hi Lock")))
243 (define-key-after map [highlight-regexp]
244 '(menu-item "Highlight Regexp..." highlight-regexp
245 :help "Highlight text matching PATTERN (a regexp)."))
246
247 (define-key-after map [highlight-phrase]
248 '(menu-item "Highlight Phrase..." highlight-phrase
249 :help "Highlight text matching PATTERN (a regexp processed to match phrases)."))
250
251 (define-key-after map [highlight-lines-matching-regexp]
252 '(menu-item "Highlight Lines..." highlight-lines-matching-regexp
253 :help "Highlight lines containing match of PATTERN (a regexp)."))
254
e5e4a942
JL
255 (define-key-after map [highlight-symbol-at-point]
256 '(menu-item "Highlight Symbol at Point" highlight-symbol-at-point
257 :help "Highlight symbol found near point without prompting."))
258
b016851c
SM
259 (define-key-after map [unhighlight-regexp]
260 '(menu-item "Remove Highlighting..." unhighlight-regexp
261 :help "Remove previously entered highlighting pattern."
262 :enable hi-lock-interactive-patterns))
263
264 (define-key-after map [hi-lock-write-interactive-patterns]
265 '(menu-item "Patterns to Buffer" hi-lock-write-interactive-patterns
266 :help "Insert interactively added REGEXPs into buffer at point."
267 :enable hi-lock-interactive-patterns))
268
269 (define-key-after map [hi-lock-find-patterns]
270 '(menu-item "Patterns from Buffer" hi-lock-find-patterns
271 :help "Use patterns (if any) near top of buffer."))
272 map)
abb2db1c
GM
273 "Menu for hi-lock mode.")
274
b016851c
SM
275(defvar hi-lock-map
276 (let ((map (make-sparse-keymap "Hi Lock")))
277 (define-key map "\C-xwi" 'hi-lock-find-patterns)
278 (define-key map "\C-xwl" 'highlight-lines-matching-regexp)
279 (define-key map "\C-xwp" 'highlight-phrase)
280 (define-key map "\C-xwh" 'highlight-regexp)
e5e4a942 281 (define-key map "\C-xw." 'highlight-symbol-at-point)
b016851c
SM
282 (define-key map "\C-xwr" 'unhighlight-regexp)
283 (define-key map "\C-xwb" 'hi-lock-write-interactive-patterns)
284 map)
abb2db1c
GM
285 "Key map for hi-lock.")
286
eb1a6e15
J
287(defvar hi-lock-read-regexp-defaults-function
288 'hi-lock-read-regexp-defaults
289 "Function that provides default regexp(s) for highlighting commands.
290This function should take no arguments and return one of nil, a
291regexp or a list of regexps for use with highlighting commands -
292`hi-lock-face-phrase-buffer', `hi-lock-line-face-buffer' and
293`hi-lock-face-buffer'. The return value of this function is used
294as DEFAULTS param of `read-regexp' while executing the
295highlighting command. This function is called only during
296interactive use.
297
298For example, to highlight at symbol at point use
299
300 \(setq hi-lock-read-regexp-defaults-function
301 'find-tag-default-as-regexp\)
302
303If you need different defaults for different highlighting
304operations, use `this-command' to identify the command under
305execution.")
306
abb2db1c
GM
307;; Visible Functions
308
abb2db1c 309;;;###autoload
71060bdd 310(define-minor-mode hi-lock-mode
06e21633
CY
311 "Toggle selective highlighting of patterns (Hi Lock mode).
312With a prefix argument ARG, enable Hi Lock mode if ARG is
313positive, and disable it otherwise. If called from Lisp, enable
314the mode if ARG is omitted or nil.
315
a9f6f311
CY
316Hi Lock mode is automatically enabled when you invoke any of the
317highlighting commands listed below, such as \\[highlight-regexp].
318To enable Hi Lock mode in all buffers, use `global-hi-lock-mode'
319or add (global-hi-lock-mode 1) to your init file.
320
321In buffers where Font Lock mode is enabled, patterns are
322highlighted using font lock. In buffers where Font Lock mode is
323disabled, patterns are applied using overlays; in this case, the
324highlighting will not be updated as you type.
325
326When Hi Lock mode is enabled, a \"Regexp Highlighting\" submenu
327is added to the \"Edit\" menu. The commands in the submenu,
328which can be called interactively, are:
abb2db1c
GM
329
330\\[highlight-regexp] REGEXP FACE
331 Highlight matches of pattern REGEXP in current buffer with FACE.
332
108ee42b
GM
333\\[highlight-phrase] PHRASE FACE
334 Highlight matches of phrase PHRASE in current buffer with FACE.
335 (PHRASE can be any REGEXP, but spaces will be replaced by matches
336 to whitespace and initial lower-case letters will become case insensitive.)
71296446 337
abb2db1c
GM
338\\[highlight-lines-matching-regexp] REGEXP FACE
339 Highlight lines containing matches of REGEXP in current buffer with FACE.
340
e5e4a942
JL
341\\[highlight-symbol-at-point]
342 Highlight the symbol found near point without prompting, using the next
343 available face automatically.
344
abb2db1c
GM
345\\[unhighlight-regexp] REGEXP
346 Remove highlighting on matches of REGEXP in current buffer.
347
348\\[hi-lock-write-interactive-patterns]
c898bef7 349 Write active REGEXPs into buffer as comments (if possible). They may
abb2db1c
GM
350 be read the next time file is loaded or when the \\[hi-lock-find-patterns] command
351 is issued. The inserted regexps are in the form of font lock keywords.
ee44464c 352 (See `font-lock-keywords'.) They may be edited and re-loaded with \\[hi-lock-find-patterns],
869d3e17
JB
353 any valid `font-lock-keywords' form is acceptable. When a file is
354 loaded the patterns are read if `hi-lock-file-patterns-policy' is
c898bef7
CY
355 'ask and the user responds y to the prompt, or if
356 `hi-lock-file-patterns-policy' is bound to a function and that
357 function returns t.
abb2db1c
GM
358
359\\[hi-lock-find-patterns]
360 Re-read patterns stored in buffer (in the format produced by \\[hi-lock-write-interactive-patterns]).
361
c898bef7
CY
362When hi-lock is started and if the mode is not excluded or patterns
363rejected, the beginning of the buffer is searched for lines of the
364form:
abb2db1c 365 Hi-lock: FOO
a9f6f311
CY
366
367where FOO is a list of patterns. The patterns must start before
368position \(number of characters into buffer)
369`hi-lock-file-patterns-range'. Patterns will be read until
370Hi-lock: end is found. A mode is excluded if it's in the list
371`hi-lock-exclude-modes'."
963b2040 372 :group 'hi-lock
90756087
JL
373 :lighter (:eval (if (or hi-lock-interactive-patterns
374 hi-lock-file-patterns)
375 " Hi" ""))
963b2040
CY
376 :global nil
377 :keymap hi-lock-map
71060bdd
EZ
378 (when (and (equal (buffer-name) "*scratch*")
379 load-in-progress
32226619 380 (not (called-interactively-p 'interactive))
71060bdd
EZ
381 (not hi-lock-archaic-interface-message-used))
382 (setq hi-lock-archaic-interface-message-used t)
383 (if hi-lock-archaic-interface-deduce
384 (global-hi-lock-mode hi-lock-mode)
385 (warn
386 "Possible archaic use of (hi-lock-mode).
387Use (global-hi-lock-mode 1) in .emacs to enable hi-lock for all buffers,
90756087 388use (hi-lock-mode 1) for individual buffers. For compatibility with Emacs
865fe16f 389versions before 22 use the following in your init file:
71060bdd
EZ
390
391 (if (functionp 'global-hi-lock-mode)
392 (global-hi-lock-mode 1)
393 (hi-lock-mode 1))
394")))
395 (if hi-lock-mode
963b2040
CY
396 ;; Turned on.
397 (progn
398 (define-key-after menu-bar-edit-menu [hi-lock]
399 (cons "Regexp Highlighting" hi-lock-menu))
400 (hi-lock-find-patterns)
2bd8a4a8
SM
401 (add-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook nil t)
402 ;; Remove regexps from font-lock-keywords (bug#13891).
403 (add-hook 'change-major-mode-hook (lambda () (hi-lock-mode -1)) nil t))
abb2db1c 404 ;; Turned off.
e4d59066
CY
405 (when (or hi-lock-interactive-patterns
406 hi-lock-file-patterns)
71060bdd 407 (when hi-lock-interactive-patterns
e4d59066
CY
408 (font-lock-remove-keywords nil hi-lock-interactive-patterns)
409 (setq hi-lock-interactive-patterns nil))
410 (when hi-lock-file-patterns
411 (font-lock-remove-keywords nil hi-lock-file-patterns)
412 (setq hi-lock-file-patterns nil))
81dc5714
JL
413 (remove-overlays nil nil 'hi-lock-overlay t)
414 (when font-lock-fontified (font-lock-fontify-buffer)))
963b2040
CY
415 (define-key-after menu-bar-edit-menu [hi-lock] nil)
416 (remove-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook t)))
abb2db1c 417
963b2040 418;;;###autoload
98007d83 419(define-globalized-minor-mode global-hi-lock-mode
71060bdd 420 hi-lock-mode turn-on-hi-lock-if-enabled
9cb4bb45 421 :group 'hi-lock)
71060bdd 422
963b2040 423(defun turn-on-hi-lock-if-enabled ()
71060bdd 424 (setq hi-lock-archaic-interface-message-used t)
963b2040 425 (unless (memq major-mode hi-lock-exclude-modes)
71060bdd 426 (hi-lock-mode 1)))
abb2db1c
GM
427
428;;;###autoload
429(defalias 'highlight-lines-matching-regexp 'hi-lock-line-face-buffer)
430;;;###autoload
431(defun hi-lock-line-face-buffer (regexp &optional face)
108ee42b 432 "Set face of all lines containing a match of REGEXP to FACE.
eb1a6e15
J
433Interactively, prompt for REGEXP then FACE. Use
434`hi-lock-read-regexp-defaults-function' to retrieve default
435value(s) of REGEXP. Use the global history list for FACE.
abb2db1c 436
eb1a6e15
J
437Use Font lock mode, if enabled, to highlight REGEXP. Otherwise,
438use overlays for highlighting. If overlays are used, the
439highlighting will not update as you type."
abb2db1c
GM
440 (interactive
441 (list
8677dea3 442 (hi-lock-regexp-okay
eb1a6e15
J
443 (read-regexp "Regexp to highlight line"
444 (funcall hi-lock-read-regexp-defaults-function)))
abb2db1c 445 (hi-lock-read-face-name)))
597767da 446 (or (facep face) (setq face 'hi-yellow))
71060bdd 447 (unless hi-lock-mode (hi-lock-mode 1))
abb2db1c 448 (hi-lock-set-pattern
b18f5523
SM
449 ;; The \\(?:...\\) grouping construct ensures that a leading ^, +, * or ?
450 ;; or a trailing $ in REGEXP will be interpreted correctly.
963b2040 451 (concat "^.*\\(?:" regexp "\\).*$") face))
abb2db1c 452
108ee42b 453
abb2db1c
GM
454;;;###autoload
455(defalias 'highlight-regexp 'hi-lock-face-buffer)
456;;;###autoload
457(defun hi-lock-face-buffer (regexp &optional face)
108ee42b 458 "Set face of each match of REGEXP to FACE.
eb1a6e15
J
459Interactively, prompt for REGEXP then FACE. Use
460`hi-lock-read-regexp-defaults-function' to retrieve default
461value(s) REGEXP. Use the global history list for FACE.
abb2db1c 462
eb1a6e15
J
463Use Font lock mode, if enabled, to highlight REGEXP. Otherwise,
464use overlays for highlighting. If overlays are used, the
465highlighting will not update as you type."
abb2db1c
GM
466 (interactive
467 (list
8677dea3 468 (hi-lock-regexp-okay
eb1a6e15
J
469 (read-regexp "Regexp to highlight"
470 (funcall hi-lock-read-regexp-defaults-function)))
abb2db1c 471 (hi-lock-read-face-name)))
597767da 472 (or (facep face) (setq face 'hi-yellow))
71060bdd 473 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 474 (hi-lock-set-pattern regexp face))
abb2db1c 475
108ee42b
GM
476;;;###autoload
477(defalias 'highlight-phrase 'hi-lock-face-phrase-buffer)
478;;;###autoload
479(defun hi-lock-face-phrase-buffer (regexp &optional face)
480 "Set face of each match of phrase REGEXP to FACE.
eb1a6e15
J
481Interactively, prompt for REGEXP then FACE. Use
482`hi-lock-read-regexp-defaults-function' to retrieve default
483value(s) of REGEXP. Use the global history list for FACE. When
484called interactively, replace whitespace in user provided regexp
485with arbitrary whitespace and make initial lower-case letters
486case-insensitive before highlighting with `hi-lock-set-pattern'.
487
488Use Font lock mode, if enabled, to highlight REGEXP. Otherwise,
489use overlays for highlighting. If overlays are used, the
490highlighting will not update as you type."
108ee42b
GM
491 (interactive
492 (list
493 (hi-lock-regexp-okay
494 (hi-lock-process-phrase
eb1a6e15
J
495 (read-regexp "Phrase to highlight"
496 (funcall hi-lock-read-regexp-defaults-function))))
108ee42b 497 (hi-lock-read-face-name)))
597767da 498 (or (facep face) (setq face 'hi-yellow))
71060bdd 499 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 500 (hi-lock-set-pattern regexp face))
108ee42b 501
e5e4a942
JL
502;;;###autoload
503(defalias 'highlight-symbol-at-point 'hi-lock-face-symbol-at-point)
504;;;###autoload
505(defun hi-lock-face-symbol-at-point ()
506 "Set face of each match of the symbol at point.
507Use `find-tag-default-as-regexp' to retrieve the symbol at point.
508Use non-nil `hi-lock-auto-select-face' to retrieve the next face
509from `hi-lock-face-defaults' automatically.
510
511Use Font lock mode, if enabled, to highlight symbol at point.
512Otherwise, use overlays for highlighting. If overlays are used,
513the highlighting will not update as you type."
514 (interactive)
515 (let* ((regexp (hi-lock-regexp-okay
516 (find-tag-default-as-regexp)))
517 (hi-lock-auto-select-face t)
518 (face (hi-lock-read-face-name)))
519 (or (facep face) (setq face 'hi-yellow))
520 (unless hi-lock-mode (hi-lock-mode 1))
521 (hi-lock-set-pattern regexp face)))
522
c868b919
J
523(defun hi-lock-keyword->face (keyword)
524 (cadr (cadr (cadr keyword)))) ; Keyword looks like (REGEXP (0 'FACE) ...).
525
e8a11b22 526(declare-function x-popup-menu "menu.c" (position menu))
f2d9c15f 527
b85aec93
J
528(defun hi-lock--regexps-at-point ()
529 (let ((regexps '()))
530 ;; When using overlays, there is no ambiguity on the best
531 ;; choice of regexp.
853c1ffc
SM
532 (let ((regexp (get-char-property (point) 'hi-lock-overlay-regexp)))
533 (when regexp (push regexp regexps)))
c868b919 534 ;; With font-locking on, check if the cursor is on a highlighted text.
ed6f2cd4
SM
535 (let ((face-after (get-text-property (point) 'face))
536 (face-before
537 (unless (bobp) (get-text-property (1- (point)) 'face)))
538 (faces (mapcar #'hi-lock-keyword->face
539 hi-lock-interactive-patterns)))
540 (unless (memq face-before faces) (setq face-before nil))
541 (unless (memq face-after faces) (setq face-after nil))
542 (when (and face-before face-after (not (eq face-before face-after)))
543 (setq face-before nil))
544 (when (or face-after face-before)
545 (let* ((hi-text
546 (buffer-substring-no-properties
547 (if face-before
548 (or (previous-single-property-change (point) 'face)
549 (point-min))
550 (point))
551 (if face-after
552 (or (next-single-property-change (point) 'face)
553 (point-max))
554 (point)))))
555 ;; Compute hi-lock patterns that match the
556 ;; highlighted text at point. Use this later in
557 ;; during completing-read.
558 (dolist (hi-lock-pattern hi-lock-interactive-patterns)
559 (let ((regexp (car hi-lock-pattern)))
560 (if (string-match regexp hi-text)
561 (push regexp regexps)))))))
2e4ad7e5 562 regexps))
b85aec93 563
c868b919
J
564(defvar-local hi-lock--unused-faces nil
565 "List of faces that is not used and is available for highlighting new text.
566Face names from this list come from `hi-lock-face-defaults'.")
853c1ffc 567
abb2db1c
GM
568;;;###autoload
569(defalias 'unhighlight-regexp 'hi-lock-unface-buffer)
570;;;###autoload
571(defun hi-lock-unface-buffer (regexp)
108ee42b 572 "Remove highlighting of each match to REGEXP set by hi-lock.
a9f6f311 573Interactively, prompt for REGEXP, accepting only regexps
b85aec93
J
574previously inserted by hi-lock interactive functions.
575If REGEXP is t (or if \\[universal-argument] was specified interactively),
576then remove all hi-lock highlighting."
abb2db1c 577 (interactive
b85aec93
J
578 (cond
579 (current-prefix-arg (list t))
580 ((and (display-popup-menus-p)
581 (listp last-nonmenu-event)
582 use-dialog-box)
583 (catch 'snafu
584 (or
585 (x-popup-menu
586 t
587 (cons
588 `keymap
589 (cons "Select Pattern to Unhighlight"
590 (mapcar (lambda (pattern)
591 (list (car pattern)
592 (format
593 "%s (%s)" (car pattern)
c868b919 594 (hi-lock-keyword->face pattern))
b85aec93
J
595 (cons nil nil)
596 (car pattern)))
597 hi-lock-interactive-patterns))))
598 ;; If the user clicks outside the menu, meaning that they
599 ;; change their mind, x-popup-menu returns nil, and
600 ;; interactive signals a wrong number of arguments error.
601 ;; To prevent that, we return an empty string, which will
602 ;; effectively disable the rest of the function.
603 (throw 'snafu '("")))))
604 (t
605 ;; Un-highlighting triggered via keyboard action.
606 (unless hi-lock-interactive-patterns
607 (error "No highlighting to remove"))
608 ;; Infer the regexp to un-highlight based on cursor position.
2e4ad7e5
SM
609 (let* ((defaults (or (hi-lock--regexps-at-point)
610 (mapcar #'car hi-lock-interactive-patterns))))
abb2db1c 611 (list
b85aec93
J
612 (completing-read (if (null defaults)
613 "Regexp to unhighlight: "
614 (format "Regexp to unhighlight (default %s): "
615 (car defaults)))
616 hi-lock-interactive-patterns
617 nil t nil nil defaults))))))
618 (dolist (keyword (if (eq regexp t) hi-lock-interactive-patterns
619 (list (assoc regexp hi-lock-interactive-patterns))))
abb2db1c 620 (when keyword
c868b919 621 (let ((face (hi-lock-keyword->face keyword)))
853c1ffc 622 ;; Make `face' the next one to use by default.
b2dc4f52
SM
623 (when (symbolp face) ;Don't add it if it's a list (bug#13297).
624 (add-to-list 'hi-lock--unused-faces (face-name face))))
abb2db1c
GM
625 (font-lock-remove-keywords nil (list keyword))
626 (setq hi-lock-interactive-patterns
627 (delq keyword hi-lock-interactive-patterns))
81dc5714 628 (remove-overlays
c868b919 629 nil nil 'hi-lock-overlay-regexp (hi-lock--hashcons (car keyword)))
81dc5714 630 (when font-lock-fontified (font-lock-fontify-buffer)))))
abb2db1c
GM
631
632;;;###autoload
633(defun hi-lock-write-interactive-patterns ()
634 "Write interactively added patterns, if any, into buffer at point.
635
636Interactively added patterns are those normally specified using
637`highlight-regexp' and `highlight-lines-matching-regexp'; they can
638be found in variable `hi-lock-interactive-patterns'."
639 (interactive)
597767da
CY
640 (if (null hi-lock-interactive-patterns)
641 (error "There are no interactive patterns"))
642 (let ((beg (point)))
ee44464c 643 (mapc
abb2db1c 644 (lambda (pattern)
90756087
JL
645 (insert (format "%s: (%s)\n"
646 hi-lock-file-patterns-prefix
647 (prin1-to-string pattern))))
597767da
CY
648 hi-lock-interactive-patterns)
649 (comment-region beg (point)))
650 (when (> (point) hi-lock-file-patterns-range)
651 (warn "Inserted keywords not close enough to top of file")))
abb2db1c
GM
652
653;; Implementation Functions
654
108ee42b
GM
655(defun hi-lock-process-phrase (phrase)
656 "Convert regexp PHRASE to a regexp that matches phrases.
657
658Blanks in PHRASE replaced by regexp that matches arbitrary whitespace
659and initial lower-case letters made case insensitive."
660 (let ((mod-phrase nil))
82ed3ab4 661 ;; FIXME fragile; better to just bind case-fold-search? (Bug#7161)
108ee42b
GM
662 (setq mod-phrase
663 (replace-regexp-in-string
1595ecfa
GM
664 "\\(^\\|\\s-\\)\\([a-z]\\)"
665 (lambda (m) (format "%s[%s%s]"
666 (match-string 1 m)
667 (upcase (match-string 2 m))
668 (match-string 2 m))) phrase))
82ed3ab4 669 ;; FIXME fragile; better to use search-spaces-regexp?
108ee42b
GM
670 (setq mod-phrase
671 (replace-regexp-in-string
672 "\\s-+" "[ \t\n]+" mod-phrase nil t))))
673
abb2db1c
GM
674(defun hi-lock-regexp-okay (regexp)
675 "Return REGEXP if it appears suitable for a font-lock pattern.
676
677Otherwise signal an error. A pattern that matches the null string is
678not suitable."
679 (if (string-match regexp "")
680 (error "Regexp cannot match an empty string")
681 regexp))
682
eb1a6e15
J
683(defun hi-lock-read-regexp-defaults ()
684 "Return the latest regexp from `regexp-history'.
685See `hi-lock-read-regexp-defaults-function' for details."
686 (car regexp-history))
687
abb2db1c 688(defun hi-lock-read-face-name ()
853c1ffc 689 "Return face for interactive highlighting.
b85aec93
J
690When `hi-lock-auto-select-face' is non-nil, just return the next face.
691Otherwise, read face name from minibuffer with completion and history."
c868b919
J
692 (unless hi-lock-interactive-patterns
693 (setq hi-lock--unused-faces hi-lock-face-defaults))
694 (let* ((last-used-face
695 (when hi-lock-interactive-patterns
696 (face-name (hi-lock-keyword->face
697 (car hi-lock-interactive-patterns)))))
698 (defaults (append hi-lock--unused-faces
699 (cdr (member last-used-face hi-lock-face-defaults))
700 hi-lock-face-defaults))
701 face)
853c1ffc 702 (if (and hi-lock-auto-select-face (not current-prefix-arg))
c868b919
J
703 (setq face (or (pop hi-lock--unused-faces) (car defaults)))
704 (setq face (completing-read
705 (format "Highlight using face (default %s): "
706 (car defaults))
707 obarray 'facep t nil 'face-name-history defaults))
708 ;; Update list of un-used faces.
709 (setq hi-lock--unused-faces (remove face hi-lock--unused-faces))
710 ;; Grow the list of defaults.
711 (add-to-list 'hi-lock-face-defaults face t))
712 (intern face)))
abb2db1c 713
963b2040
CY
714(defun hi-lock-set-pattern (regexp face)
715 "Highlight REGEXP with face FACE."
853c1ffc
SM
716 ;; Hashcons the regexp, so it can be passed to remove-overlays later.
717 (setq regexp (hi-lock--hashcons regexp))
501158bc 718 (let ((pattern (list regexp (list 0 (list 'quote face) 'prepend))))
c868b919
J
719 ;; Refuse to highlight a text that is already highlighted.
720 (unless (assoc regexp hi-lock-interactive-patterns)
963b2040 721 (push pattern hi-lock-interactive-patterns)
137f57c8 722 (if (and font-lock-mode (font-lock-specified-p major-mode))
c58059f2
DK
723 (progn
724 (font-lock-add-keywords nil (list pattern) t)
725 (font-lock-fontify-buffer))
853c1ffc 726 (let* ((range-min (- (point) (/ hi-lock-highlight-range 2)))
81dc5714
JL
727 (range-max (+ (point) (/ hi-lock-highlight-range 2)))
728 (search-start
729 (max (point-min)
730 (- range-min (max 0 (- range-max (point-max))))))
731 (search-end
732 (min (point-max)
733 (+ range-max (max 0 (- (point-min) range-min))))))
734 (save-excursion
735 (goto-char search-start)
736 (while (re-search-forward regexp search-end t)
737 (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
738 (overlay-put overlay 'hi-lock-overlay t)
853c1ffc 739 (overlay-put overlay 'hi-lock-overlay-regexp regexp)
81dc5714
JL
740 (overlay-put overlay 'face face))
741 (goto-char (match-end 0)))))))))
abb2db1c
GM
742
743(defun hi-lock-set-file-patterns (patterns)
744 "Replace file patterns list with PATTERNS and refontify."
108ee42b
GM
745 (when (or hi-lock-file-patterns patterns)
746 (font-lock-remove-keywords nil hi-lock-file-patterns)
747 (setq hi-lock-file-patterns patterns)
90756087 748 (font-lock-add-keywords nil hi-lock-file-patterns t)
e4d59066 749 (font-lock-fontify-buffer)))
abb2db1c
GM
750
751(defun hi-lock-find-patterns ()
752 "Find patterns in current buffer for hi-lock."
753 (interactive)
754 (unless (memq major-mode hi-lock-exclude-modes)
755 (let ((all-patterns nil)
756 (target-regexp (concat "\\<" hi-lock-file-patterns-prefix ":")))
757 (save-excursion
62cec9fe
SM
758 (save-restriction
759 (widen)
760 (goto-char (point-min))
761 (re-search-forward target-regexp
762 (+ (point) hi-lock-file-patterns-range) t)
763 (beginning-of-line)
764 (while (and (re-search-forward target-regexp (+ (point) 100) t)
765 (not (looking-at "\\s-*end")))
ed6773fa
JB
766 (condition-case nil
767 (setq all-patterns (append (read (current-buffer)) all-patterns))
768 (error (message "Invalid pattern list expression at %d"
963b2040 769 (line-number-at-pos)))))))
c898bef7
CY
770 (when (and all-patterns
771 hi-lock-mode
772 (cond
773 ((eq this-command 'hi-lock-find-patterns) t)
774 ((functionp hi-lock-file-patterns-policy)
775 (funcall hi-lock-file-patterns-policy all-patterns))
776 ((eq hi-lock-file-patterns-policy 'ask)
777 (y-or-n-p "Add patterns from this buffer to hi-lock? "))
778 (t nil)))
779 (hi-lock-set-file-patterns all-patterns)
32226619 780 (if (called-interactively-p 'interactive)
c898bef7 781 (message "Hi-lock added %d patterns." (length all-patterns)))))))
abb2db1c
GM
782
783(defun hi-lock-font-lock-hook ()
8b730780 784 "Add hi-lock patterns to font-lock's."
c58059f2
DK
785 (when font-lock-fontified
786 (font-lock-add-keywords nil hi-lock-file-patterns t)
787 (font-lock-add-keywords nil hi-lock-interactive-patterns t)))
abb2db1c 788
853c1ffc
SM
789(defvar hi-lock--hashcons-hash
790 (make-hash-table :test 'equal :weakness t)
791 "Hash table used to hash cons regexps.")
81dc5714 792
853c1ffc
SM
793(defun hi-lock--hashcons (string)
794 "Return unique object equal to STRING."
795 (or (gethash string hi-lock--hashcons-hash)
796 (puthash string string hi-lock--hashcons-hash)))
81dc5714 797
869d3e17
JB
798(defun hi-lock-unload-function ()
799 "Unload the Hi-Lock library."
800 (global-hi-lock-mode -1)
801 ;; continue standard unloading
802 nil)
803
abb2db1c
GM
804(provide 'hi-lock)
805
806;;; hi-lock.el ends here