Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / hi-lock.el
CommitLineData
e8af40ee 1;;; hi-lock.el --- minor mode for interactive automatic highlighting
abb2db1c 2
73b0cd50 3;; Copyright (C) 2000-2011 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:
40;; M-x highlight-regexp ground_contact_switches_closed RET RET
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:
44;; M-x highlight-regexp Total execution time [0-9]+ RET hi-blue-b RET
45;;
46;; In buffers displaying tables, highlight the lines you're interested in:
47;; M-x highlight-lines-matching-regexp January 2000 RET hi-black-b RET
48;;
49;; When writing text, highlight personal cliches. This can be
50;; amusing.
108ee42b 51;; M-x highlight-phrase as can be seen RET RET
abb2db1c 52;;
108ee42b 53;; Setup:
abb2db1c
GM
54;;
55;; Put the following code in your .emacs 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
CY
61;; To enable the use of patterns found in files (presumably placed
62;; there by hi-lock) include the following in your .emacs file:
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
588aca27 90(eval-and-compile
abb2db1c
GM
91 (require 'font-lock))
92
f43eaaef 93(defgroup hi-lock nil
abb2db1c 94 "Interactively add and remove font-lock patterns for highlighting text."
f43eaaef
JL
95 :link '(custom-manual "(emacs)Highlight Interactively")
96 :group 'font-lock)
abb2db1c 97
abb2db1c
GM
98(defcustom hi-lock-file-patterns-range 10000
99 "Limit of search in a buffer for hi-lock patterns.
8b730780 100When a file is visited and hi-lock mode is on, patterns starting
abb2db1c
GM
101up to this limit are added to font-lock's patterns. See documentation
102of functions `hi-lock-mode' and `hi-lock-find-patterns'."
103 :type 'integer
f43eaaef 104 :group 'hi-lock)
abb2db1c 105
81dc5714
JL
106(defcustom hi-lock-highlight-range 200000
107 "Size of area highlighted by hi-lock when font-lock not active.
108Font-lock is not active in buffers that do their own highlighting,
109such as the buffer created by `list-colors-display'. In those buffers
110hi-lock patterns will only be applied over a range of
111`hi-lock-highlight-range' characters. If font-lock is active then
112highlighting will be applied throughout the buffer."
113 :type 'integer
114 :group 'hi-lock)
115
abb2db1c
GM
116(defcustom hi-lock-exclude-modes
117 '(rmail-mode mime/viewer-mode gnus-article-mode)
118 "List of major modes in which hi-lock will not run.
119For security reasons since font lock patterns can specify function
120calls."
f5bd8092 121 :type '(repeat symbol)
f43eaaef 122 :group 'hi-lock)
abb2db1c 123
cf995676 124(defcustom hi-lock-file-patterns-policy 'ask
c898bef7 125 "Specify when hi-lock should use patterns found in file.
06b1a5ef 126If `ask', prompt when patterns found in buffer; if bound to a function,
c898bef7 127use patterns when function returns t (function is called with patterns
06b1a5ef 128as first argument); if nil or `never' or anything else, don't use file
176eb1f1
GM
129patterns."
130 :type '(choice (const :tag "Do not use file patterns" never)
131 (const :tag "Ask about file patterns" ask)
132 (function :tag "Function to check file patterns"))
133 :group 'hi-lock
134 :version "22.1")
135
136;; It can have a function value.
137(put 'hi-lock-file-patterns-policy 'risky-local-variable t)
abb2db1c
GM
138
139(defgroup hi-lock-faces nil
140 "Faces for hi-lock."
f43eaaef
JL
141 :group 'hi-lock
142 :group 'faces)
abb2db1c
GM
143
144(defface hi-yellow
e0d815a2 145 '((((min-colors 88) (background dark))
ea81d57e
DN
146 (:background "yellow1" :foreground "black"))
147 (((background dark)) (:background "yellow" :foreground "black"))
148 (((min-colors 88)) (:background "yellow1"))
a0b8c939 149 (t (:background "yellow")))
abb2db1c
GM
150 "Default face for hi-lock mode."
151 :group 'hi-lock-faces)
152
153(defface hi-pink
16cdf141 154 '((((background dark)) (:background "pink" :foreground "black"))
a0b8c939 155 (t (:background "pink")))
abb2db1c
GM
156 "Face for hi-lock mode."
157 :group 'hi-lock-faces)
158
159(defface hi-green
e0d815a2 160 '((((min-colors 88) (background dark))
ea81d57e
DN
161 (:background "green1" :foreground "black"))
162 (((background dark)) (:background "green" :foreground "black"))
e0d815a2 163 (((min-colors 88)) (:background "green1"))
a0b8c939 164 (t (:background "green")))
abb2db1c
GM
165 "Face for hi-lock mode."
166 :group 'hi-lock-faces)
167
168(defface hi-blue
16cdf141 169 '((((background dark)) (:background "light blue" :foreground "black"))
a0b8c939 170 (t (:background "light blue")))
abb2db1c
GM
171 "Face for hi-lock mode."
172 :group 'hi-lock-faces)
173
174(defface hi-black-b
175 '((t (:weight bold)))
176 "Face for hi-lock mode."
177 :group 'hi-lock-faces)
178
179(defface hi-blue-b
ea81d57e
DN
180 '((((min-colors 88)) (:weight bold :foreground "blue1"))
181 (t (:weight bold :foreground "blue")))
abb2db1c
GM
182 "Face for hi-lock mode."
183 :group 'hi-lock-faces)
184
185(defface hi-green-b
ea81d57e
DN
186 '((((min-colors 88)) (:weight bold :foreground "green1"))
187 (t (:weight bold :foreground "green")))
abb2db1c
GM
188 "Face for hi-lock mode."
189 :group 'hi-lock-faces)
190
191(defface hi-red-b
ea81d57e
DN
192 '((((min-colors 88)) (:weight bold :foreground "red1"))
193 (t (:weight bold :foreground "red")))
abb2db1c
GM
194 "Face for hi-lock mode."
195 :group 'hi-lock-faces)
196
197(defface hi-black-hb
c3b27206 198 '((t (:weight bold :height 1.67 :inherit variable-pitch)))
abb2db1c
GM
199 "Face for hi-lock mode."
200 :group 'hi-lock-faces)
201
202(defvar hi-lock-file-patterns nil
203 "Patterns found in file for hi-lock. Should not be changed.")
204
205(defvar hi-lock-interactive-patterns nil
206 "Patterns provided to hi-lock by user. Should not be changed.")
207
ef705f25
JL
208(defvar hi-lock-face-defaults
209 '("hi-yellow" "hi-pink" "hi-green" "hi-blue" "hi-black-b"
210 "hi-blue-b" "hi-red-b" "hi-green-b" "hi-black-hb")
211 "Default faces for hi-lock interactive functions.")
abb2db1c 212
ef705f25 213;(dolist (f hi-lock-face-defaults) (unless (facep f) (error "%s not a face" f)))
abb2db1c 214
ef705f25
JL
215(define-obsolete-variable-alias 'hi-lock-face-history
216 'hi-lock-face-defaults
217 "23.1")
218
219(define-obsolete-variable-alias 'hi-lock-regexp-history
220 'regexp-history
221 "23.1")
abb2db1c
GM
222
223(defvar hi-lock-file-patterns-prefix "Hi-lock"
c898bef7 224 "Search target for finding hi-lock patterns at top of file.")
abb2db1c 225
71060bdd 226(defvar hi-lock-archaic-interface-message-used nil
90756087 227 "True if user alerted that `global-hi-lock-mode' is now the global switch.
8b730780 228Earlier versions of hi-lock used `hi-lock-mode' as the global switch;
90756087 229the message is issued if it appears that `hi-lock-mode' is used assuming
71060bdd
EZ
230that older functionality. This variable avoids multiple reminders.")
231
232(defvar hi-lock-archaic-interface-deduce nil
90756087
JL
233 "If non-nil, sometimes assume that `hi-lock-mode' means `global-hi-lock-mode'.
234Assumption is made if `hi-lock-mode' used in the *scratch* buffer while
71060bdd
EZ
235a library is being loaded.")
236
abb2db1c
GM
237(make-variable-buffer-local 'hi-lock-interactive-patterns)
238(put 'hi-lock-interactive-patterns 'permanent-local t)
abb2db1c
GM
239(make-variable-buffer-local 'hi-lock-file-patterns)
240(put 'hi-lock-file-patterns 'permanent-local t)
241
242(defvar hi-lock-menu (make-sparse-keymap "Hi Lock")
243 "Menu for hi-lock mode.")
244
245(define-key-after hi-lock-menu [highlight-regexp]
246 '(menu-item "Highlight Regexp..." highlight-regexp
247 :help "Highlight text matching PATTERN (a regexp)."))
248
108ee42b
GM
249(define-key-after hi-lock-menu [highlight-phrase]
250 '(menu-item "Highlight Phrase..." highlight-phrase
251 :help "Highlight text matching PATTERN (a regexp processed to match phrases)."))
252
abb2db1c
GM
253(define-key-after hi-lock-menu [highlight-lines-matching-regexp]
254 '(menu-item "Highlight Lines..." highlight-lines-matching-regexp
d88444f2 255 :help "Highlight lines containing match of PATTERN (a regexp)."))
abb2db1c
GM
256
257(define-key-after hi-lock-menu [unhighlight-regexp]
258 '(menu-item "Remove Highlighting..." unhighlight-regexp
259 :help "Remove previously entered highlighting pattern."
260 :enable hi-lock-interactive-patterns))
261
262(define-key-after hi-lock-menu [hi-lock-write-interactive-patterns]
263 '(menu-item "Patterns to Buffer" hi-lock-write-interactive-patterns
264 :help "Insert interactively added REGEXPs into buffer at point."
265 :enable hi-lock-interactive-patterns))
266
267(define-key-after hi-lock-menu [hi-lock-find-patterns]
268 '(menu-item "Patterns from Buffer" hi-lock-find-patterns
269 :help "Use patterns (if any) near top of buffer."))
270
271(defvar hi-lock-map (make-sparse-keymap "Hi Lock")
272 "Key map for hi-lock.")
273
274(define-key hi-lock-map "\C-xwi" 'hi-lock-find-patterns)
275(define-key hi-lock-map "\C-xwl" 'highlight-lines-matching-regexp)
108ee42b 276(define-key hi-lock-map "\C-xwp" 'highlight-phrase)
abb2db1c
GM
277(define-key hi-lock-map "\C-xwh" 'highlight-regexp)
278(define-key hi-lock-map "\C-xwr" 'unhighlight-regexp)
279(define-key hi-lock-map "\C-xwb" 'hi-lock-write-interactive-patterns)
280
abb2db1c
GM
281;; Visible Functions
282
abb2db1c 283;;;###autoload
71060bdd 284(define-minor-mode hi-lock-mode
abb2db1c
GM
285 "Toggle minor mode for interactively adding font-lock highlighting patterns.
286
90756087
JL
287If ARG positive, turn hi-lock on. Issuing a hi-lock command will also
288turn hi-lock on. To turn hi-lock on in all buffers use
289`global-hi-lock-mode' or in your .emacs file (global-hi-lock-mode 1).
71060bdd
EZ
290When hi-lock is turned on, a \"Regexp Highlighting\" submenu is added
291to the \"Edit\" menu. The commands in the submenu, which can be
292called interactively, are:
abb2db1c
GM
293
294\\[highlight-regexp] REGEXP FACE
295 Highlight matches of pattern REGEXP in current buffer with FACE.
296
108ee42b
GM
297\\[highlight-phrase] PHRASE FACE
298 Highlight matches of phrase PHRASE in current buffer with FACE.
299 (PHRASE can be any REGEXP, but spaces will be replaced by matches
300 to whitespace and initial lower-case letters will become case insensitive.)
71296446 301
abb2db1c
GM
302\\[highlight-lines-matching-regexp] REGEXP FACE
303 Highlight lines containing matches of REGEXP in current buffer with FACE.
304
305\\[unhighlight-regexp] REGEXP
306 Remove highlighting on matches of REGEXP in current buffer.
307
308\\[hi-lock-write-interactive-patterns]
c898bef7 309 Write active REGEXPs into buffer as comments (if possible). They may
abb2db1c
GM
310 be read the next time file is loaded or when the \\[hi-lock-find-patterns] command
311 is issued. The inserted regexps are in the form of font lock keywords.
ee44464c 312 (See `font-lock-keywords'.) They may be edited and re-loaded with \\[hi-lock-find-patterns],
869d3e17
JB
313 any valid `font-lock-keywords' form is acceptable. When a file is
314 loaded the patterns are read if `hi-lock-file-patterns-policy' is
c898bef7
CY
315 'ask and the user responds y to the prompt, or if
316 `hi-lock-file-patterns-policy' is bound to a function and that
317 function returns t.
abb2db1c
GM
318
319\\[hi-lock-find-patterns]
320 Re-read patterns stored in buffer (in the format produced by \\[hi-lock-write-interactive-patterns]).
321
c898bef7
CY
322When hi-lock is started and if the mode is not excluded or patterns
323rejected, the beginning of the buffer is searched for lines of the
324form:
abb2db1c 325 Hi-lock: FOO
8b730780
JB
326where FOO is a list of patterns. These are added to the font lock
327keywords already present. The patterns must start before position
328\(number of characters into buffer) `hi-lock-file-patterns-range'.
329Patterns will be read until
abb2db1c 330 Hi-lock: end
8b730780 331is found. A mode is excluded if it's in the list `hi-lock-exclude-modes'."
963b2040 332 :group 'hi-lock
90756087
JL
333 :lighter (:eval (if (or hi-lock-interactive-patterns
334 hi-lock-file-patterns)
335 " Hi" ""))
963b2040
CY
336 :global nil
337 :keymap hi-lock-map
71060bdd
EZ
338 (when (and (equal (buffer-name) "*scratch*")
339 load-in-progress
32226619 340 (not (called-interactively-p 'interactive))
71060bdd
EZ
341 (not hi-lock-archaic-interface-message-used))
342 (setq hi-lock-archaic-interface-message-used t)
343 (if hi-lock-archaic-interface-deduce
344 (global-hi-lock-mode hi-lock-mode)
345 (warn
346 "Possible archaic use of (hi-lock-mode).
347Use (global-hi-lock-mode 1) in .emacs to enable hi-lock for all buffers,
90756087 348use (hi-lock-mode 1) for individual buffers. For compatibility with Emacs
71060bdd
EZ
349versions before 22 use the following in your .emacs file:
350
351 (if (functionp 'global-hi-lock-mode)
352 (global-hi-lock-mode 1)
353 (hi-lock-mode 1))
354")))
355 (if hi-lock-mode
963b2040
CY
356 ;; Turned on.
357 (progn
e4d59066 358 (unless font-lock-mode (font-lock-mode 1))
963b2040
CY
359 (define-key-after menu-bar-edit-menu [hi-lock]
360 (cons "Regexp Highlighting" hi-lock-menu))
361 (hi-lock-find-patterns)
e4d59066 362 (add-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook nil t))
abb2db1c 363 ;; Turned off.
e4d59066
CY
364 (when (or hi-lock-interactive-patterns
365 hi-lock-file-patterns)
71060bdd 366 (when hi-lock-interactive-patterns
e4d59066
CY
367 (font-lock-remove-keywords nil hi-lock-interactive-patterns)
368 (setq hi-lock-interactive-patterns nil))
369 (when hi-lock-file-patterns
370 (font-lock-remove-keywords nil hi-lock-file-patterns)
371 (setq hi-lock-file-patterns nil))
81dc5714
JL
372 (remove-overlays nil nil 'hi-lock-overlay t)
373 (when font-lock-fontified (font-lock-fontify-buffer)))
963b2040
CY
374 (define-key-after menu-bar-edit-menu [hi-lock] nil)
375 (remove-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook t)))
abb2db1c 376
963b2040 377;;;###autoload
98007d83 378(define-globalized-minor-mode global-hi-lock-mode
71060bdd 379 hi-lock-mode turn-on-hi-lock-if-enabled
9cb4bb45 380 :group 'hi-lock)
71060bdd 381
963b2040 382(defun turn-on-hi-lock-if-enabled ()
71060bdd 383 (setq hi-lock-archaic-interface-message-used t)
963b2040 384 (unless (memq major-mode hi-lock-exclude-modes)
71060bdd 385 (hi-lock-mode 1)))
abb2db1c
GM
386
387;;;###autoload
388(defalias 'highlight-lines-matching-regexp 'hi-lock-line-face-buffer)
389;;;###autoload
390(defun hi-lock-line-face-buffer (regexp &optional face)
108ee42b 391 "Set face of all lines containing a match of REGEXP to FACE.
abb2db1c
GM
392
393Interactively, prompt for REGEXP then FACE. Buffer-local history
394list maintained for regexps, global history maintained for faces.
ef705f25
JL
395\\<minibuffer-local-map>Use \\[previous-history-element] to retrieve previous history items,
396and \\[next-history-element] to retrieve default values.
8b730780 397\(See info node `Minibuffer History'.)"
abb2db1c
GM
398 (interactive
399 (list
8677dea3
JL
400 (hi-lock-regexp-okay
401 (read-regexp "Regexp to highlight line" (car regexp-history)))
abb2db1c 402 (hi-lock-read-face-name)))
597767da 403 (or (facep face) (setq face 'hi-yellow))
71060bdd 404 (unless hi-lock-mode (hi-lock-mode 1))
abb2db1c 405 (hi-lock-set-pattern
b18f5523
SM
406 ;; The \\(?:...\\) grouping construct ensures that a leading ^, +, * or ?
407 ;; or a trailing $ in REGEXP will be interpreted correctly.
963b2040 408 (concat "^.*\\(?:" regexp "\\).*$") face))
abb2db1c 409
108ee42b 410
abb2db1c
GM
411;;;###autoload
412(defalias 'highlight-regexp 'hi-lock-face-buffer)
413;;;###autoload
414(defun hi-lock-face-buffer (regexp &optional face)
108ee42b 415 "Set face of each match of REGEXP to FACE.
abb2db1c
GM
416
417Interactively, prompt for REGEXP then FACE. Buffer-local history
418list maintained for regexps, global history maintained for faces.
ef705f25
JL
419\\<minibuffer-local-map>Use \\[previous-history-element] to retrieve previous history items,
420and \\[next-history-element] to retrieve default values.
8b730780 421\(See info node `Minibuffer History'.)"
abb2db1c
GM
422 (interactive
423 (list
8677dea3
JL
424 (hi-lock-regexp-okay
425 (read-regexp "Regexp to highlight" (car regexp-history)))
abb2db1c 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
8677dea3 443 (read-regexp "Phrase to highlight" (car regexp-history))))
108ee42b 444 (hi-lock-read-face-name)))
597767da 445 (or (facep face) (setq face 'hi-yellow))
71060bdd 446 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 447 (hi-lock-set-pattern regexp face))
108ee42b 448
e8a11b22 449(declare-function x-popup-menu "menu.c" (position menu))
f2d9c15f 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
ef705f25 555 (cons (car hi-lock-face-defaults)
abb2db1c
GM
556 (let ((prefix
557 (try-completion
ef705f25
JL
558 (substring (car hi-lock-face-defaults) 0 1)
559 hi-lock-face-defaults)))
abb2db1c 560 (if (and (stringp prefix)
ef705f25 561 (not (equal prefix (car hi-lock-face-defaults))))
abb2db1c 562 (length prefix) 0)))
ef705f25
JL
563 'face-name-history
564 (cdr hi-lock-face-defaults))))
abb2db1c 565
963b2040
CY
566(defun hi-lock-set-pattern (regexp face)
567 "Highlight REGEXP with face FACE."
c58059f2 568 (let ((pattern (list regexp (list 0 (list 'quote face) t))))
abb2db1c 569 (unless (member pattern hi-lock-interactive-patterns)
963b2040 570 (push pattern hi-lock-interactive-patterns)
81dc5714 571 (if font-lock-fontified
c58059f2
DK
572 (progn
573 (font-lock-add-keywords nil (list pattern) t)
574 (font-lock-fontify-buffer))
81dc5714
JL
575 (let* ((serial (hi-lock-string-serialize regexp))
576 (range-min (- (point) (/ hi-lock-highlight-range 2)))
577 (range-max (+ (point) (/ hi-lock-highlight-range 2)))
578 (search-start
579 (max (point-min)
580 (- range-min (max 0 (- range-max (point-max))))))
581 (search-end
582 (min (point-max)
583 (+ range-max (max 0 (- (point-min) range-min))))))
584 (save-excursion
585 (goto-char search-start)
586 (while (re-search-forward regexp search-end t)
587 (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
588 (overlay-put overlay 'hi-lock-overlay t)
589 (overlay-put overlay 'hi-lock-overlay-regexp serial)
590 (overlay-put overlay 'face face))
591 (goto-char (match-end 0)))))))))
abb2db1c
GM
592
593(defun hi-lock-set-file-patterns (patterns)
594 "Replace file patterns list with PATTERNS and refontify."
108ee42b
GM
595 (when (or hi-lock-file-patterns patterns)
596 (font-lock-remove-keywords nil hi-lock-file-patterns)
597 (setq hi-lock-file-patterns patterns)
90756087 598 (font-lock-add-keywords nil hi-lock-file-patterns t)
e4d59066 599 (font-lock-fontify-buffer)))
abb2db1c
GM
600
601(defun hi-lock-find-patterns ()
602 "Find patterns in current buffer for hi-lock."
603 (interactive)
604 (unless (memq major-mode hi-lock-exclude-modes)
605 (let ((all-patterns nil)
606 (target-regexp (concat "\\<" hi-lock-file-patterns-prefix ":")))
607 (save-excursion
62cec9fe
SM
608 (save-restriction
609 (widen)
610 (goto-char (point-min))
611 (re-search-forward target-regexp
612 (+ (point) hi-lock-file-patterns-range) t)
613 (beginning-of-line)
614 (while (and (re-search-forward target-regexp (+ (point) 100) t)
615 (not (looking-at "\\s-*end")))
ed6773fa
JB
616 (condition-case nil
617 (setq all-patterns (append (read (current-buffer)) all-patterns))
618 (error (message "Invalid pattern list expression at %d"
963b2040 619 (line-number-at-pos)))))))
c898bef7
CY
620 (when (and all-patterns
621 hi-lock-mode
622 (cond
623 ((eq this-command 'hi-lock-find-patterns) t)
624 ((functionp hi-lock-file-patterns-policy)
625 (funcall hi-lock-file-patterns-policy all-patterns))
626 ((eq hi-lock-file-patterns-policy 'ask)
627 (y-or-n-p "Add patterns from this buffer to hi-lock? "))
628 (t nil)))
629 (hi-lock-set-file-patterns all-patterns)
32226619 630 (if (called-interactively-p 'interactive)
c898bef7 631 (message "Hi-lock added %d patterns." (length all-patterns)))))))
abb2db1c
GM
632
633(defun hi-lock-font-lock-hook ()
8b730780 634 "Add hi-lock patterns to font-lock's."
c58059f2
DK
635 (when font-lock-fontified
636 (font-lock-add-keywords nil hi-lock-file-patterns t)
637 (font-lock-add-keywords nil hi-lock-interactive-patterns t)))
abb2db1c 638
81dc5714
JL
639(defvar hi-lock-string-serialize-hash
640 (make-hash-table :test 'equal)
641 "Hash table used to assign unique numbers to strings.")
642
643(defvar hi-lock-string-serialize-serial 1
644 "Number assigned to last new string in call to `hi-lock-string-serialize'.
645A string is considered new if it had not previously been used in a call to
646`hi-lock-string-serialize'.")
647
648(defun hi-lock-string-serialize (string)
649 "Return unique serial number for STRING."
650 (interactive)
651 (let ((val (gethash string hi-lock-string-serialize-hash)))
652 (if val val
653 (puthash string
654 (setq hi-lock-string-serialize-serial
655 (1+ hi-lock-string-serialize-serial))
656 hi-lock-string-serialize-hash)
657 hi-lock-string-serialize-serial)))
658
869d3e17
JB
659(defun hi-lock-unload-function ()
660 "Unload the Hi-Lock library."
661 (global-hi-lock-mode -1)
662 ;; continue standard unloading
663 nil)
664
abb2db1c
GM
665(provide 'hi-lock)
666
667;;; hi-lock.el ends here