Switch to recommended form of GPLv3 permissions notice.
[bpt/emacs.git] / lisp / hi-lock.el
CommitLineData
e8af40ee 1;;; hi-lock.el --- minor mode for interactive automatic highlighting
abb2db1c 2
0d30b337 3;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
409cc4a3 4;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
abb2db1c 5
c898bef7 6;; Author: David M. Koppelman, koppel@ece.lsu.edu
abb2db1c
GM
7;; Keywords: faces, minor-mode, matching, display
8
9;; This file is part of GNU Emacs.
10
eb3fa2cf 11;; GNU Emacs is free software: you can redistribute it and/or modify
abb2db1c 12;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
abb2db1c
GM
15
16;; GNU Emacs 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
eb3fa2cf 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
abb2db1c 23
e8af40ee 24;;; Commentary:
71296446 25;;
abb2db1c
GM
26;; With the hi-lock commands text matching interactively entered
27;; regexp's can be highlighted. For example, `M-x highlight-regexp
28;; RET clearly RET RET' will highlight all occurrences of `clearly'
29;; using a yellow background face. New occurrences of `clearly' will
30;; be highlighted as they are typed. `M-x unhighlight-regexp RET'
31;; will remove the highlighting. Any existing face can be used for
32;; highlighting and a set of appropriate faces is provided. The
33;; regexps can be written into the current buffer in a form that will
c898bef7
CY
34;; be recognized the next time the corresponding file is read (when
35;; file patterns is turned on).
abb2db1c
GM
36;;
37;; Applications:
38;;
39;; In program source code highlight a variable to quickly see all
40;; places it is modified or referenced:
41;; M-x highlight-regexp ground_contact_switches_closed RET RET
42;;
43;; In a shell or other buffer that is showing lots of program
44;; output, highlight the parts of the output you're interested in:
45;; M-x highlight-regexp Total execution time [0-9]+ RET hi-blue-b RET
46;;
47;; In buffers displaying tables, highlight the lines you're interested in:
48;; M-x highlight-lines-matching-regexp January 2000 RET hi-black-b RET
49;;
50;; When writing text, highlight personal cliches. This can be
51;; amusing.
108ee42b 52;; M-x highlight-phrase as can be seen RET RET
abb2db1c 53;;
108ee42b 54;; Setup:
abb2db1c
GM
55;;
56;; Put the following code in your .emacs file. This turns on
108ee42b 57;; hi-lock mode and adds a "Regexp Highlighting" entry
abb2db1c
GM
58;; to the edit menu.
59;;
71060bdd 60;; (global-hi-lock-mode 1)
71296446 61;;
c898bef7
CY
62;; To enable the use of patterns found in files (presumably placed
63;; there by hi-lock) include the following in your .emacs file:
64;;
65;; (setq hi-lock-file-patterns-policy 'ask)
66;;
67;; If you get tired of being asked each time a file is loaded replace
06b1a5ef 68;; `ask' with a function that returns t if patterns should be read.
c898bef7 69;;
abb2db1c
GM
70;; You might also want to bind the hi-lock commands to more
71;; finger-friendly sequences:
72
73;; (define-key hi-lock-map "\C-z\C-h" 'highlight-lines-matching-regexp)
74;; (define-key hi-lock-map "\C-zi" 'hi-lock-find-patterns)
75;; (define-key hi-lock-map "\C-zh" 'highlight-regexp)
108ee42b 76;; (define-key hi-lock-map "\C-zp" 'highlight-phrase)
abb2db1c
GM
77;; (define-key hi-lock-map "\C-zr" 'unhighlight-regexp)
78;; (define-key hi-lock-map "\C-zb" 'hi-lock-write-interactive-patterns))
79
80;; See the documentation for hi-lock-mode `C-h f hi-lock-mode' for
81;; additional instructions.
82
83;; Sample file patterns:
84
85; Hi-lock: (("^;;; .*" (0 (quote hi-black-hb) t)))
86; Hi-lock: ( ("make-variable-buffer-\\(local\\)" (0 font-lock-keyword-face)(1 'italic append)))))
87; Hi-lock: end
88
89;;; Code:
90
588aca27 91(eval-and-compile
abb2db1c
GM
92 (require 'font-lock))
93
f43eaaef 94(defgroup hi-lock nil
abb2db1c 95 "Interactively add and remove font-lock patterns for highlighting text."
f43eaaef
JL
96 :link '(custom-manual "(emacs)Highlight Interactively")
97 :group 'font-lock)
abb2db1c 98
abb2db1c
GM
99(defcustom hi-lock-file-patterns-range 10000
100 "Limit of search in a buffer for hi-lock patterns.
8b730780 101When a file is visited and hi-lock mode is on, patterns starting
abb2db1c
GM
102up to this limit are added to font-lock's patterns. See documentation
103of functions `hi-lock-mode' and `hi-lock-find-patterns'."
104 :type 'integer
f43eaaef 105 :group 'hi-lock)
abb2db1c 106
81dc5714
JL
107(defcustom hi-lock-highlight-range 200000
108 "Size of area highlighted by hi-lock when font-lock not active.
109Font-lock is not active in buffers that do their own highlighting,
110such as the buffer created by `list-colors-display'. In those buffers
111hi-lock patterns will only be applied over a range of
112`hi-lock-highlight-range' characters. If font-lock is active then
113highlighting will be applied throughout the buffer."
114 :type 'integer
115 :group 'hi-lock)
116
abb2db1c
GM
117(defcustom hi-lock-exclude-modes
118 '(rmail-mode mime/viewer-mode gnus-article-mode)
119 "List of major modes in which hi-lock will not run.
120For security reasons since font lock patterns can specify function
121calls."
f5bd8092 122 :type '(repeat symbol)
f43eaaef 123 :group 'hi-lock)
abb2db1c 124
cf995676 125(defcustom hi-lock-file-patterns-policy 'ask
c898bef7 126 "Specify when hi-lock should use patterns found in file.
06b1a5ef 127If `ask', prompt when patterns found in buffer; if bound to a function,
c898bef7 128use patterns when function returns t (function is called with patterns
06b1a5ef 129as first argument); if nil or `never' or anything else, don't use file
176eb1f1
GM
130patterns."
131 :type '(choice (const :tag "Do not use file patterns" never)
132 (const :tag "Ask about file patterns" ask)
133 (function :tag "Function to check file patterns"))
134 :group 'hi-lock
135 :version "22.1")
136
137;; It can have a function value.
138(put 'hi-lock-file-patterns-policy 'risky-local-variable t)
abb2db1c
GM
139
140(defgroup hi-lock-faces nil
141 "Faces for hi-lock."
f43eaaef
JL
142 :group 'hi-lock
143 :group 'faces)
abb2db1c
GM
144
145(defface hi-yellow
e0d815a2 146 '((((min-colors 88) (background dark))
ea81d57e
DN
147 (:background "yellow1" :foreground "black"))
148 (((background dark)) (:background "yellow" :foreground "black"))
149 (((min-colors 88)) (:background "yellow1"))
a0b8c939 150 (t (:background "yellow")))
abb2db1c
GM
151 "Default face for hi-lock mode."
152 :group 'hi-lock-faces)
153
154(defface hi-pink
16cdf141 155 '((((background dark)) (:background "pink" :foreground "black"))
a0b8c939 156 (t (:background "pink")))
abb2db1c
GM
157 "Face for hi-lock mode."
158 :group 'hi-lock-faces)
159
160(defface hi-green
e0d815a2 161 '((((min-colors 88) (background dark))
ea81d57e
DN
162 (:background "green1" :foreground "black"))
163 (((background dark)) (:background "green" :foreground "black"))
e0d815a2 164 (((min-colors 88)) (:background "green1"))
a0b8c939 165 (t (:background "green")))
abb2db1c
GM
166 "Face for hi-lock mode."
167 :group 'hi-lock-faces)
168
169(defface hi-blue
16cdf141 170 '((((background dark)) (:background "light blue" :foreground "black"))
a0b8c939 171 (t (:background "light blue")))
abb2db1c
GM
172 "Face for hi-lock mode."
173 :group 'hi-lock-faces)
174
175(defface hi-black-b
176 '((t (:weight bold)))
177 "Face for hi-lock mode."
178 :group 'hi-lock-faces)
179
180(defface hi-blue-b
ea81d57e
DN
181 '((((min-colors 88)) (:weight bold :foreground "blue1"))
182 (t (:weight bold :foreground "blue")))
abb2db1c
GM
183 "Face for hi-lock mode."
184 :group 'hi-lock-faces)
185
186(defface hi-green-b
ea81d57e
DN
187 '((((min-colors 88)) (:weight bold :foreground "green1"))
188 (t (:weight bold :foreground "green")))
abb2db1c
GM
189 "Face for hi-lock mode."
190 :group 'hi-lock-faces)
191
192(defface hi-red-b
ea81d57e
DN
193 '((((min-colors 88)) (:weight bold :foreground "red1"))
194 (t (:weight bold :foreground "red")))
abb2db1c
GM
195 "Face for hi-lock mode."
196 :group 'hi-lock-faces)
197
198(defface hi-black-hb
c3b27206 199 '((t (:weight bold :height 1.67 :inherit variable-pitch)))
abb2db1c
GM
200 "Face for hi-lock mode."
201 :group 'hi-lock-faces)
202
203(defvar hi-lock-file-patterns nil
204 "Patterns found in file for hi-lock. Should not be changed.")
205
206(defvar hi-lock-interactive-patterns nil
207 "Patterns provided to hi-lock by user. Should not be changed.")
208
209(defvar hi-lock-face-history
210 (list "hi-yellow" "hi-pink" "hi-green" "hi-blue" "hi-black-b"
211 "hi-blue-b" "hi-red-b" "hi-green-b" "hi-black-hb")
212 "History list of faces for hi-lock interactive functions.")
213
214;(dolist (f hi-lock-face-history) (unless (facep f) (error "%s not a face" f)))
215
216(defvar hi-lock-regexp-history nil
217 "History of regexps used for interactive fontification.")
218
219(defvar hi-lock-file-patterns-prefix "Hi-lock"
c898bef7 220 "Search target for finding hi-lock patterns at top of file.")
abb2db1c 221
71060bdd 222(defvar hi-lock-archaic-interface-message-used nil
90756087 223 "True if user alerted that `global-hi-lock-mode' is now the global switch.
8b730780 224Earlier versions of hi-lock used `hi-lock-mode' as the global switch;
90756087 225the message is issued if it appears that `hi-lock-mode' is used assuming
71060bdd
EZ
226that older functionality. This variable avoids multiple reminders.")
227
228(defvar hi-lock-archaic-interface-deduce nil
90756087
JL
229 "If non-nil, sometimes assume that `hi-lock-mode' means `global-hi-lock-mode'.
230Assumption is made if `hi-lock-mode' used in the *scratch* buffer while
71060bdd
EZ
231a library is being loaded.")
232
abb2db1c
GM
233(make-variable-buffer-local 'hi-lock-interactive-patterns)
234(put 'hi-lock-interactive-patterns 'permanent-local t)
235(make-variable-buffer-local 'hi-lock-regexp-history)
236(put 'hi-lock-regexp-history 'permanent-local t)
237(make-variable-buffer-local 'hi-lock-file-patterns)
238(put 'hi-lock-file-patterns 'permanent-local t)
239
240(defvar hi-lock-menu (make-sparse-keymap "Hi Lock")
241 "Menu for hi-lock mode.")
242
243(define-key-after hi-lock-menu [highlight-regexp]
244 '(menu-item "Highlight Regexp..." highlight-regexp
245 :help "Highlight text matching PATTERN (a regexp)."))
246
108ee42b
GM
247(define-key-after hi-lock-menu [highlight-phrase]
248 '(menu-item "Highlight Phrase..." highlight-phrase
249 :help "Highlight text matching PATTERN (a regexp processed to match phrases)."))
250
abb2db1c
GM
251(define-key-after hi-lock-menu [highlight-lines-matching-regexp]
252 '(menu-item "Highlight Lines..." highlight-lines-matching-regexp
253 :help "Highlight lines containing match of PATTERN (a regexp).."))
254
255(define-key-after hi-lock-menu [unhighlight-regexp]
256 '(menu-item "Remove Highlighting..." unhighlight-regexp
257 :help "Remove previously entered highlighting pattern."
258 :enable hi-lock-interactive-patterns))
259
260(define-key-after hi-lock-menu [hi-lock-write-interactive-patterns]
261 '(menu-item "Patterns to Buffer" hi-lock-write-interactive-patterns
262 :help "Insert interactively added REGEXPs into buffer at point."
263 :enable hi-lock-interactive-patterns))
264
265(define-key-after hi-lock-menu [hi-lock-find-patterns]
266 '(menu-item "Patterns from Buffer" hi-lock-find-patterns
267 :help "Use patterns (if any) near top of buffer."))
268
269(defvar hi-lock-map (make-sparse-keymap "Hi Lock")
270 "Key map for hi-lock.")
271
272(define-key hi-lock-map "\C-xwi" 'hi-lock-find-patterns)
273(define-key hi-lock-map "\C-xwl" 'highlight-lines-matching-regexp)
108ee42b 274(define-key hi-lock-map "\C-xwp" 'highlight-phrase)
abb2db1c
GM
275(define-key hi-lock-map "\C-xwh" 'highlight-regexp)
276(define-key hi-lock-map "\C-xwr" 'unhighlight-regexp)
277(define-key hi-lock-map "\C-xwb" 'hi-lock-write-interactive-patterns)
278
abb2db1c
GM
279;; Visible Functions
280
abb2db1c 281;;;###autoload
71060bdd 282(define-minor-mode hi-lock-mode
abb2db1c
GM
283 "Toggle minor mode for interactively adding font-lock highlighting patterns.
284
90756087
JL
285If ARG positive, turn hi-lock on. Issuing a hi-lock command will also
286turn hi-lock on. To turn hi-lock on in all buffers use
287`global-hi-lock-mode' or in your .emacs file (global-hi-lock-mode 1).
71060bdd
EZ
288When hi-lock is turned on, a \"Regexp Highlighting\" submenu is added
289to the \"Edit\" menu. The commands in the submenu, which can be
290called interactively, are:
abb2db1c
GM
291
292\\[highlight-regexp] REGEXP FACE
293 Highlight matches of pattern REGEXP in current buffer with FACE.
294
108ee42b
GM
295\\[highlight-phrase] PHRASE FACE
296 Highlight matches of phrase PHRASE in current buffer with FACE.
297 (PHRASE can be any REGEXP, but spaces will be replaced by matches
298 to whitespace and initial lower-case letters will become case insensitive.)
71296446 299
abb2db1c
GM
300\\[highlight-lines-matching-regexp] REGEXP FACE
301 Highlight lines containing matches of REGEXP in current buffer with FACE.
302
303\\[unhighlight-regexp] REGEXP
304 Remove highlighting on matches of REGEXP in current buffer.
305
306\\[hi-lock-write-interactive-patterns]
c898bef7 307 Write active REGEXPs into buffer as comments (if possible). They may
abb2db1c
GM
308 be read the next time file is loaded or when the \\[hi-lock-find-patterns] command
309 is issued. The inserted regexps are in the form of font lock keywords.
ee44464c 310 (See `font-lock-keywords'.) They may be edited and re-loaded with \\[hi-lock-find-patterns],
869d3e17
JB
311 any valid `font-lock-keywords' form is acceptable. When a file is
312 loaded the patterns are read if `hi-lock-file-patterns-policy' is
c898bef7
CY
313 'ask and the user responds y to the prompt, or if
314 `hi-lock-file-patterns-policy' is bound to a function and that
315 function returns t.
abb2db1c
GM
316
317\\[hi-lock-find-patterns]
318 Re-read patterns stored in buffer (in the format produced by \\[hi-lock-write-interactive-patterns]).
319
c898bef7
CY
320When hi-lock is started and if the mode is not excluded or patterns
321rejected, the beginning of the buffer is searched for lines of the
322form:
abb2db1c 323 Hi-lock: FOO
8b730780
JB
324where FOO is a list of patterns. These are added to the font lock
325keywords already present. The patterns must start before position
326\(number of characters into buffer) `hi-lock-file-patterns-range'.
327Patterns will be read until
abb2db1c 328 Hi-lock: end
8b730780 329is found. A mode is excluded if it's in the list `hi-lock-exclude-modes'."
963b2040 330 :group 'hi-lock
90756087
JL
331 :lighter (:eval (if (or hi-lock-interactive-patterns
332 hi-lock-file-patterns)
333 " Hi" ""))
963b2040
CY
334 :global nil
335 :keymap hi-lock-map
71060bdd
EZ
336 (when (and (equal (buffer-name) "*scratch*")
337 load-in-progress
338 (not (interactive-p))
339 (not hi-lock-archaic-interface-message-used))
340 (setq hi-lock-archaic-interface-message-used t)
341 (if hi-lock-archaic-interface-deduce
342 (global-hi-lock-mode hi-lock-mode)
343 (warn
344 "Possible archaic use of (hi-lock-mode).
345Use (global-hi-lock-mode 1) in .emacs to enable hi-lock for all buffers,
90756087 346use (hi-lock-mode 1) for individual buffers. For compatibility with Emacs
71060bdd
EZ
347versions before 22 use the following in your .emacs file:
348
349 (if (functionp 'global-hi-lock-mode)
350 (global-hi-lock-mode 1)
351 (hi-lock-mode 1))
352")))
353 (if hi-lock-mode
963b2040
CY
354 ;; Turned on.
355 (progn
e4d59066 356 (unless font-lock-mode (font-lock-mode 1))
963b2040
CY
357 (define-key-after menu-bar-edit-menu [hi-lock]
358 (cons "Regexp Highlighting" hi-lock-menu))
359 (hi-lock-find-patterns)
e4d59066 360 (add-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook nil t))
abb2db1c 361 ;; Turned off.
e4d59066
CY
362 (when (or hi-lock-interactive-patterns
363 hi-lock-file-patterns)
71060bdd 364 (when hi-lock-interactive-patterns
e4d59066
CY
365 (font-lock-remove-keywords nil hi-lock-interactive-patterns)
366 (setq hi-lock-interactive-patterns nil))
367 (when hi-lock-file-patterns
368 (font-lock-remove-keywords nil hi-lock-file-patterns)
369 (setq hi-lock-file-patterns nil))
81dc5714
JL
370 (remove-overlays nil nil 'hi-lock-overlay t)
371 (when font-lock-fontified (font-lock-fontify-buffer)))
963b2040
CY
372 (define-key-after menu-bar-edit-menu [hi-lock] nil)
373 (remove-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook t)))
abb2db1c 374
963b2040 375;;;###autoload
98007d83 376(define-globalized-minor-mode global-hi-lock-mode
71060bdd 377 hi-lock-mode turn-on-hi-lock-if-enabled
9cb4bb45 378 :group 'hi-lock)
71060bdd 379
963b2040 380(defun turn-on-hi-lock-if-enabled ()
71060bdd 381 (setq hi-lock-archaic-interface-message-used t)
963b2040 382 (unless (memq major-mode hi-lock-exclude-modes)
71060bdd 383 (hi-lock-mode 1)))
abb2db1c
GM
384
385;;;###autoload
386(defalias 'highlight-lines-matching-regexp 'hi-lock-line-face-buffer)
387;;;###autoload
388(defun hi-lock-line-face-buffer (regexp &optional face)
108ee42b 389 "Set face of all lines containing a match of REGEXP to FACE.
abb2db1c
GM
390
391Interactively, prompt for REGEXP then FACE. Buffer-local history
392list maintained for regexps, global history maintained for faces.
393\\<minibuffer-local-map>Use \\[next-history-element] and \\[previous-history-element] to retrieve next or previous history item.
8b730780 394\(See info node `Minibuffer History'.)"
abb2db1c
GM
395 (interactive
396 (list
397 (hi-lock-regexp-okay
398 (read-from-minibuffer "Regexp to highlight line: "
399 (cons (or (car hi-lock-regexp-history) "") 1 )
400 nil nil 'hi-lock-regexp-history))
401 (hi-lock-read-face-name)))
597767da 402 (or (facep face) (setq face 'hi-yellow))
71060bdd 403 (unless hi-lock-mode (hi-lock-mode 1))
abb2db1c 404 (hi-lock-set-pattern
b18f5523
SM
405 ;; The \\(?:...\\) grouping construct ensures that a leading ^, +, * or ?
406 ;; or a trailing $ in REGEXP will be interpreted correctly.
963b2040 407 (concat "^.*\\(?:" regexp "\\).*$") face))
abb2db1c 408
108ee42b 409
abb2db1c
GM
410;;;###autoload
411(defalias 'highlight-regexp 'hi-lock-face-buffer)
412;;;###autoload
413(defun hi-lock-face-buffer (regexp &optional face)
108ee42b 414 "Set face of each match of REGEXP to FACE.
abb2db1c
GM
415
416Interactively, prompt for REGEXP then FACE. Buffer-local history
417list maintained for regexps, global history maintained for faces.
418\\<minibuffer-local-map>Use \\[next-history-element] and \\[previous-history-element] to retrieve next or previous history item.
8b730780 419\(See info node `Minibuffer History'.)"
abb2db1c
GM
420 (interactive
421 (list
422 (hi-lock-regexp-okay
423 (read-from-minibuffer "Regexp to highlight: "
424 (cons (or (car hi-lock-regexp-history) "") 1 )
425 nil nil 'hi-lock-regexp-history))
426 (hi-lock-read-face-name)))
597767da 427 (or (facep face) (setq face 'hi-yellow))
71060bdd 428 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 429 (hi-lock-set-pattern regexp face))
abb2db1c 430
108ee42b
GM
431;;;###autoload
432(defalias 'highlight-phrase 'hi-lock-face-phrase-buffer)
433;;;###autoload
434(defun hi-lock-face-phrase-buffer (regexp &optional face)
435 "Set face of each match of phrase REGEXP to FACE.
436
437Whitespace in REGEXP converted to arbitrary whitespace and initial
438lower-case letters made case insensitive."
439 (interactive
440 (list
441 (hi-lock-regexp-okay
442 (hi-lock-process-phrase
443 (read-from-minibuffer "Phrase to highlight: "
444 (cons (or (car hi-lock-regexp-history) "") 1 )
445 nil nil 'hi-lock-regexp-history)))
446 (hi-lock-read-face-name)))
597767da 447 (or (facep face) (setq face 'hi-yellow))
71060bdd 448 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 449 (hi-lock-set-pattern regexp face))
108ee42b 450
abb2db1c
GM
451;;;###autoload
452(defalias 'unhighlight-regexp 'hi-lock-unface-buffer)
453;;;###autoload
454(defun hi-lock-unface-buffer (regexp)
108ee42b 455 "Remove highlighting of each match to REGEXP set by hi-lock.
abb2db1c
GM
456
457Interactively, prompt for REGEXP. Buffer-local history of inserted
458regexp's maintained. Will accept only regexps inserted by hi-lock
108ee42b 459interactive functions. \(See `hi-lock-interactive-patterns'.\)
abb2db1c 460\\<minibuffer-local-must-match-map>Use \\[minibuffer-complete] to complete a partially typed regexp.
cf667bc5 461\(See info node `Minibuffer History'.\)"
abb2db1c 462 (interactive
a2ab90da 463 (if (and (display-popup-menus-p) (not last-nonmenu-event))
cf667bc5
EZ
464 (catch 'snafu
465 (or
466 (x-popup-menu
467 t
468 (cons
469 `keymap
470 (cons "Select Pattern to Unhighlight"
471 (mapcar (lambda (pattern)
472 (list (car pattern)
473 (format
474 "%s (%s)" (car pattern)
475 (symbol-name
476 (car
477 (cdr (car (cdr (car (cdr pattern))))))))
478 (cons nil nil)
479 (car pattern)))
480 hi-lock-interactive-patterns))))
481 ;; If the user clicks outside the menu, meaning that they
482 ;; change their mind, x-popup-menu returns nil, and
483 ;; interactive signals a wrong number of arguments error.
484 ;; To prevent that, we return an empty string, which will
485 ;; effectively disable the rest of the function.
486 (throw 'snafu '(""))))
abb2db1c
GM
487 (let ((history-list (mapcar (lambda (p) (car p))
488 hi-lock-interactive-patterns)))
489 (unless hi-lock-interactive-patterns
490 (error "No highlighting to remove"))
491 (list
492 (completing-read "Regexp to unhighlight: "
932c309e 493 hi-lock-interactive-patterns nil t
abb2db1c
GM
494 (car (car hi-lock-interactive-patterns))
495 (cons 'history-list 1))))))
496 (let ((keyword (assoc regexp hi-lock-interactive-patterns)))
497 (when keyword
498 (font-lock-remove-keywords nil (list keyword))
499 (setq hi-lock-interactive-patterns
500 (delq keyword hi-lock-interactive-patterns))
81dc5714
JL
501 (remove-overlays
502 nil nil 'hi-lock-overlay-regexp (hi-lock-string-serialize regexp))
503 (when font-lock-fontified (font-lock-fontify-buffer)))))
abb2db1c
GM
504
505;;;###autoload
506(defun hi-lock-write-interactive-patterns ()
507 "Write interactively added patterns, if any, into buffer at point.
508
509Interactively added patterns are those normally specified using
510`highlight-regexp' and `highlight-lines-matching-regexp'; they can
511be found in variable `hi-lock-interactive-patterns'."
512 (interactive)
597767da
CY
513 (if (null hi-lock-interactive-patterns)
514 (error "There are no interactive patterns"))
515 (let ((beg (point)))
ee44464c 516 (mapc
abb2db1c 517 (lambda (pattern)
90756087
JL
518 (insert (format "%s: (%s)\n"
519 hi-lock-file-patterns-prefix
520 (prin1-to-string pattern))))
597767da
CY
521 hi-lock-interactive-patterns)
522 (comment-region beg (point)))
523 (when (> (point) hi-lock-file-patterns-range)
524 (warn "Inserted keywords not close enough to top of file")))
abb2db1c
GM
525
526;; Implementation Functions
527
108ee42b
GM
528(defun hi-lock-process-phrase (phrase)
529 "Convert regexp PHRASE to a regexp that matches phrases.
530
531Blanks in PHRASE replaced by regexp that matches arbitrary whitespace
532and initial lower-case letters made case insensitive."
533 (let ((mod-phrase nil))
534 (setq mod-phrase
535 (replace-regexp-in-string
536 "\\<[a-z]" (lambda (m) (format "[%s%s]" (upcase m) m)) phrase))
537 (setq mod-phrase
538 (replace-regexp-in-string
539 "\\s-+" "[ \t\n]+" mod-phrase nil t))))
540
abb2db1c
GM
541(defun hi-lock-regexp-okay (regexp)
542 "Return REGEXP if it appears suitable for a font-lock pattern.
543
544Otherwise signal an error. A pattern that matches the null string is
545not suitable."
546 (if (string-match regexp "")
547 (error "Regexp cannot match an empty string")
548 regexp))
549
550(defun hi-lock-read-face-name ()
551 "Read face name from minibuffer with completion and history."
552 (intern (completing-read
553 "Highlight using face: "
554 obarray 'facep t
555 (cons (car hi-lock-face-history)
556 (let ((prefix
557 (try-completion
558 (substring (car hi-lock-face-history) 0 1)
559 (mapcar (lambda (f) (cons f f))
560 hi-lock-face-history))))
561 (if (and (stringp prefix)
562 (not (equal prefix (car hi-lock-face-history))))
563 (length prefix) 0)))
564 '(hi-lock-face-history . 0))))
565
963b2040
CY
566(defun hi-lock-set-pattern (regexp face)
567 "Highlight REGEXP with face FACE."
568 (let ((pattern (list regexp (list 0 (list 'quote face) t))))
abb2db1c 569 (unless (member pattern hi-lock-interactive-patterns)
90756087 570 (font-lock-add-keywords nil (list pattern) t)
963b2040 571 (push pattern hi-lock-interactive-patterns)
81dc5714
JL
572 (if font-lock-fontified
573 (font-lock-fontify-buffer)
574 (let* ((serial (hi-lock-string-serialize regexp))
575 (range-min (- (point) (/ hi-lock-highlight-range 2)))
576 (range-max (+ (point) (/ hi-lock-highlight-range 2)))
577 (search-start
578 (max (point-min)
579 (- range-min (max 0 (- range-max (point-max))))))
580 (search-end
581 (min (point-max)
582 (+ range-max (max 0 (- (point-min) range-min))))))
583 (save-excursion
584 (goto-char search-start)
585 (while (re-search-forward regexp search-end t)
586 (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
587 (overlay-put overlay 'hi-lock-overlay t)
588 (overlay-put overlay 'hi-lock-overlay-regexp serial)
589 (overlay-put overlay 'face face))
590 (goto-char (match-end 0)))))))))
abb2db1c
GM
591
592(defun hi-lock-set-file-patterns (patterns)
593 "Replace file patterns list with PATTERNS and refontify."
108ee42b
GM
594 (when (or hi-lock-file-patterns patterns)
595 (font-lock-remove-keywords nil hi-lock-file-patterns)
596 (setq hi-lock-file-patterns patterns)
90756087 597 (font-lock-add-keywords nil hi-lock-file-patterns t)
e4d59066 598 (font-lock-fontify-buffer)))
abb2db1c
GM
599
600(defun hi-lock-find-patterns ()
601 "Find patterns in current buffer for hi-lock."
602 (interactive)
603 (unless (memq major-mode hi-lock-exclude-modes)
604 (let ((all-patterns nil)
605 (target-regexp (concat "\\<" hi-lock-file-patterns-prefix ":")))
606 (save-excursion
62cec9fe
SM
607 (save-restriction
608 (widen)
609 (goto-char (point-min))
610 (re-search-forward target-regexp
611 (+ (point) hi-lock-file-patterns-range) t)
612 (beginning-of-line)
613 (while (and (re-search-forward target-regexp (+ (point) 100) t)
614 (not (looking-at "\\s-*end")))
ed6773fa
JB
615 (condition-case nil
616 (setq all-patterns (append (read (current-buffer)) all-patterns))
617 (error (message "Invalid pattern list expression at %d"
963b2040 618 (line-number-at-pos)))))))
c898bef7
CY
619 (when (and all-patterns
620 hi-lock-mode
621 (cond
622 ((eq this-command 'hi-lock-find-patterns) t)
623 ((functionp hi-lock-file-patterns-policy)
624 (funcall hi-lock-file-patterns-policy all-patterns))
625 ((eq hi-lock-file-patterns-policy 'ask)
626 (y-or-n-p "Add patterns from this buffer to hi-lock? "))
627 (t nil)))
628 (hi-lock-set-file-patterns all-patterns)
629 (if (interactive-p)
630 (message "Hi-lock added %d patterns." (length all-patterns)))))))
abb2db1c
GM
631
632(defun hi-lock-font-lock-hook ()
8b730780 633 "Add hi-lock patterns to font-lock's."
e4d59066 634 (if font-lock-mode
90756087
JL
635 (progn
636 (font-lock-add-keywords nil hi-lock-file-patterns t)
637 (font-lock-add-keywords nil hi-lock-interactive-patterns t))
71060bdd 638 (hi-lock-mode -1)))
abb2db1c 639
81dc5714
JL
640(defvar hi-lock-string-serialize-hash
641 (make-hash-table :test 'equal)
642 "Hash table used to assign unique numbers to strings.")
643
644(defvar hi-lock-string-serialize-serial 1
645 "Number assigned to last new string in call to `hi-lock-string-serialize'.
646A string is considered new if it had not previously been used in a call to
647`hi-lock-string-serialize'.")
648
649(defun hi-lock-string-serialize (string)
650 "Return unique serial number for STRING."
651 (interactive)
652 (let ((val (gethash string hi-lock-string-serialize-hash)))
653 (if val val
654 (puthash string
655 (setq hi-lock-string-serialize-serial
656 (1+ hi-lock-string-serialize-serial))
657 hi-lock-string-serialize-hash)
658 hi-lock-string-serialize-serial)))
659
869d3e17
JB
660(defun hi-lock-unload-function ()
661 "Unload the Hi-Lock library."
662 (global-hi-lock-mode -1)
663 ;; continue standard unloading
664 nil)
665
abb2db1c
GM
666(provide 'hi-lock)
667
ffc30f4f 668;; arch-tag: d2e8fd07-4cc9-4c6f-a200-1e729bc54066
abb2db1c 669;;; hi-lock.el ends here