Include <config.h> uniformly in oldXMenu sources.
[bpt/emacs.git] / lisp / hi-lock.el
CommitLineData
e8af40ee 1;;; hi-lock.el --- minor mode for interactive automatic highlighting
abb2db1c 2
acaf905b 3;; Copyright (C) 2000-2012 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 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
GM
137
138(defgroup hi-lock-faces nil
139 "Faces for hi-lock."
f43eaaef
JL
140 :group 'hi-lock
141 :group 'faces)
abb2db1c
GM
142
143(defface hi-yellow
e0d815a2 144 '((((min-colors 88) (background dark))
ea81d57e
DN
145 (:background "yellow1" :foreground "black"))
146 (((background dark)) (:background "yellow" :foreground "black"))
147 (((min-colors 88)) (:background "yellow1"))
a0b8c939 148 (t (:background "yellow")))
abb2db1c
GM
149 "Default face for hi-lock mode."
150 :group 'hi-lock-faces)
151
152(defface hi-pink
16cdf141 153 '((((background dark)) (:background "pink" :foreground "black"))
a0b8c939 154 (t (:background "pink")))
abb2db1c
GM
155 "Face for hi-lock mode."
156 :group 'hi-lock-faces)
157
158(defface hi-green
e0d815a2 159 '((((min-colors 88) (background dark))
ea81d57e
DN
160 (:background "green1" :foreground "black"))
161 (((background dark)) (:background "green" :foreground "black"))
e0d815a2 162 (((min-colors 88)) (:background "green1"))
a0b8c939 163 (t (:background "green")))
abb2db1c
GM
164 "Face for hi-lock mode."
165 :group 'hi-lock-faces)
166
167(defface hi-blue
16cdf141 168 '((((background dark)) (:background "light blue" :foreground "black"))
a0b8c939 169 (t (:background "light blue")))
abb2db1c
GM
170 "Face for hi-lock mode."
171 :group 'hi-lock-faces)
172
173(defface hi-black-b
174 '((t (:weight bold)))
175 "Face for hi-lock mode."
176 :group 'hi-lock-faces)
177
178(defface hi-blue-b
ea81d57e
DN
179 '((((min-colors 88)) (:weight bold :foreground "blue1"))
180 (t (:weight bold :foreground "blue")))
abb2db1c
GM
181 "Face for hi-lock mode."
182 :group 'hi-lock-faces)
183
184(defface hi-green-b
ea81d57e
DN
185 '((((min-colors 88)) (:weight bold :foreground "green1"))
186 (t (:weight bold :foreground "green")))
abb2db1c
GM
187 "Face for hi-lock mode."
188 :group 'hi-lock-faces)
189
190(defface hi-red-b
ea81d57e
DN
191 '((((min-colors 88)) (:weight bold :foreground "red1"))
192 (t (:weight bold :foreground "red")))
abb2db1c
GM
193 "Face for hi-lock mode."
194 :group 'hi-lock-faces)
195
196(defface hi-black-hb
c3b27206 197 '((t (:weight bold :height 1.67 :inherit variable-pitch)))
abb2db1c
GM
198 "Face for hi-lock mode."
199 :group 'hi-lock-faces)
200
201(defvar hi-lock-file-patterns nil
202 "Patterns found in file for hi-lock. Should not be changed.")
203
204(defvar hi-lock-interactive-patterns nil
205 "Patterns provided to hi-lock by user. Should not be changed.")
206
a52c0aa0
SM
207(define-obsolete-variable-alias 'hi-lock-face-history
208 'hi-lock-face-defaults "23.1")
ef705f25
JL
209(defvar hi-lock-face-defaults
210 '("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 "Default faces for hi-lock interactive functions.")
abb2db1c 213
a52c0aa0
SM
214;;(dolist (f hi-lock-face-defaults)
215;; (unless (facep f) (error "%s not a face" f)))
ef705f25
JL
216
217(define-obsolete-variable-alias 'hi-lock-regexp-history
218 'regexp-history
219 "23.1")
abb2db1c
GM
220
221(defvar hi-lock-file-patterns-prefix "Hi-lock"
c898bef7 222 "Search target for finding hi-lock patterns at top of file.")
abb2db1c 223
71060bdd 224(defvar hi-lock-archaic-interface-message-used nil
90756087 225 "True if user alerted that `global-hi-lock-mode' is now the global switch.
8b730780 226Earlier versions of hi-lock used `hi-lock-mode' as the global switch;
90756087 227the message is issued if it appears that `hi-lock-mode' is used assuming
71060bdd
EZ
228that older functionality. This variable avoids multiple reminders.")
229
230(defvar hi-lock-archaic-interface-deduce nil
90756087
JL
231 "If non-nil, sometimes assume that `hi-lock-mode' means `global-hi-lock-mode'.
232Assumption is made if `hi-lock-mode' used in the *scratch* buffer while
71060bdd
EZ
233a library is being loaded.")
234
abb2db1c
GM
235(make-variable-buffer-local 'hi-lock-interactive-patterns)
236(put 'hi-lock-interactive-patterns 'permanent-local t)
abb2db1c
GM
237(make-variable-buffer-local 'hi-lock-file-patterns)
238(put 'hi-lock-file-patterns 'permanent-local t)
239
b016851c
SM
240(defvar hi-lock-menu
241 (let ((map (make-sparse-keymap "Hi Lock")))
242 (define-key-after map [highlight-regexp]
243 '(menu-item "Highlight Regexp..." highlight-regexp
244 :help "Highlight text matching PATTERN (a regexp)."))
245
246 (define-key-after map [highlight-phrase]
247 '(menu-item "Highlight Phrase..." highlight-phrase
248 :help "Highlight text matching PATTERN (a regexp processed to match phrases)."))
249
250 (define-key-after map [highlight-lines-matching-regexp]
251 '(menu-item "Highlight Lines..." highlight-lines-matching-regexp
252 :help "Highlight lines containing match of PATTERN (a regexp)."))
253
254 (define-key-after map [unhighlight-regexp]
255 '(menu-item "Remove Highlighting..." unhighlight-regexp
256 :help "Remove previously entered highlighting pattern."
257 :enable hi-lock-interactive-patterns))
258
259 (define-key-after map [hi-lock-write-interactive-patterns]
260 '(menu-item "Patterns to Buffer" hi-lock-write-interactive-patterns
261 :help "Insert interactively added REGEXPs into buffer at point."
262 :enable hi-lock-interactive-patterns))
263
264 (define-key-after map [hi-lock-find-patterns]
265 '(menu-item "Patterns from Buffer" hi-lock-find-patterns
266 :help "Use patterns (if any) near top of buffer."))
267 map)
abb2db1c
GM
268 "Menu for hi-lock mode.")
269
b016851c
SM
270(defvar hi-lock-map
271 (let ((map (make-sparse-keymap "Hi Lock")))
272 (define-key map "\C-xwi" 'hi-lock-find-patterns)
273 (define-key map "\C-xwl" 'highlight-lines-matching-regexp)
274 (define-key map "\C-xwp" 'highlight-phrase)
275 (define-key map "\C-xwh" 'highlight-regexp)
276 (define-key map "\C-xwr" 'unhighlight-regexp)
277 (define-key map "\C-xwb" 'hi-lock-write-interactive-patterns)
278 map)
abb2db1c
GM
279 "Key map for hi-lock.")
280
abb2db1c
GM
281;; Visible Functions
282
abb2db1c 283;;;###autoload
71060bdd 284(define-minor-mode hi-lock-mode
06e21633
CY
285 "Toggle selective highlighting of patterns (Hi Lock mode).
286With a prefix argument ARG, enable Hi Lock mode if ARG is
287positive, and disable it otherwise. If called from Lisp, enable
288the mode if ARG is omitted or nil.
289
a9f6f311
CY
290Hi Lock mode is automatically enabled when you invoke any of the
291highlighting commands listed below, such as \\[highlight-regexp].
292To enable Hi Lock mode in all buffers, use `global-hi-lock-mode'
293or add (global-hi-lock-mode 1) to your init file.
294
295In buffers where Font Lock mode is enabled, patterns are
296highlighted using font lock. In buffers where Font Lock mode is
297disabled, patterns are applied using overlays; in this case, the
298highlighting will not be updated as you type.
299
300When Hi Lock mode is enabled, a \"Regexp Highlighting\" submenu
301is added to the \"Edit\" menu. The commands in the submenu,
302which can be called interactively, are:
abb2db1c
GM
303
304\\[highlight-regexp] REGEXP FACE
305 Highlight matches of pattern REGEXP in current buffer with FACE.
306
108ee42b
GM
307\\[highlight-phrase] PHRASE FACE
308 Highlight matches of phrase PHRASE in current buffer with FACE.
309 (PHRASE can be any REGEXP, but spaces will be replaced by matches
310 to whitespace and initial lower-case letters will become case insensitive.)
71296446 311
abb2db1c
GM
312\\[highlight-lines-matching-regexp] REGEXP FACE
313 Highlight lines containing matches of REGEXP in current buffer with FACE.
314
315\\[unhighlight-regexp] REGEXP
316 Remove highlighting on matches of REGEXP in current buffer.
317
318\\[hi-lock-write-interactive-patterns]
c898bef7 319 Write active REGEXPs into buffer as comments (if possible). They may
abb2db1c
GM
320 be read the next time file is loaded or when the \\[hi-lock-find-patterns] command
321 is issued. The inserted regexps are in the form of font lock keywords.
ee44464c 322 (See `font-lock-keywords'.) They may be edited and re-loaded with \\[hi-lock-find-patterns],
869d3e17
JB
323 any valid `font-lock-keywords' form is acceptable. When a file is
324 loaded the patterns are read if `hi-lock-file-patterns-policy' is
c898bef7
CY
325 'ask and the user responds y to the prompt, or if
326 `hi-lock-file-patterns-policy' is bound to a function and that
327 function returns t.
abb2db1c
GM
328
329\\[hi-lock-find-patterns]
330 Re-read patterns stored in buffer (in the format produced by \\[hi-lock-write-interactive-patterns]).
331
c898bef7
CY
332When hi-lock is started and if the mode is not excluded or patterns
333rejected, the beginning of the buffer is searched for lines of the
334form:
abb2db1c 335 Hi-lock: FOO
a9f6f311
CY
336
337where FOO is a list of patterns. The patterns must start before
338position \(number of characters into buffer)
339`hi-lock-file-patterns-range'. Patterns will be read until
340Hi-lock: end is found. A mode is excluded if it's in the list
341`hi-lock-exclude-modes'."
963b2040 342 :group 'hi-lock
90756087
JL
343 :lighter (:eval (if (or hi-lock-interactive-patterns
344 hi-lock-file-patterns)
345 " Hi" ""))
963b2040
CY
346 :global nil
347 :keymap hi-lock-map
71060bdd
EZ
348 (when (and (equal (buffer-name) "*scratch*")
349 load-in-progress
32226619 350 (not (called-interactively-p 'interactive))
71060bdd
EZ
351 (not hi-lock-archaic-interface-message-used))
352 (setq hi-lock-archaic-interface-message-used t)
353 (if hi-lock-archaic-interface-deduce
354 (global-hi-lock-mode hi-lock-mode)
355 (warn
356 "Possible archaic use of (hi-lock-mode).
357Use (global-hi-lock-mode 1) in .emacs to enable hi-lock for all buffers,
90756087 358use (hi-lock-mode 1) for individual buffers. For compatibility with Emacs
865fe16f 359versions before 22 use the following in your init file:
71060bdd
EZ
360
361 (if (functionp 'global-hi-lock-mode)
362 (global-hi-lock-mode 1)
363 (hi-lock-mode 1))
364")))
365 (if hi-lock-mode
963b2040
CY
366 ;; Turned on.
367 (progn
368 (define-key-after menu-bar-edit-menu [hi-lock]
369 (cons "Regexp Highlighting" hi-lock-menu))
370 (hi-lock-find-patterns)
e4d59066 371 (add-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook nil t))
abb2db1c 372 ;; Turned off.
e4d59066
CY
373 (when (or hi-lock-interactive-patterns
374 hi-lock-file-patterns)
71060bdd 375 (when hi-lock-interactive-patterns
e4d59066
CY
376 (font-lock-remove-keywords nil hi-lock-interactive-patterns)
377 (setq hi-lock-interactive-patterns nil))
378 (when hi-lock-file-patterns
379 (font-lock-remove-keywords nil hi-lock-file-patterns)
380 (setq hi-lock-file-patterns nil))
81dc5714
JL
381 (remove-overlays nil nil 'hi-lock-overlay t)
382 (when font-lock-fontified (font-lock-fontify-buffer)))
963b2040
CY
383 (define-key-after menu-bar-edit-menu [hi-lock] nil)
384 (remove-hook 'font-lock-mode-hook 'hi-lock-font-lock-hook t)))
abb2db1c 385
963b2040 386;;;###autoload
98007d83 387(define-globalized-minor-mode global-hi-lock-mode
71060bdd 388 hi-lock-mode turn-on-hi-lock-if-enabled
9cb4bb45 389 :group 'hi-lock)
71060bdd 390
963b2040 391(defun turn-on-hi-lock-if-enabled ()
71060bdd 392 (setq hi-lock-archaic-interface-message-used t)
963b2040 393 (unless (memq major-mode hi-lock-exclude-modes)
71060bdd 394 (hi-lock-mode 1)))
abb2db1c
GM
395
396;;;###autoload
397(defalias 'highlight-lines-matching-regexp 'hi-lock-line-face-buffer)
398;;;###autoload
399(defun hi-lock-line-face-buffer (regexp &optional face)
108ee42b 400 "Set face of all lines containing a match of REGEXP to FACE.
a9f6f311
CY
401Interactively, prompt for REGEXP then FACE, using a buffer-local
402history list for REGEXP and a global history list for FACE.
abb2db1c 403
a9f6f311
CY
404If Font Lock mode is enabled in the buffer, it is used to
405highlight REGEXP. If Font Lock mode is disabled, overlays are
406used for highlighting; in this case, the highlighting will not be
407updated as you type."
abb2db1c
GM
408 (interactive
409 (list
8677dea3
JL
410 (hi-lock-regexp-okay
411 (read-regexp "Regexp to highlight line" (car regexp-history)))
abb2db1c 412 (hi-lock-read-face-name)))
597767da 413 (or (facep face) (setq face 'hi-yellow))
71060bdd 414 (unless hi-lock-mode (hi-lock-mode 1))
abb2db1c 415 (hi-lock-set-pattern
b18f5523
SM
416 ;; The \\(?:...\\) grouping construct ensures that a leading ^, +, * or ?
417 ;; or a trailing $ in REGEXP will be interpreted correctly.
963b2040 418 (concat "^.*\\(?:" regexp "\\).*$") face))
abb2db1c 419
108ee42b 420
abb2db1c
GM
421;;;###autoload
422(defalias 'highlight-regexp 'hi-lock-face-buffer)
423;;;###autoload
424(defun hi-lock-face-buffer (regexp &optional face)
108ee42b 425 "Set face of each match of REGEXP to FACE.
a9f6f311
CY
426Interactively, prompt for REGEXP then FACE, using a buffer-local
427history list for REGEXP and a global history list for FACE.
abb2db1c 428
a9f6f311
CY
429If Font Lock mode is enabled in the buffer, it is used to
430highlight REGEXP. If Font Lock mode is disabled, overlays are
431used for highlighting; in this case, the highlighting will not be
432updated as you type."
abb2db1c
GM
433 (interactive
434 (list
8677dea3
JL
435 (hi-lock-regexp-okay
436 (read-regexp "Regexp to highlight" (car regexp-history)))
abb2db1c 437 (hi-lock-read-face-name)))
597767da 438 (or (facep face) (setq face 'hi-yellow))
71060bdd 439 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 440 (hi-lock-set-pattern regexp face))
abb2db1c 441
108ee42b
GM
442;;;###autoload
443(defalias 'highlight-phrase 'hi-lock-face-phrase-buffer)
444;;;###autoload
445(defun hi-lock-face-phrase-buffer (regexp &optional face)
446 "Set face of each match of phrase REGEXP to FACE.
82ed3ab4
GM
447If called interactively, replaces whitespace in REGEXP with
448arbitrary whitespace and makes initial lower-case letters case-insensitive.
a9f6f311
CY
449
450If Font Lock mode is enabled in the buffer, it is used to
451highlight REGEXP. If Font Lock mode is disabled, overlays are
452used for highlighting; in this case, the highlighting will not be
453updated as you type."
108ee42b
GM
454 (interactive
455 (list
456 (hi-lock-regexp-okay
457 (hi-lock-process-phrase
8677dea3 458 (read-regexp "Phrase to highlight" (car regexp-history))))
108ee42b 459 (hi-lock-read-face-name)))
597767da 460 (or (facep face) (setq face 'hi-yellow))
71060bdd 461 (unless hi-lock-mode (hi-lock-mode 1))
963b2040 462 (hi-lock-set-pattern regexp face))
108ee42b 463
e8a11b22 464(declare-function x-popup-menu "menu.c" (position menu))
f2d9c15f 465
abb2db1c
GM
466;;;###autoload
467(defalias 'unhighlight-regexp 'hi-lock-unface-buffer)
468;;;###autoload
469(defun hi-lock-unface-buffer (regexp)
108ee42b 470 "Remove highlighting of each match to REGEXP set by hi-lock.
a9f6f311
CY
471Interactively, prompt for REGEXP, accepting only regexps
472previously inserted by hi-lock interactive functions."
abb2db1c 473 (interactive
8d96c9a4
CY
474 (if (and (display-popup-menus-p)
475 (listp last-nonmenu-event)
476 use-dialog-box)
cf667bc5
EZ
477 (catch 'snafu
478 (or
479 (x-popup-menu
480 t
481 (cons
482 `keymap
483 (cons "Select Pattern to Unhighlight"
484 (mapcar (lambda (pattern)
485 (list (car pattern)
486 (format
487 "%s (%s)" (car pattern)
488 (symbol-name
489 (car
490 (cdr (car (cdr (car (cdr pattern))))))))
491 (cons nil nil)
492 (car pattern)))
493 hi-lock-interactive-patterns))))
494 ;; If the user clicks outside the menu, meaning that they
495 ;; change their mind, x-popup-menu returns nil, and
496 ;; interactive signals a wrong number of arguments error.
497 ;; To prevent that, we return an empty string, which will
498 ;; effectively disable the rest of the function.
499 (throw 'snafu '(""))))
abb2db1c
GM
500 (let ((history-list (mapcar (lambda (p) (car p))
501 hi-lock-interactive-patterns)))
502 (unless hi-lock-interactive-patterns
503 (error "No highlighting to remove"))
504 (list
505 (completing-read "Regexp to unhighlight: "
932c309e 506 hi-lock-interactive-patterns nil t
abb2db1c
GM
507 (car (car hi-lock-interactive-patterns))
508 (cons 'history-list 1))))))
509 (let ((keyword (assoc regexp hi-lock-interactive-patterns)))
510 (when keyword
511 (font-lock-remove-keywords nil (list keyword))
512 (setq hi-lock-interactive-patterns
513 (delq keyword hi-lock-interactive-patterns))
81dc5714
JL
514 (remove-overlays
515 nil nil 'hi-lock-overlay-regexp (hi-lock-string-serialize regexp))
516 (when font-lock-fontified (font-lock-fontify-buffer)))))
abb2db1c
GM
517
518;;;###autoload
519(defun hi-lock-write-interactive-patterns ()
520 "Write interactively added patterns, if any, into buffer at point.
521
522Interactively added patterns are those normally specified using
523`highlight-regexp' and `highlight-lines-matching-regexp'; they can
524be found in variable `hi-lock-interactive-patterns'."
525 (interactive)
597767da
CY
526 (if (null hi-lock-interactive-patterns)
527 (error "There are no interactive patterns"))
528 (let ((beg (point)))
ee44464c 529 (mapc
abb2db1c 530 (lambda (pattern)
90756087
JL
531 (insert (format "%s: (%s)\n"
532 hi-lock-file-patterns-prefix
533 (prin1-to-string pattern))))
597767da
CY
534 hi-lock-interactive-patterns)
535 (comment-region beg (point)))
536 (when (> (point) hi-lock-file-patterns-range)
537 (warn "Inserted keywords not close enough to top of file")))
abb2db1c
GM
538
539;; Implementation Functions
540
108ee42b
GM
541(defun hi-lock-process-phrase (phrase)
542 "Convert regexp PHRASE to a regexp that matches phrases.
543
544Blanks in PHRASE replaced by regexp that matches arbitrary whitespace
545and initial lower-case letters made case insensitive."
546 (let ((mod-phrase nil))
82ed3ab4 547 ;; FIXME fragile; better to just bind case-fold-search? (Bug#7161)
108ee42b
GM
548 (setq mod-phrase
549 (replace-regexp-in-string
1595ecfa
GM
550 "\\(^\\|\\s-\\)\\([a-z]\\)"
551 (lambda (m) (format "%s[%s%s]"
552 (match-string 1 m)
553 (upcase (match-string 2 m))
554 (match-string 2 m))) phrase))
82ed3ab4 555 ;; FIXME fragile; better to use search-spaces-regexp?
108ee42b
GM
556 (setq mod-phrase
557 (replace-regexp-in-string
558 "\\s-+" "[ \t\n]+" mod-phrase nil t))))
559
abb2db1c
GM
560(defun hi-lock-regexp-okay (regexp)
561 "Return REGEXP if it appears suitable for a font-lock pattern.
562
563Otherwise signal an error. A pattern that matches the null string is
564not suitable."
565 (if (string-match regexp "")
566 (error "Regexp cannot match an empty string")
567 regexp))
568
569(defun hi-lock-read-face-name ()
570 "Read face name from minibuffer with completion and history."
571 (intern (completing-read
572 "Highlight using face: "
573 obarray 'facep t
ef705f25 574 (cons (car hi-lock-face-defaults)
abb2db1c
GM
575 (let ((prefix
576 (try-completion
ef705f25
JL
577 (substring (car hi-lock-face-defaults) 0 1)
578 hi-lock-face-defaults)))
abb2db1c 579 (if (and (stringp prefix)
ef705f25 580 (not (equal prefix (car hi-lock-face-defaults))))
abb2db1c 581 (length prefix) 0)))
ef705f25
JL
582 'face-name-history
583 (cdr hi-lock-face-defaults))))
abb2db1c 584
963b2040
CY
585(defun hi-lock-set-pattern (regexp face)
586 "Highlight REGEXP with face FACE."
c58059f2 587 (let ((pattern (list regexp (list 0 (list 'quote face) t))))
abb2db1c 588 (unless (member pattern hi-lock-interactive-patterns)
963b2040 589 (push pattern hi-lock-interactive-patterns)
a9f6f311 590 (if font-lock-mode
c58059f2
DK
591 (progn
592 (font-lock-add-keywords nil (list pattern) t)
593 (font-lock-fontify-buffer))
81dc5714
JL
594 (let* ((serial (hi-lock-string-serialize regexp))
595 (range-min (- (point) (/ hi-lock-highlight-range 2)))
596 (range-max (+ (point) (/ hi-lock-highlight-range 2)))
597 (search-start
598 (max (point-min)
599 (- range-min (max 0 (- range-max (point-max))))))
600 (search-end
601 (min (point-max)
602 (+ range-max (max 0 (- (point-min) range-min))))))
603 (save-excursion
604 (goto-char search-start)
605 (while (re-search-forward regexp search-end t)
606 (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
607 (overlay-put overlay 'hi-lock-overlay t)
608 (overlay-put overlay 'hi-lock-overlay-regexp serial)
609 (overlay-put overlay 'face face))
610 (goto-char (match-end 0)))))))))
abb2db1c
GM
611
612(defun hi-lock-set-file-patterns (patterns)
613 "Replace file patterns list with PATTERNS and refontify."
108ee42b
GM
614 (when (or hi-lock-file-patterns patterns)
615 (font-lock-remove-keywords nil hi-lock-file-patterns)
616 (setq hi-lock-file-patterns patterns)
90756087 617 (font-lock-add-keywords nil hi-lock-file-patterns t)
e4d59066 618 (font-lock-fontify-buffer)))
abb2db1c
GM
619
620(defun hi-lock-find-patterns ()
621 "Find patterns in current buffer for hi-lock."
622 (interactive)
623 (unless (memq major-mode hi-lock-exclude-modes)
624 (let ((all-patterns nil)
625 (target-regexp (concat "\\<" hi-lock-file-patterns-prefix ":")))
626 (save-excursion
62cec9fe
SM
627 (save-restriction
628 (widen)
629 (goto-char (point-min))
630 (re-search-forward target-regexp
631 (+ (point) hi-lock-file-patterns-range) t)
632 (beginning-of-line)
633 (while (and (re-search-forward target-regexp (+ (point) 100) t)
634 (not (looking-at "\\s-*end")))
ed6773fa
JB
635 (condition-case nil
636 (setq all-patterns (append (read (current-buffer)) all-patterns))
637 (error (message "Invalid pattern list expression at %d"
963b2040 638 (line-number-at-pos)))))))
c898bef7
CY
639 (when (and all-patterns
640 hi-lock-mode
641 (cond
642 ((eq this-command 'hi-lock-find-patterns) t)
643 ((functionp hi-lock-file-patterns-policy)
644 (funcall hi-lock-file-patterns-policy all-patterns))
645 ((eq hi-lock-file-patterns-policy 'ask)
646 (y-or-n-p "Add patterns from this buffer to hi-lock? "))
647 (t nil)))
648 (hi-lock-set-file-patterns all-patterns)
32226619 649 (if (called-interactively-p 'interactive)
c898bef7 650 (message "Hi-lock added %d patterns." (length all-patterns)))))))
abb2db1c
GM
651
652(defun hi-lock-font-lock-hook ()
8b730780 653 "Add hi-lock patterns to font-lock's."
c58059f2
DK
654 (when font-lock-fontified
655 (font-lock-add-keywords nil hi-lock-file-patterns t)
656 (font-lock-add-keywords nil hi-lock-interactive-patterns t)))
abb2db1c 657
81dc5714
JL
658(defvar hi-lock-string-serialize-hash
659 (make-hash-table :test 'equal)
660 "Hash table used to assign unique numbers to strings.")
661
662(defvar hi-lock-string-serialize-serial 1
663 "Number assigned to last new string in call to `hi-lock-string-serialize'.
664A string is considered new if it had not previously been used in a call to
665`hi-lock-string-serialize'.")
666
667(defun hi-lock-string-serialize (string)
668 "Return unique serial number for STRING."
669 (interactive)
670 (let ((val (gethash string hi-lock-string-serialize-hash)))
671 (if val val
672 (puthash string
673 (setq hi-lock-string-serialize-serial
674 (1+ hi-lock-string-serialize-serial))
675 hi-lock-string-serialize-hash)
676 hi-lock-string-serialize-serial)))
677
869d3e17
JB
678(defun hi-lock-unload-function ()
679 "Unload the Hi-Lock library."
680 (global-hi-lock-mode -1)
681 ;; continue standard unloading
682 nil)
683
abb2db1c
GM
684(provide 'hi-lock)
685
686;;; hi-lock.el ends here