]> code.delx.au - gnu-emacs-elpa/blob - packages/auto-overlays/auto-overlays.el
Merge commit '59de0b7591713d38c6d5c99cb49c4a4cc434a272' from context-coloring
[gnu-emacs-elpa] / packages / auto-overlays / auto-overlays.el
1 ;;; auto-overlays.el --- Automatic regexp-delimited overlays
2
3
4 ;; Copyright (C) 2005-2015 Free Software Foundation, Inc
5
6 ;; Version: 0.10.8
7 ;; Author: Toby Cubitt <toby-predictive@dr-qubit.org>
8 ;; Maintainer: Toby Cubitt <toby-predictive@dr-qubit.org>
9 ;; Keywords: extensions
10 ;; URL: http://www.dr-qubit.org/emacs.php
11 ;; Repository: http://www.dr-qubit.org/git/predictive.git
12
13 ;; This file is part of the Emacs.
14 ;;
15 ;; This file is free software: you can redistribute it and/or modify it under
16 ;; the terms of the GNU General Public License as published by the Free
17 ;; Software Foundation, either version 3 of the License, or (at your option)
18 ;; any later version.
19 ;;
20 ;; This program is distributed in the hope that it will be useful, but WITHOUT
21 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
23 ;; more details.
24 ;;
25 ;; You should have received a copy of the GNU General Public License along
26 ;; with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29 ;;; Code:
30
31 (defvar auto-overlay-regexps nil)
32 (make-variable-buffer-local 'auto-overlay-regexps)
33 (defvar auto-overlay-load-hook nil)
34 (defvar auto-overlay-unload-hook nil)
35
36
37 (eval-when-compile (require 'cl))
38 (require 'auto-overlay-common)
39 (provide 'auto-overlays)
40
41
42 ;; (defvar auto-overlay-list nil)
43 ;; (make-variable-buffer-local 'auto-overlay-list)
44 (defvar auto-o-pending-updates nil)
45 (make-variable-buffer-local 'auto-o-pending-updates)
46 (defvar auto-o-pending-suicides nil)
47 (make-variable-buffer-local 'auto-o-pending-suicides)
48 (defvar auto-o-pending-pre-suicide nil)
49 (make-variable-buffer-local 'auto-o-pending-pre-suicide)
50 (defvar auto-o-pending-post-suicide nil)
51 (make-variable-buffer-local 'auto-o-pending-post-suicide)
52 (defvar auto-o-pending-post-update nil)
53 (make-variable-buffer-local 'auto-o-pending-post-update)
54
55
56
57
58 ;;;========================================================
59 ;;; Code-tidying macros
60
61 (defmacro auto-o-create-set (set-id)
62 ;; Add blank entry for a new regexp set SET-ID to `auto-overlay-regexps'.
63 `(push (list ,set-id nil) auto-overlay-regexps))
64
65
66 (defmacro auto-o-delete-set (set-id)
67 ;; Delete SET-ID entry from `auto-overlay-regexps'.
68 `(setq auto-overlay-regexps
69 (assq-delete-all ,set-id auto-overlay-regexps)))
70
71
72 (defmacro auto-o-get-full-buffer-list (set-id)
73 ;; Return the list of buffers and associated properties for regexp set
74 ;; SET-ID.
75 `(nth 1 (assq ,set-id auto-overlay-regexps)))
76
77
78 (defmacro auto-o-get-buffer-list (set-id)
79 ;; Return list of buffers using regexp set SET-ID.
80 `(mapcar 'car (auto-o-get-full-buffer-list ,set-id)))
81
82
83 (defmacro auto-o-get-regexps (set-id)
84 ;; Return the list of regexp definitions for regexp set SET-ID.
85 `(cddr (assq ,set-id auto-overlay-regexps)))
86
87
88 ;; (defmacro auto-o-set-regexps (set-id regexps)
89 ;; ;; Set the list of regexp definitions for regexp set SET-ID.
90 ;; `(setcdr (cdr (assq ,set-id auto-overlay-regexps)) ,regexps))
91
92
93
94
95 ;; (defmacro auto-o-set-buffer-list (set-id list)
96 ;; ;; Set the list of buffers that use the regexp set SET-ID to LIST.
97 ;; `(let ((set (assq ,set-id auto-overlay-regexps)))
98 ;; (and set (setcar (cddr set) ,list))))
99
100
101 (defmacro auto-o-add-to-buffer-list (set-id buffer)
102 ;; Add BUFFER to the list of buffers using regexp set SET-ID.
103 `(let ((set (assq ,set-id auto-overlay-regexps)))
104 (and set
105 (null (assq ,buffer (cadr set)))
106 (setcar (cdr set) (cons (cons ,buffer nil) (cadr set))))))
107
108
109 (defmacro auto-o-delete-from-buffer-list (set-id buffer)
110 ;; Remove BUFFER from the list of buffers using regexp set SET-ID.
111 `(let ((set (assq ,set-id auto-overlay-regexps)))
112 (and set
113 (setcar (cdr set) (assq-delete-all ,buffer (cadr set))))))
114
115
116
117
118 (defmacro auto-o-enabled-p (set-id &optional buffer)
119 ;; Return non-nil if regexp set identified by SET-ID is enabled in BUFFER.
120 `(let ((buff (or ,buffer (current-buffer))))
121 (cdr (assq buff (auto-o-get-full-buffer-list ,set-id)))))
122
123
124 (defmacro auto-o-enable-set (set-id buffer)
125 ;; Set enabled flag for BUFFER in regexp set SET-ID.
126 `(setcdr (assq ,buffer (auto-o-get-full-buffer-list ,set-id)) t))
127
128
129 (defmacro auto-o-disable-set (set-id buffer)
130 ;; Unset enabled flag for BUFFER in regexp set SET-ID.
131 `(setcdr (assq ,buffer (auto-o-get-full-buffer-list ,set-id)) nil))
132
133
134
135
136 (defmacro auto-o-append-regexp (set-id entry)
137 ;; Append regexp ENTRY to SET-ID's regexps.
138 `(nconc (auto-o-get-regexps ,set-id) (list ,entry)))
139
140
141 (defmacro auto-o-prepend-regexp (set-id entry)
142 ;; Prepend regexp ENTRY to SET-ID's regexps.
143 `(setcdr (cdr (assq ,set-id auto-overlay-regexps))
144 (nconc (list ,entry) (auto-o-get-regexps ,set-id))))
145
146
147 (defmacro auto-o-insert-regexp (set-id pos entry)
148 ;; Insert regexp ENTRY in SET-ID's regexps at POS.
149 `(setcdr (nthcdr (1- pos) (auto-o-get-regexps ,set-id))
150 (nconc (list ,entry) (nthcdr pos (auto-o-get-regexps ,set-id)))))
151
152
153
154 (defmacro auto-o-entry (set-id definition-id &optional regexp-id)
155 ;; Return regexp entry identified by SET-ID, DEFINITION-ID and REGEXP-ID.
156 `(if ,regexp-id
157 (cdr (assq ,regexp-id
158 (cdr (assq ,definition-id
159 (auto-o-get-regexps ,set-id)))))
160 (cdr (assq ,definition-id (cddr (assq ,set-id auto-overlay-regexps))))))
161
162
163 (defmacro auto-o-entry-class (set-id definition-id)
164 ;; Return class corresponding to SET-ID and DEFINITION-ID.
165 `(cadr (assq ,definition-id (auto-o-get-regexps ,set-id))))
166
167
168 (defmacro auto-o-class (o-match)
169 ;; Return class of match overlay O-MATCH.
170 `(auto-o-entry-class (overlay-get ,o-match 'set-id)
171 (overlay-get ,o-match 'definition-id)))
172
173
174 (defmacro auto-o-entry-regexp (set-id definition-id &optional regexp-id)
175 ;; Return regexp corresponsing to SET-ID, DEFINITION-ID and REGEXP-ID.
176 `(let ((regexp (nth 1 (auto-o-entry ,set-id ,definition-id ,regexp-id))))
177 (if (atom regexp) regexp (car regexp))))
178
179
180 (defmacro auto-o-regexp (o-match)
181 ;; Return match overlay O-MATCH's regexp.
182 `(auto-o-entry-regexp (overlay-get ,o-match 'set-id)
183 (overlay-get ,o-match 'definition-id)
184 (overlay-get ,o-match 'regexp-id)))
185
186
187 (defmacro auto-o-entry-regexp-group (set-id definition-id &optional regexp-id)
188 ;; Return regexp group corresponsing to SET-ID, DEFINITION-ID and REGEXP-ID,
189 ;; or 0 if none is specified.
190 `(let ((regexp (nth 1 (auto-o-entry ,set-id ,definition-id ,regexp-id))))
191 (cond
192 ((atom regexp) 0)
193 ((atom (cdr regexp)) (cdr regexp))
194 (t (cadr regexp)))))
195
196
197 (defmacro auto-o-regexp-group (o-match)
198 ;; Return match overlay O-MATCH's regexp group.
199 `(auto-o-entry-regexp-group (overlay-get ,o-match 'set-id)
200 (overlay-get ,o-match 'definition-id)
201 (overlay-get ,o-match 'regexp-id)))
202
203
204 (defmacro auto-o-entry-regexp-group-nth (n set-id definition-id
205 &optional regexp-id)
206 ;; Return Nth regexp group entry corresponsing to SET-ID, DEFINITION-ID and
207 ;; REGEXP-ID, or 0 if there is no Nth entry.
208 `(let ((regexp (nth 1 (auto-o-entry ,set-id ,definition-id ,regexp-id))))
209 (cond
210 ((atom regexp) 0)
211 ((> (1+ ,n) (length (cdr regexp))) 0)
212 (t (nth ,n (cdr regexp))))))
213
214
215 (defmacro auto-o-regexp-group-nth (n o-match)
216 ;; Return match overlay O-MATCH's Nth regexp group entry, or 0 if there is
217 ;; no Nth entry.
218 `(auto-o-entry-regexp-group-nth ,n
219 (overlay-get ,o-match 'set-id)
220 (overlay-get ,o-match 'definition-id)
221 (overlay-get ,o-match 'regexp-id)))
222
223
224 (defmacro auto-o-entry-props (set-id definition-id &optional regexp-id)
225 ;; Return properties of regexp corresponding to SET-ID, DEFINITION-ID and
226 ;; REGEXP-ID.
227 `(nthcdr 2 (auto-o-entry ,set-id ,definition-id ,regexp-id)))
228
229
230 (defmacro auto-o-props (o-match)
231 ;; Return properties associated with match overlay O-MATCH.
232 `(auto-o-entry-props (overlay-get ,o-match 'set-id)
233 (overlay-get ,o-match 'definition-id)
234 (overlay-get ,o-match 'regexp-id)))
235
236
237 (defmacro auto-o-entry-edge (set-id definition-id regexp-id)
238 ;; Return edge ('start or 'end) of regexp with SET-ID, DEFINITION-ID and
239 ;; REGEXP-ID
240 `(car (auto-o-entry ,set-id ,definition-id ,regexp-id)))
241
242
243 (defmacro auto-o-edge (o-match)
244 ;; Return edge ('start or 'end) of match overlay O-MATCH
245 `(auto-o-entry-edge (overlay-get ,o-match 'set-id)
246 (overlay-get ,o-match 'definition-id)
247 (overlay-get ,o-match 'regexp-id)))
248
249
250 (defmacro auto-o-parse-function (o-match)
251 ;; Return appropriate parse function for match overlay O-MATCH.
252 `(get (auto-o-class ,o-match) 'auto-overlay-parse-function))
253
254
255 (defmacro auto-o-suicide-function (o-match)
256 ;; Return appropriate suicide function for match overlay O-MATCH.
257 `(get (auto-o-class ,o-match) 'auto-overlay-suicide-function))
258
259
260 (defmacro auto-o-match-function (o-match)
261 ;; Return match function for match overlay O-MATCH, if any.
262 `(get (auto-o-class ,o-match) 'auto-overlay-match-function))
263
264
265 (defmacro auto-o-edge-matched-p (overlay edge)
266 ;; test if EDGE of OVERLAY is matched
267 `(overlay-get ,overlay ,edge))
268
269
270 (defmacro auto-o-start-matched-p (overlay)
271 ;; test if OVERLAY is start-matched
272 `(overlay-get ,overlay 'start))
273
274
275 (defmacro auto-o-end-matched-p (overlay)
276 ;; test if OVERLAY is end-matched
277 `(overlay-get ,overlay 'end))
278
279
280 ;; (defmacro auto-o-entry-compound-class-p (set-id definition-id)
281 ;; ;; Return non-nil if regexp corresponding to SET-ID and DEFINITION-ID
282 ;; ;; contains a list of regexp entries rather than a single entry.
283 ;; `(let ((entry (cadr (auto-o-entry ,set-id ,definition-id))))
284 ;; (and (listp entry)
285 ;; (or (symbolp (cdr entry))
286 ;; (and (listp (cdr entry)) (symbolp (cadr entry)))))))
287
288 ;; (defmacro auto-o-compound-class-p (o-match)
289 ;; ;; Return non-nil if O-MATCH's regexp class is a compound class
290 ;; ;; (can just check for 'regexp-id property instead of checking regexp
291 ;; ;; definitions, since this is always set for such match overlays)
292 ;; `(overlay-get ,o-match 'regexp-id))
293
294
295 (defmacro auto-o-entry-complex-class-p (set-id definition-id)
296 ;; Return non-nil if regexp corresponding to SET-ID and DEFINITION-ID
297 ;; requires separate start and end regexps
298 `(get (auto-o-entry-class ,set-id ,definition-id)
299 'auto-overlay-complex-class))
300
301
302 (defmacro auto-o-complex-class-p (o-match)
303 ;; Return non-nil if O-MATCH's regexp class is a compound class
304 `(get (auto-o-class ,o-match) 'auto-overlay-complex-class))
305
306
307
308 (defmacro auto-o-rank (o-match)
309 ;; Return the rank of match overlay O-MATCH
310 `(auto-o-assq-position
311 (overlay-get ,o-match 'regexp-id)
312 (cddr (assq (overlay-get ,o-match 'definition-id)
313 (auto-o-get-regexps (overlay-get ,o-match 'set-id))))))
314
315
316 (defmacro auto-o-overlay-filename (set-id)
317 ;; Return the default filename to save overlays in
318 `(concat "auto-overlays-"
319 (replace-regexp-in-string
320 "\\." "-" (file-name-nondirectory (or (buffer-file-name)
321 (buffer-name))))
322 "-" (symbol-name ,set-id)))
323
324
325
326
327 ;;;============================================================
328 ;;; Replacements for CL functions
329
330 (defun auto-o-assq-position (key alist)
331 "Find the first association of KEY in ALIST.
332 Return the index of the matching item, or nil of not found.
333 Comparison is done with 'eq."
334 (let (el (i 0))
335 (catch 'found
336 (while (setq el (nth i alist))
337 (when (eq key (car el)) (throw 'found i))
338 (setq i (1+ i))
339 nil))))
340
341
342
343 (defun auto-o-position (item list)
344 "Find the first occurrence of ITEM in LIST.
345 Return the index of the matching item, or nil of not found.
346 Comparison is done with 'equal."
347 (let (el (i 0))
348 (catch 'found
349 (while (setq el (nth i list))
350 (when (equal item el) (throw 'found i))
351 (setq i (1+ i))
352 nil))))
353
354
355
356 (defun auto-o-sublist (list start &optional end)
357 "Return the sub-list of LIST from START to END.
358 If END is omitted, it defaults to the length of the list
359 If START or END is negative, it counts from the end."
360 (let (len)
361 ;; sort out arguments
362 (if end
363 (when (< end 0) (setq end (+ end (setq len (length list)))))
364 (setq end (or len (setq len (length list)))))
365 (when (< start 0)
366 (setq start (+ start (or len (length list)))))
367
368 ;; construct sub-list
369 (let (res)
370 (while (< start end)
371 (push (nth start list) res)
372 (setq start (1+ start)))
373 (nreverse res))))
374
375
376 (defmacro auto-o-adjoin (item list)
377 "Cons ITEM onto front of LIST if it's not already there.
378 Comparison is done with `eq'."
379 `(if (memq ,item ,list) ,list (setf ,list (cons ,item ,list))))
380
381
382
383 ;;;=========================================================
384 ;;; auto-overlay definition functions
385
386 (defun auto-overlay-load-definition (set-id definition &optional pos)
387 "Load DEFINITION into the set of auto-overlay definitions SET-ID
388 in the current buffer. If SET-ID does not exist, it is created.
389
390 If POS is nil, DEFINITION is added at the end of the list of
391 auto-overlay definitions. If it is t, it is added at the
392 beginning. If it is an integer, it is added at that position in
393 the list. The position in the list makes no difference to the
394 behaviour of the auto-overlays. But it can make a difference to
395 the speed and efficiency. In general, higher-priority and
396 exclusive DEFINITIONS should appear earlier in the list.
397
398 If DEFINITION-ID is supplied, it should be a symbol that can be
399 used to uniquely identify DEFINITION (see
400 `auto-overlay-unload-definition').
401
402
403 DEFINITION should be a list of the form:
404
405 (CLASS @optional :id DEFINITION-ID @rest REGEXP1 REGEXP2 ... )
406
407 CLASS is a symbol specifying the auto-overlay class. The standard
408 classes are 'word, 'line, 'self, 'flat and 'nested. The :id
409 property is optional. It should be a symbol that can be used to
410 uniquely identify DEFINITION (see
411 `auto-overlay-unload-definition').
412
413 The REGEXP's should be lists of the form:
414
415 (RGXP &optional :edge EDGE :id REGEXP-ID
416 &rest PROPERTY1 PROPERTY2 ... )
417
418 RGXP is either a single regular expression (a string), or a cons
419 cell of the form (RGXP . GROUP) where RGXP is a regular
420 expression and GROUP is an integer specifying which group in the
421 regular expression forms the delimiter for the auto-overlay. The
422 rest of the PROPERTY entries should be cons cells of the
423 form (NAME . VALUE) where NAME is an overlay property name (a
424 symbol) and VALUE is its value.
425
426 The properties :edge and :id are optional. The :edge property
427 EDGE should be one of the symbols 'start or 'end. If it is not
428 specified, :edge is assumed to be 'start. The :id property is a
429 symbol that can be used to uniquely identify REGEXP (see
430 `auto-overlay-unload-regexp')."
431
432 (let ((regexps (auto-o-get-regexps set-id))
433 (class (car definition))
434 definition-id)
435 ;; if SET-ID doesn't exist in regexp list, create empty set
436 (when (null regexps)
437 (auto-o-create-set set-id)
438 (auto-o-add-to-buffer-list set-id (current-buffer))
439 (setq regexps (auto-o-get-regexps set-id)))
440
441 (let (n)
442 (if (null (setq n (auto-o-position :id definition)))
443 ;; if DEFINITION-ID is not specified, create a unique numeric
444 ;; DEFINITION-ID
445 (setq definition-id
446 (1+ (apply 'max -1
447 (mapcar (lambda (elt)
448 (if (integerp (car elt))
449 (car elt) -1))
450 regexps))))
451 ;; if DEFINITION-ID is specified, check it's unique
452 (setq definition-id (nth (1+ n) definition))
453 (setq definition (append (auto-o-sublist definition 0 n)
454 (auto-o-sublist definition (+ n 2))))
455 (when (assq definition-id regexps)
456 (error "Definition ID \"%s\" is not unique"
457 (symbol-name definition-id)))
458 ))
459
460 (cond
461 ;; adding first entry or at start
462 ((or (eq pos t) (= (length regexps) 0)
463 (and (integerp pos) (<= pos 0)))
464 (auto-o-prepend-regexp set-id (list definition-id class)))
465 ;; adding at end
466 ((or (null pos) (and (integerp pos) (>= pos (length regexps))))
467 (auto-o-append-regexp set-id (list definition-id class)))
468 ;; adding at POS
469 ((integerp pos)
470 (auto-o-insert-regexp set-id pos (list definition-id class))))
471
472 ;; load regexp definitions
473 (dolist (regexp (cdr definition))
474 (auto-overlay-load-regexp set-id definition-id regexp))
475
476 definition-id)) ; return new entry ID
477
478
479
480 (defun auto-overlay-load-regexp (set-id definition-id regexp &optional pos)
481 "Load REGEXP into the auto-overlay definition identified by
482 DEFINITION-ID in the regexp list named SET-ID in the current
483 buffer.
484
485 If POS is nil, REGEXP is added at the end of the definition. If
486 it is t, it is added at the beginning. If it is an integer, it is
487 added at that position.
488
489
490 REGEXP should be a list of the form:
491
492 (RGXP &optional :edge EDGE :id REGEXP-ID
493 &rest PROPERTY1 PROPERTY2 ... )
494
495 RGXP is either a single regular expression (a string), or a cons
496 cell of the form (RGXP . GROUP) where RGXP is a regular
497 expression and GROUP is an integer specifying which group in the
498 regular expression forms the delimiter for the auto-overlay. The
499 rest of the PROPERTY entries should be cons cells of the
500 form (NAME . VALUE) where NAME is an overlay property name (a
501 symbol) and VALUE is its value.
502
503 The properties :edge and :id are optional. The :edge property
504 EDGE should be one of the symbols 'start or 'end. If it is not
505 specified, :edge is assumed to be 'start. The :id property is a
506 symbol that can be used to uniquely identify REGEXP (see
507 `auto-overlay-unload-regexp')."
508
509 (let ((defs (assq definition-id (auto-o-get-regexps set-id)))
510 regexp-id rgxp edge props)
511 (when (null defs)
512 (error "Definition \"%s\" not found in auto-overlay regexp set %s"
513 (symbol-name definition-id) (symbol-name set-id)))
514
515 ;; extract regexp
516 (setq rgxp (car regexp))
517 (setq regexp (cdr regexp))
518 (let (n)
519 ;; extract edge
520 (if (null (setq n (auto-o-position :edge regexp)))
521 (setq edge 'start) ; assume 'start if unspecified
522 (setq edge (nth (1+ n) regexp))
523 (setq regexp (append (auto-o-sublist regexp 0 n)
524 (auto-o-sublist regexp (+ n 2)))))
525 ;; extract regexp-id
526 (if (setq n (auto-o-position :id regexp))
527 (progn
528 (setq regexp-id (nth (1+ n) regexp))
529 (when (assq regexp-id defs)
530 (error "Regexp ID \"%s\" is not unique"
531 (symbol-name regexp-id)))
532 (setq regexp (append (auto-o-sublist regexp 0 n)
533 (auto-o-sublist regexp (+ n 2)))))
534 ;; if no id is specified, create a unique numeric ID
535 (setq regexp-id
536 (1+ (apply 'max -1
537 (mapcar (lambda (elt)
538 (if (integerp (car elt)) (car elt) -1))
539 (cddr defs))))))
540 ;; extract properties
541 (setq props regexp))
542
543 ;; create regexp definition
544 (setq regexp (append (list regexp-id edge rgxp) props))
545
546 (cond
547 ;; adding at end
548 ((or (null pos) (and (integerp pos) (>= pos (length (cddr defs)))))
549 (if (= (length (cddr defs)) 0)
550 (setcdr (cdr defs) (list regexp))
551 (nconc (cddr defs) (list regexp))))
552 ;; adding at start
553 ((or (eq pos t) (and (integerp pos) (<= pos 0)))
554 (setcdr (cdr defs) (nconc (list regexp) (cddr defs))))
555 ;; adding at POS
556 ((integerp pos)
557 (setcdr (nthcdr (1- pos) (cddr defs))
558 (nconc (list regexp) (nthcdr pos (cddr defs))))))
559
560 regexp-id)) ; return new subentry ID
561
562
563
564 (defun auto-overlay-unload-set (set-id)
565 "Unload the entire regexp set SET-ID from the current buffer."
566
567 ;; disable regexp set to delete overlays, then delete regexp set from
568 ;; current buffer
569 (when (auto-o-enabled-p set-id)
570 (auto-overlay-stop set-id))
571 (auto-o-delete-from-buffer-list set-id (current-buffer))
572 (auto-o-delete-set set-id))
573
574
575
576 (defun auto-overlay-unload-definition (set-id definition-id)
577 "Unload auto-overlay definition DEFINITION-ID in set SET-ID
578 from the current buffer. Returns the deleted definition."
579
580 (save-excursion
581 ;; call suicide function for corresponding overlays in all buffers in
582 ;; which the set is enabled
583 (dolist (buff (auto-o-get-buffer-list set-id))
584 (set-buffer buff)
585 (when (auto-o-enabled-p set-id)
586 (mapc (lambda (o) (auto-o-suicide o 'force))
587 (auto-overlays-in (point-min) (point-max)
588 `((eq set-id ,set-id)
589 (eq definition-id ,definition-id))))))
590 ;; delete definition
591 (let ((olddef (assq definition-id (auto-o-get-regexps set-id)))
592 def-id class regexps regexp edge regexp-id props)
593 ;; safe to delete by side effect here because definition is guaranteed
594 ;; not to be the first element of the list (the first two elements of a
595 ;; regexp set are always the set-id and the buffer list)
596 (assq-delete-all definition-id (assq set-id auto-overlay-regexps))
597
598
599 ;; massage deleted definition into form suitable for
600 ;; `auto-overlay-load-definition'
601 (setq def-id (nth 0 olddef)
602 class (nth 1 olddef)
603 regexps (nthcdr 2 olddef))
604 (setq olddef (list class :id def-id))
605 (dolist (rgxp regexps)
606 (setq regexp-id (nth 0 rgxp)
607 edge (nth 1 rgxp)
608 regexp (nth 2 rgxp)
609 props (nthcdr 3 rgxp))
610 (setq olddef
611 (append olddef
612 (list (append (list regexp :edge edge :id regexp-id)
613 props)))))
614 olddef))) ; return deleted definition
615
616
617
618 (defun auto-overlay-unload-regexp (set-id definition-id regexp-id)
619 "Unload the regexp identified by REGEXP-ID from auto-overlay
620 definition DEFINITION-ID in set SET-ID of the current buffer.
621 Returns the deleted regexp."
622
623 (save-excursion
624 ;; call suicide function for corresponding overlays in all buffers in
625 ;; which the set is enabled
626 (dolist (buff (auto-o-get-buffer-list set-id))
627 (set-buffer buff)
628 (when (auto-o-enabled-p set-id)
629 (mapc (lambda (o) (auto-o-suicide o 'force))
630 (auto-overlays-in (point-min) (point-max)
631 `((identity auto-overlay-match)
632 (eq set-id ,set-id)
633 (eq definition-id ,definition-id)
634 (eq regexp-id ,regexp-id))))))
635 ;; delete regexp entry
636 (let* ((def (cdr (assq definition-id (auto-o-get-regexps set-id))))
637 (oldregexp (assq regexp-id def))
638 id edge regexp props)
639 ;; can safely delete by side effect here because the regexp definition
640 ;; is guaranteed not to be the first element of the list (the first two
641 ;; elements of a definition are always the :id and class)
642 (assq-delete-all regexp-id def)
643
644 ;; massage deleted definition into form suitable for
645 ;; `auto-overlay-load-definition'
646 (setq id (nth 0 oldregexp)
647 edge (nth 1 oldregexp)
648 regexp (nth 2 oldregexp)
649 props (nthcdr 3 oldregexp))
650 (setq oldregexp (append (list regexp :edge edge :id id) props))
651 oldregexp)) ; return deleted regexp
652 )
653
654
655
656 (defun auto-overlay-share-regexp-set (set-id from-buffer &optional to-buffer)
657 "Make TO-BUFFER share the regexp set identified by SET-ID with FROM-BUFFER.
658 Any changes to that regexp set in either buffer will be reflected in the
659 other. TO-BUFFER defaults to the current buffer."
660
661 (unless to-buffer (setq to-buffer (current-buffer)))
662 (let (regexps)
663 ;; get regexp set from FROM-BUFFER
664 (with-current-buffer from-buffer
665 (setq regexps (assq set-id auto-overlay-regexps))
666 ;; delete any existing set with same ID, and add regexp set to TO-BUFFER
667 (set-buffer to-buffer)
668 (setq auto-overlay-regexps
669 (assq-delete-all set-id auto-overlay-regexps))
670 (push regexps auto-overlay-regexps)
671 ;; add TO-BUFFER to list of buffers using regexp set SET-ID
672 (auto-o-add-to-buffer-list set-id to-buffer)
673 )))
674
675
676
677 (defun auto-overlay-start (set-id &optional buffer save-file no-regexp-check)
678 "Activate the set of auto-overlay regexps identified by SET-ID
679 in BUFFER, or the current buffer if none is specified.
680
681 If optional argument SAVE-FILE is nil, it will try to load the
682 overlays from the default save file if it exists. If SAVE-FILE is
683 a string, it specifies the location of the file (if only a
684 directory is given, it will look for the default filename in that
685 directory). Anything else will cause the save file to be ignored,
686 and the buffer will be reparsed from scratch, as it will be if
687 the save file does not exist.
688
689 If the overlays are being loaded from a save file, but optional
690 argument NO-REGEXP-CHECK is non-nil, the file of saved overlays
691 will be used, but no check will be made to ensure regexp
692 refinitions are the same as when the overlays were saved."
693
694 (save-excursion
695 (when buffer (set-buffer buffer))
696 ;; run initialisation hooks
697 (run-hooks 'auto-overlay-load-hook)
698 ;; add hook to run all the various functions scheduled be run after a
699 ;; buffer modification
700 (add-hook 'after-change-functions 'auto-o-run-after-change-functions
701 nil t)
702 ;; add hook to schedule an update after a buffer modification
703 (add-hook 'after-change-functions 'auto-o-schedule-update nil t)
704 ;; add hook to simulate missing `delete-in-front-hooks' and
705 ;; `delete-behind-hooks' overlay properties
706 (add-hook 'after-change-functions
707 'auto-o-schedule-delete-in-front-or-behind-suicide nil t)
708
709 ;; set enabled flag for regexp set, and make sure buffer is in buffer list
710 ;; for the regexp set
711 (auto-o-enable-set set-id (current-buffer))
712
713 ;; try to load overlays from file
714 (unless (and (or (null save-file) (stringp save-file))
715 (auto-overlay-load-overlays set-id nil save-file
716 no-regexp-check))
717 ;; if loading was unsuccessful, search for new auto overlays
718 (let ((lines (count-lines (point-min) (point-max))))
719 (goto-char (point-min))
720 (message "Scanning for auto-overlays...(line 1 of %d)"
721 lines)
722 (dotimes (i lines)
723 (when (= 9 (mod i 10))
724 (message
725 "Scanning for auto-overlays...(line %d of %d)"
726 (+ i 1) lines))
727 (auto-overlay-update nil nil set-id)
728 (forward-line 1))
729 (message "Scanning for auto-overlays...done")))
730 ))
731
732
733
734 (defun auto-overlay-stop (set-id &optional buffer save-file leave-overlays)
735 "Clear all auto-overlays in the set identified by SET-ID
736 from BUFFER, or the current buffer if none is specified.
737
738 If SAVE-FILE is non-nil and the buffer is associated with a file,
739 save the overlays to a file to speed up loading if the same set
740 of regexp definitions is enabled again. If SAVE-FILE is a string,
741 it specifies the location of the file to save to (if it only
742 specifies a directory, the default filename is used). Anything
743 else will cause the overlays to be saved to the default file name
744 in the current directory.
745
746 If LEAVE-OVERLAYS is non-nil, don't bother deleting the overlays
747 from the buffer \(this is generally a bad idea, unless the buffer
748 is about to be killed in which case it speeds things up a bit\)."
749
750 (save-excursion
751 (when buffer (set-buffer buffer))
752 ;; disable overlay set
753 (auto-o-disable-set set-id (current-buffer))
754
755 ;; if SAVE-FILE is non-nil and buffer is associated with a file, save
756 ;; overlays to file
757 (when save-file
758 (unless (stringp save-file) (setq save-file nil))
759 (auto-overlay-save-overlays set-id nil save-file))
760
761 ;; delete overlays unless told not to bother
762 (unless leave-overlays
763 (mapc 'delete-overlay
764 (auto-overlays-in
765 (point-min) (point-max)
766 (list
767 (list (lambda (overlay match) (or overlay match))
768 '(auto-overlay auto-overlay-match))
769 (list 'eq 'set-id set-id))
770 nil 'inactive)))
771
772 ;; if there are no more active auto-overlay definitions...
773 (unless (catch 'enabled
774 (dolist (set auto-overlay-regexps)
775 (when (auto-o-enabled-p (car set))
776 (throw 'enabled t)))
777 nil)
778 ;; run clear hooks
779 (run-hooks 'auto-overlay-unload-hook)
780 ;; reset variables
781 (remove-hook 'after-change-functions 'auto-o-schedule-update t)
782 (remove-hook 'after-change-functions
783 'auto-o-run-after-change-functions t)
784 (setq auto-o-pending-suicides nil
785 auto-o-pending-updates nil
786 auto-o-pending-post-suicide nil))))
787
788
789
790 (defun auto-overlay-save-overlays (set-id &optional buffer file)
791 "Save overlays in set SET-ID in BUFFER to FILE.
792 Defaults to the current buffer.
793
794 If FILE is nil or a directory, and if the buffer is associated
795 with a file, the filename is constructed from the buffer's file
796 name and SET-ID. The directory is created if necessary. If the
797 buffer is not associated with a file and FILE doesn't specify a
798 filename, an error occurs.
799
800 The overlays can be loaded again later using
801 `auto-overlay-load-overlays'."
802
803 (save-excursion
804 (when buffer (set-buffer buffer))
805
806 ;; construct filename
807 (let ((path (or (and file (file-name-directory file)) ""))
808 (filename (or (and file (file-name-nondirectory file)) "")))
809 ;; use default filename if none supplied
810 (when (string= filename "")
811 (if (buffer-file-name)
812 (setq filename (auto-o-overlay-filename set-id))
813 (error "Can't save overlays to default filename when buffer isn't\
814 visiting a file")))
815 ;; create directory if it doesn't exist
816 (make-directory path t)
817 ;; construct full path to file, since that's all we need from now on
818 (setq file (concat path filename)))
819
820 ;; create temporary buffer
821 (let ((buff (generate-new-buffer " *auto-overlay-save*"))
822 overlay-list)
823 ;; write md5 digests to first two lines
824 (prin1 (md5 (current-buffer)) buff)
825 (terpri buff)
826 (prin1 (md5 (prin1-to-string (auto-o-get-regexps set-id))) buff)
827 (terpri buff)
828
829 ;; get sorted list of all match overlays in set SET-ID
830 (setq overlay-list
831 (auto-overlays-in (point-min) (point-max)
832 (list '(identity auto-overlay-match)
833 (list 'eq 'set-id set-id))))
834 (setq overlay-list
835 (sort overlay-list
836 (lambda (a b)
837 (or (< (overlay-start a) (overlay-start b))
838 (and (= (overlay-start a) (overlay-start b))
839 (> (overlay-end a) (overlay-end b)))))))
840
841 ;; write overlay data to temporary buffer
842 (mapc (lambda (o)
843 (prin1 (list (overlay-get o 'definition-id)
844 (overlay-get o 'regexp-id)
845 (overlay-start o)
846 (overlay-end o)
847 (marker-position (overlay-get o 'delim-start))
848 (marker-position (overlay-get o 'delim-end)))
849 buff)
850 (terpri buff))
851 overlay-list)
852
853 ;; save the buffer and kill it
854 (with-current-buffer buff (write-file file))
855 (kill-buffer buff))
856 ))
857
858
859
860 (defun auto-overlay-load-overlays (set-id &optional buffer
861 file no-regexp-check)
862 "Load overlays for BUFFER from FILE.
863 Returns t if successful, nil otherwise.
864 Defaults to the current buffer.
865
866 If FILE is null, or is a string that only specifies a directory,
867 the filename is constructed from the buffer's file name and
868 SET-ID. If the buffer is not associated with a file and FILE
869 doesn't specify a full filename, an error occurs.
870
871 The FILE should be generated by `auto-overlay-save-overlays'. By
872 default, the buffer contents and regexp definitions for SET-ID
873 will be checked to make sure neither have changed since the
874 overlays were saved. If they don't match, the saved overlay data
875 will not be loaded, and the function will return nil.
876
877 If NO-REGEXP-CHECK is non-nil, the check for matching regexp
878 definitions will be skipped; the saved overlays will be loaded
879 even if different regexp definitions were active when the
880 overlays were saved."
881
882 (save-excursion
883 (when buffer (set-buffer buffer))
884
885 ;; construct filename
886 (let ((path (or (and file (file-name-directory file)) ""))
887 (filename (and file (file-name-nondirectory file))))
888 ;; use default filename if none supplied
889 ;; FIXME: should we throw error if buffer not associated with file?
890 (when (string= filename "")
891 (setq filename (auto-o-overlay-filename set-id)))
892 ;; construct full path to file, since that's all we need from now on
893 (setq file (concat path filename)))
894
895
896 ;; return nil if file does not exist
897 (if (not (file-exists-p file))
898 nil
899
900 ;; otherwise...
901 (let ((buff (find-file-noselect file t))
902 md5-buff md5-regexp data o-match o-new lines
903 (i 0))
904
905 ;; read md5 digests from first two lines of FILE
906 (with-current-buffer buff (goto-char (point-min)))
907 (setq md5-buff (read buff))
908 (setq md5-regexp (read buff))
909
910
911 ;; if saved buffer md5 sum doesn't match buffer contents, or if saved
912 ;; regexp md5 sum doesn't match regexp definitions and checking is not
913 ;; overridden, return nil
914 (if (not (and (string= md5-buff (md5 (current-buffer)))
915 (or no-regexp-check
916 (string= md5-regexp
917 (md5 (prin1-to-string
918 (auto-o-get-regexps set-id)))))))
919 (progn (kill-buffer buff) nil)
920
921 ;; count number of overlays, for progress message
922 (with-current-buffer buff
923 (setq lines (count-lines (point) (point-max))))
924
925 ;; read overlay data from FILE until we reach the end
926 (message "Loading auto-overlays...(1 of %d)" lines)
927 (while (condition-case nil (setq data (read buff)) ('end-of-file))
928 ;; create a match overlay corresponding to the data
929 (setq o-match (auto-o-make-match
930 set-id (nth 0 data) (nth 1 data) (nth 2 data)
931 (nth 3 data) (nth 4 data) (nth 5 data)))
932 ;; call the appropriate parse function, unless match overlay is
933 ;; within a higher priority exclusive overlay
934 (unless (auto-o-within-exclusive-p
935 (overlay-get o-match 'delim-start)
936 (overlay-get o-match 'delim-end)
937 (assq 'priority (auto-o-entry-props
938 (overlay-get o-match 'definition-id)
939 (overlay-get o-match 'regexp-id))))
940 (setq o-new
941 (funcall (auto-o-parse-function o-match) o-match))
942 (unless (listp o-new) (setq o-new (list o-new)))
943 ;; give any new overlays some basic properties
944 (mapc (lambda (o)
945 (overlay-put o 'auto-overlay t)
946 (overlay-put o 'set-id set-id)
947 (overlay-put o 'definition-id
948 (overlay-get o-match 'definition-id))
949 (overlay-put o 'regexp-id
950 (overlay-get o-match 'regexp-id)))
951 o-new)
952 ;; run match function if there is one
953 (let ((match-func (auto-o-match-function o-match)))
954 (when match-func (funcall match-func o-match))))
955 ;; display progress message
956 (setq i (1+ i))
957 (when (= 0 (mod i 10))
958 (message "Loading auto-overlays...(%d of %d)" i lines)))
959
960 (kill-buffer buff)
961 t))))) ; return t to indicate successful loading)
962
963
964
965
966
967 ;;;=============================================================
968 ;;; auto-overlay overlay functions
969
970 (defun auto-o-run-after-change-functions (beg end len)
971 ;; Assigned to the `after-change-functions' hook. Run all the various
972 ;; functions that should run after a change to the buffer, in the correct
973 ;; order.
974
975 ;; ignore changes that aren't either insertions or deletions
976 (when (and (not undo-in-progress)
977 (or (and (/= beg end) (= len 0)) ; insertion
978 (and (= beg end) (/= len 0)))) ; deletion
979 ;; repeat until all the pending functions have been cleared (it may be
980 ;; necessary to run multiple times since the pending functions may
981 ;; themselves cause more functions to be added to the pending lists)
982 (while (or auto-o-pending-pre-suicide auto-o-pending-suicides
983 auto-o-pending-post-suicide auto-o-pending-updates
984 auto-o-pending-post-update)
985 ;; run pending pre-suicide functions
986 (when auto-o-pending-pre-suicide
987 (mapc (lambda (f) (apply (car f) (cdr f)))
988 auto-o-pending-pre-suicide)
989 (setq auto-o-pending-pre-suicide nil))
990 ;; run pending suicides
991 (when auto-o-pending-suicides
992 (mapc 'auto-o-suicide auto-o-pending-suicides)
993 (setq auto-o-pending-suicides nil))
994 ;; run pending post-suicide functions
995 (when auto-o-pending-post-suicide
996 (mapc (lambda (f) (apply (car f) (cdr f)))
997 auto-o-pending-post-suicide)
998 (setq auto-o-pending-post-suicide nil))
999 ;; run updates
1000 (when auto-o-pending-updates
1001 (mapc (lambda (l) (auto-overlay-update (car l) (cdr l)))
1002 auto-o-pending-updates)
1003 (setq auto-o-pending-updates nil))
1004 ;; run pending post-update functions
1005 (when auto-o-pending-post-update
1006 (mapc (lambda (f) (apply (car f) (cdr f)))
1007 auto-o-pending-post-update)
1008 (setq auto-o-pending-post-update nil))
1009 ))
1010
1011 ;; ;; FIXME: horrible hack to delete all marker update entries in latest
1012 ;; ;; `buffer-undo-list' change group, since undoing these can badly
1013 ;; ;; mess up the overlays
1014 ;; (while (and (consp (car buffer-undo-list))
1015 ;; (markerp (caar buffer-undo-list)))
1016 ;; (setq buffer-undo-list (cdr buffer-undo-list)))
1017 ;; (let ((p buffer-undo-list))
1018 ;; (while (cadr p)
1019 ;; (if (and (consp (cadr p)) (markerp (car (cadr p))))
1020 ;; (setcdr p (cddr p))
1021 ;; (setq p (cdr p)))))
1022 )
1023
1024
1025
1026 (defun auto-o-schedule-update (start &optional end unused set-id)
1027 ;; Schedule `auto-overlay-update' of lines between positions START and END
1028 ;; (including lines containing START and END), optionally restricted to
1029 ;; SET-ID. If END is not supplied, schedule update for just line containing
1030 ;; START. The update will be run by `auto-o-run-after-change-functions'
1031 ;; after buffer modification is complete. This function is assigned to
1032 ;; `after-change-functions'.
1033
1034 (save-restriction
1035 (widen) ; need to widen, since goto-line goes to absolute line
1036 (setq start (line-number-at-pos start))
1037 (setq end (if end (line-number-at-pos end) start))
1038
1039 (let ((pending auto-o-pending-updates))
1040 (cond
1041 ;; if pending list is empty, just add new entry to the list
1042 ((null pending)
1043 (setq auto-o-pending-updates (list (cons start end))))
1044
1045 ;; if start of the new entry is before start of the first entry in
1046 ;; pending list, add new entry to front of the list
1047 ((<= start (caar pending))
1048 (setq auto-o-pending-updates (nconc (list (cons start end)) pending))
1049 (setq pending auto-o-pending-updates))
1050
1051 ;; otherwise...
1052 (t
1053 ;; search for entry in pending list that new one should come after
1054 ;; Note: we do an O(n) linear search here, as opposed to the O(log n)
1055 ;; we would get were we to store the entries in a binary tree. But the
1056 ;; pending list is unlikely to ever be all that long, so the
1057 ;; optimisation almost certainly isn't worth the effort.
1058 (catch 'found
1059 (while (cdr pending)
1060 (when (<= start (car (cadr pending))) (throw 'found t))
1061 (setq pending (cdr pending))))
1062 ;; if start of new entry is before end of entry it should come after,
1063 ;; merge it with that entry
1064 (if (<= start (1+ (cdar pending)))
1065 (when (> end (cdar pending)) (setcdr (car pending) end))
1066 ;; otherwise, insert new entry after it
1067 (setcdr pending (nconc (list (cons start end)) (cdr pending)))
1068 (setq pending (cdr pending)))
1069 ))
1070
1071 ;; merge new entry with successive entries until end of merged entry is
1072 ;; before start of next entry (see above note about O(n) vs. O(log n))
1073 (while (and (cdr pending)
1074 (>= (1+ (cdar pending)) (car (cadr pending))))
1075 (setcdr (car pending) (max (cdar pending) (cdr (cadr pending))))
1076 (setcdr pending (cddr pending)))
1077 )))
1078
1079
1080
1081 (defun auto-o-schedule-delete-in-front-or-behind-suicide (start end len)
1082 ;; Schedule `auto-o-suicide' for any overlay that has had characters deleted
1083 ;; in front or behind it, to simulate missing `delete-in-front-hooks' and
1084 ;; `delete-behind-hooks' overlay properties
1085 (unless (= len 0)
1086 (dolist (o (auto-overlays-at-point nil '(identity auto-overlay-match)))
1087 (when (or (= (overlay-end o) start) (= (overlay-start o) end))
1088 (auto-o-adjoin o auto-o-pending-suicides)))))
1089
1090
1091
1092 (defun auto-o-schedule-suicide (o-self &optional modified &rest unused)
1093 ;; Schedule `auto-o-suicide' to run after buffer modification is
1094 ;; complete. It will be run by `auto-o-run-after-change-functions'. Assigned
1095 ;; to overlay modification and insert in-front/behind hooks.
1096 (unless modified (auto-o-adjoin o-self auto-o-pending-suicides)))
1097
1098
1099
1100 (defun auto-overlay-update (&optional start end set-id)
1101 ;; Parse lines from line number START to line number END. If only START is
1102 ;; supplied, just parse that line. If neither are supplied, parse line
1103 ;; containing the point. If SET-ID is specified, only look for matches in
1104 ;; that set of overlay regexps definitions.
1105
1106 (save-restriction
1107 (widen)
1108 (let (regexp-entry definition-id class regexp group priority set-id
1109 regexp-id o-match o-overlap o-new)
1110 (unless start (setq start (line-number-at-pos)))
1111 (save-excursion
1112 (save-match-data
1113 ;; (goto-line start) without messing around with mark and messages
1114 ;; Note: this is a bug in simple.el; there clearly can be a need for
1115 ;; non-interactive calls to goto-line from Lisp code, and
1116 ;; there's no warning about doing this. Yet goto-line *always*
1117 ;; calls push-mark, which usually *shouldn't* be invoked by
1118 ;; Lisp programs, as its docstring warns.
1119 (goto-char 1)
1120 (if (eq selective-display t)
1121 (re-search-forward "[\n\C-m]" nil 'end (1- start))
1122 (forward-line (1- start)))
1123
1124 (dotimes (i (if end (1+ (- end start)) 1))
1125
1126 ;; check each enabled set of overlays, or just the specified set
1127 (dotimes (s (if set-id 1 (length auto-overlay-regexps)))
1128 (setq set-id (or set-id (car (nth s auto-overlay-regexps))))
1129 (when (auto-o-enabled-p set-id)
1130
1131 ;; check each auto-overlay definition in regexp set
1132 (dolist (regexp-entry (auto-o-get-regexps set-id))
1133 (setq definition-id (pop regexp-entry))
1134 (setq class (pop regexp-entry))
1135
1136 ;; check all regexps for current definition
1137 (dotimes (rank (length regexp-entry))
1138 (setq regexp-id (car (nth rank regexp-entry)))
1139
1140 ;; extract regexp properties from current entry
1141 (setq regexp (auto-o-entry-regexp set-id definition-id
1142 regexp-id))
1143 (setq group (auto-o-entry-regexp-group
1144 set-id definition-id regexp-id))
1145 (setq priority
1146 (cdr (assq 'priority
1147 (auto-o-entry-props
1148 set-id definition-id regexp-id))))
1149
1150
1151 ;; look for matches in current line, ensuring case *is*
1152 ;; significant
1153 (forward-line 0)
1154 (while (let ((case-fold-search nil))
1155 (re-search-forward regexp (line-end-position) t))
1156 ;; sanity check regexp definition against match
1157 (when (or (null (match-beginning group))
1158 (null (match-end group)))
1159 (error "Match for regexp \"%s\" has no group %d"
1160 regexp group))
1161
1162 (cond
1163 ;; ignore match if it already has a match overlay
1164 ((auto-o-matched-p (match-beginning 0) (match-end 0)
1165 set-id definition-id regexp-id))
1166
1167
1168 ;; if existing match overlay corresponding to same entry
1169 ;; and edge but different subentry overlaps new match...
1170 ((setq o-overlap
1171 (auto-o-overlapping-match
1172 (match-beginning group) (match-end group)
1173 set-id definition-id regexp-id
1174 (auto-o-entry-edge set-id definition-id
1175 regexp-id)))
1176 ;; if new match takes precedence, replace existing one
1177 ;; with new one, otherwise ignore new match
1178 (when (< rank (auto-o-rank o-overlap))
1179 (delete-overlay o-overlap)
1180 (setq o-match (auto-o-make-match
1181 set-id definition-id regexp-id
1182 (match-beginning 0) (match-end 0)
1183 (match-beginning group)
1184 (match-end group)))
1185 (when (overlay-get o-overlap 'parent)
1186 (auto-o-match-overlay
1187 (overlay-get o-overlap 'parent)
1188 o-match))
1189 ;; run match function if there is one
1190 (let ((match-func (auto-o-match-function o-match)))
1191 (when match-func (funcall match-func o-match)))))
1192
1193 ;; if match is within a higher priority exclusive
1194 ;; overlay, create match overlay but don't parse it
1195 ((auto-o-within-exclusive-p (match-beginning group)
1196 (match-end group)
1197 priority)
1198 (auto-o-make-match set-id definition-id regexp-id
1199 (match-beginning 0) (match-end 0)
1200 (match-beginning group)
1201 (match-end group)))
1202
1203
1204 ;; if we're going to parse the new match...
1205 (t
1206 ;; create a match overlay for it
1207 (setq o-match (auto-o-make-match
1208 set-id definition-id regexp-id
1209 (match-beginning 0) (match-end 0)
1210 (match-beginning group)
1211 (match-end group)))
1212 ;; call the appropriate parse function
1213 (setq o-new
1214 (funcall (auto-o-parse-function o-match) o-match))
1215 (unless (listp o-new) (setq o-new (list o-new)))
1216 ;; give any new overlays some basic properties
1217 (mapc (lambda (o)
1218 (overlay-put o 'auto-overlay t)
1219 (overlay-put o 'set-id set-id)
1220 (overlay-put o 'definition-id definition-id)
1221 (overlay-put o 'regexp-id regexp-id))
1222 o-new)
1223 ;; run match function if there is one
1224 (let ((match-func (auto-o-match-function o-match)))
1225 (when match-func (funcall match-func o-match)))))
1226
1227
1228 ;; go to character one beyond the start of the match, to
1229 ;; make sure we don't miss the next match (if we find the
1230 ;; same one again, it will just be ignored)
1231 (goto-char (+ (match-beginning 0) 1)))))
1232 (forward-line 1))
1233 )))
1234 ))))
1235
1236
1237
1238
1239 (defun auto-o-suicide (o-self &optional force)
1240 ;; This function is assigned to all match overlay modification hooks, and
1241 ;; calls the appropriate suicide function for match overlay O-SELF.
1242 ;; If FORCE is non-nil, O-SELF is deleted irrespective of whether its
1243 ;; overlay still matches.
1244
1245 ;; have to widen temporarily
1246 (save-restriction
1247 (widen)
1248 ;; ;; this condition is here to avoid a weird Emacs bug(?) where the
1249 ;; ;; modification-hooks seem to be called occasionally for overlays that
1250 ;; ;; have already been deleted
1251 ;; (when (overlay-buffer o-self)
1252 ;; if match overlay no longer matches the text it covers...
1253 (unless (and (not force)
1254 (overlay-buffer o-self)
1255 (save-excursion
1256 (goto-char (overlay-start o-self))
1257 (looking-at (auto-o-regexp o-self)))
1258 (= (match-end 0) (overlay-end o-self)))
1259
1260 ;; if we have a parent overlay...
1261 (let ((o-parent (overlay-get o-self 'parent))
1262 o-other)
1263 (when o-parent
1264 ;; if our regexp class is a compound class...
1265 (when (auto-o-complex-class-p o-self)
1266 (setq o-other
1267 (overlay-get o-parent (if (eq (auto-o-edge o-self) 'start)
1268 'start 'end)))
1269 ;; if parent's properties have been set by us, remove them
1270 (when (or (null o-other)
1271 (>= (auto-o-rank o-self)
1272 (auto-o-rank o-other)))
1273 (dolist (p (auto-o-props o-self))
1274 (overlay-put o-parent (car p) nil))))
1275 ;; call appropriate suicide function
1276 (funcall (auto-o-suicide-function o-self) o-self)))
1277
1278 ;; schedule an update (necessary since if match regexp contains
1279 ;; "context", we may be comitting suicide only for the match overlay
1280 ;; to be recreated in a slightly different place)
1281 (auto-o-schedule-update (overlay-start o-self))
1282 ;; delete ourselves
1283 (delete-overlay o-self));)
1284 ))
1285
1286
1287
1288
1289 (defun auto-o-update-exclusive (set-id beg end old-priority new-priority)
1290 ;; If priority has increased, delete all overlays between BEG end END that
1291 ;; have priority lower than NEW-PRIORITY. If priority has decreased, re-parse
1292 ;; all matches with priority lower than OLD-PRIORITY.
1293
1294 (let (overlay-list)
1295 (cond
1296 ;; if priority has increased...
1297 ((and new-priority
1298 (or (null old-priority) (> new-priority old-priority)))
1299 ;; find overlays entirely within BEG and END that are both start and end
1300 ;; matched and have priority lower than NEW-PRIORITY
1301 (setq overlay-list
1302 (auto-overlays-in
1303 beg end
1304 (list '(identity auto-overlay)
1305 (list 'eq 'set-id set-id)
1306 '(identity start)
1307 (list (lambda (definition-id start end)
1308 (or (null (auto-o-entry-complex-class-p
1309 set-id definition-id))
1310 (and start end)))
1311 '(definition-id start end))
1312 (list (lambda (pri new) (or (null pri) (< pri new)))
1313 'priority new-priority))
1314 'within))
1315 ;; mark overlays in list as inactive (more efficient than calling
1316 ;; suicide functions or deleting the overlays, and leaves them intact in
1317 ;; case the exclusivity of the region is later reduced - see below)
1318 (dolist (o overlay-list) (overlay-put o 'inactive t))
1319
1320 ;; find match overlays between BEG and END that have priority lower then
1321 ;; NEW-PRIORITY but still have an active parent overlay
1322 (setq overlay-list
1323 (auto-overlays-in
1324 beg end
1325 (list '(identity auto-overlay-match)
1326 (list 'eq 'set-id set-id)
1327 ;; note: parentless overlays are possible if a suicide is
1328 ;; in progress, so need to check overlay has a parent first
1329 '(identity parent)
1330 (list (lambda (parent)
1331 (not (overlay-get parent 'inactive)))
1332 'parent)
1333 (list (lambda (set-id definition-id regexp-id new-pri)
1334 (let ((pri (cdr (assq
1335 'priority
1336 (auto-o-entry-props
1337 set-id definition-id regexp-id)))))
1338 (or (null pri) (< pri new-pri))))
1339 '(set-id definition-id regexp-id)
1340 (list new-priority)))))
1341 ;; call appropriate suicide function for each match overlay in list
1342 (dolist (o overlay-list) (funcall (auto-o-suicide-function o) o)))
1343
1344
1345 ;; if priority has decreased...
1346 ((and old-priority
1347 (or (null new-priority) (< new-priority old-priority)))
1348 ;; find inactive overlays entirely within BEG and END that have priority
1349 ;; higher or equal to NEW-PRIORITY
1350 (setq overlay-list
1351 (auto-overlays-in
1352 beg end
1353 (list '(identity auto-overlay)
1354 (list 'eq 'set-id set-id)
1355 '(identity inactive)
1356 (list (lambda (pri new) (or (null new) (>= pri new)))
1357 'priority new-priority))
1358 'within 'inactive))
1359 ;; mark overlays in list as active again
1360 (dolist (o overlay-list) (overlay-put o 'inactive nil))
1361
1362 ;; find match overlays between BEG and END that have priority higher or
1363 ;; equal to NEW-PRIORITY but no parent overlay
1364 (setq overlay-list
1365 (auto-overlays-in
1366 beg end
1367 (list '(identity auto-overlay-match)
1368 (list 'eq 'set-id set-id)
1369 '(null parent)
1370 (list (lambda (set-id definition-id regexp-id new-pri)
1371 (let ((pri (cdr (assq
1372 'priority
1373 (auto-o-entry-props
1374 set-id definition-id regexp-id)))))
1375 (or (null new-pri) (>= pri new-pri))))
1376 '(set-id definition-id regexp-id)
1377 (list new-priority)))))
1378 ;; call appropriate parse function for each match overlay in list
1379 (dolist (o-match overlay-list)
1380 (when (not (auto-o-within-exclusive-p o-match))
1381 (let ((o-new (funcall (auto-o-parse-function o-match) o-match)))
1382 ;; give any new overlays the basic properties and add them to
1383 ;; `auto-overlay-list'
1384 (unless (listp o-new) (setq o-new (list o-new)))
1385 (mapc (lambda (o)
1386 (overlay-put o 'auto-overlay t)
1387 (overlay-put o 'set-id set-id)
1388 (overlay-put o 'definition-id
1389 (overlay-get o-match 'definition-id))
1390 (overlay-put o 'regexp-id
1391 (overlay-get o-match 'regexp-id)))
1392 o-new)))))
1393 )))
1394
1395
1396
1397
1398 (defun auto-o-make-match (set-id definition-id regexp-id start end
1399 &optional delim-start delim-end)
1400 ;; Create a new match overlay and give it the appropriate properties.
1401 (let ((o-match (make-overlay start end nil 'front-advance nil)))
1402 (overlay-put o-match 'auto-overlay-match t)
1403 (overlay-put o-match 'set-id set-id)
1404 (overlay-put o-match 'definition-id definition-id)
1405 (overlay-put o-match 'regexp-id regexp-id)
1406 (overlay-put o-match 'delim-start
1407 (set-marker (make-marker)
1408 (if delim-start delim-start start)))
1409 (overlay-put o-match 'delim-end
1410 (set-marker (make-marker)
1411 (if delim-end delim-end end)))
1412 (set-marker-insertion-type (overlay-get o-match 'delim-start) t)
1413 (set-marker-insertion-type (overlay-get o-match 'delim-end) nil)
1414 (overlay-put o-match 'modification-hooks '(auto-o-schedule-suicide))
1415 (overlay-put o-match 'insert-in-front-hooks '(auto-o-schedule-suicide))
1416 (overlay-put o-match 'insert-behind-hooks '(auto-o-schedule-suicide))
1417 ;; return the new match overlay
1418 o-match))
1419
1420
1421
1422
1423 (defun auto-o-match-overlay (overlay start &optional end
1424 no-props no-parse protect-match)
1425 "Match start and end of OVERLAY with START and END match overlays.
1426 If START or END are numbers or markers, move that edge to the
1427 buffer location specified by the number or marker and make it
1428 unmatched. If START or END are non-nil but neither of the above,
1429 make that edge unmatched. If START or END are null, don't change
1430 that edge. However, if END is null, and START is an 'end overlay,
1431 match end of OVERLAY rather than start.
1432
1433 If NO-PARSE is non-nil, block re-parsing due to exclusive overlay
1434 changes. If NO-PROPS is non-nil, block updating of overlay's
1435 properties. If PROTECT-MATCH is non-nil, don't modify any match
1436 overlays associated with OVERLAY (i.e. don't modify their 'parent
1437 properties)."
1438
1439 (let ((old-start (overlay-start overlay))
1440 (old-end (overlay-end overlay))
1441 (old-o-start (overlay-get overlay 'start))
1442 (old-o-end (overlay-get overlay 'end))
1443 (old-exclusive (overlay-get overlay 'exclusive))
1444 (old-priority (overlay-get overlay 'priority)))
1445
1446 ;; if END is null, we're not unmatching, and START is an end overlay,
1447 ;; match end of overlay instead of start (Note: assumes we're matching an
1448 ;; overlay class with 'start and 'end regexps)
1449 (when (and (null end) (overlayp start) (eq (auto-o-edge start) 'end))
1450 (setq end start)
1451 (setq start nil))
1452
1453
1454 ;; move overlay to new location
1455 (move-overlay overlay
1456 (cond
1457 ((overlayp start) (overlay-get start 'delim-end))
1458 ((number-or-marker-p start) start)
1459 (start (point-min))
1460 (t (overlay-start overlay)))
1461 (cond
1462 ((overlayp end) (overlay-get end 'delim-start))
1463 ((number-or-marker-p end) end)
1464 (end (point-max))
1465 (t (overlay-end overlay))))
1466
1467 ;; if changing start match...
1468 (when start
1469 ;; sort out parent property of old start match
1470 (when (and old-o-start (not (eq old-o-start end)) (null protect-match))
1471 (overlay-put old-o-start 'parent nil))
1472 ;; if unmatching start, set start property to nil
1473 (if (not (overlayp start))
1474 (overlay-put overlay 'start nil)
1475 ;; if matching start, set start property to new start match
1476 (overlay-put overlay 'start start)
1477 (overlay-put start 'parent overlay)))
1478
1479 ;; if changing end match...
1480 (when end
1481 ;; sort out parent property of old end match
1482 (when (and old-o-end (not (eq old-o-end start)) (null protect-match))
1483 (overlay-put old-o-end 'parent nil))
1484 ;; if unmatching end, set end property to nil
1485 (if (not (overlayp end))
1486 (overlay-put overlay 'end nil)
1487 ;; if matching end, set end property to new end match
1488 (overlay-put overlay 'end end)
1489 (overlay-put end 'parent overlay)))
1490
1491
1492 ;; unless it's blocked, update properties if new match takes precedence
1493 ;; (Note: this sometimes sets the overlay's properties to the ones it
1494 ;; already had, but it hardly seems worth checking for that)
1495 (unless no-props
1496 ;; when start was previously matched and is being changed, remove
1497 ;; properties due to old start match
1498 ;; Note: no need to check if properties were really set by start match,
1499 ;; since if not they will be recreated below
1500 (when (and start old-o-start)
1501 (dolist (p (auto-o-props old-o-start))
1502 (overlay-put overlay (car p) nil)))
1503 ;; when end was previously matched and is being changed, remove
1504 ;; properties due to old end match (see note above)
1505 (when (and end old-o-end)
1506 (dolist (p (auto-o-props old-o-end))
1507 (overlay-put overlay (car p) nil)))
1508 ;; sort out properties due to new matches
1509 (let (props)
1510 (cond
1511 ;; if start has been unmatched, use properties of end match
1512 ((not (auto-o-start-matched-p overlay))
1513 (setq props (auto-o-props (overlay-get overlay 'end))))
1514 ;; if end has been unmatched, use properties of start match
1515 ((not (auto-o-end-matched-p overlay))
1516 (setq props (auto-o-props (overlay-get overlay 'start))))
1517 (t ;; otherwise, use properties of whichever match takes precedence
1518 (let ((o-start (overlay-get overlay 'start))
1519 (o-end (overlay-get overlay 'end)))
1520 (if (<= (auto-o-rank o-start)
1521 (auto-o-rank o-end))
1522 (setq props (auto-o-props o-start))
1523 (setq props (auto-o-props o-end))))))
1524 ;; bundle properties inside a list if not already, then update them
1525 (when (symbolp (car props)) (setq props (list props)))
1526 (dolist (p props) (overlay-put overlay (car p) (cdr p)))))
1527
1528
1529 ;; unless it's blocked or overlay is inactive, check if anything needs
1530 ;; reparsing due to exclusive overlay changes
1531 (unless (or no-parse (overlay-get overlay 'inactive))
1532 (let ((set-id (overlay-get overlay 'set-id))
1533 (start (overlay-start overlay))
1534 (end (overlay-end overlay))
1535 (exclusive (overlay-get overlay 'exclusive))
1536 (priority (overlay-get overlay 'priority)))
1537 (cond
1538
1539 ;; if overlay wasn't and still isn't exclusive, do nothing
1540 ((and (null exclusive) (null old-exclusive)))
1541
1542 ;; if overlay has become exclusive, delete lower priority overlays
1543 ;; within it
1544 ((and (null old-exclusive) exclusive)
1545 (auto-o-update-exclusive set-id start end nil priority))
1546
1547 ;; if overlay was exclusive but no longer is, re-parse region it
1548 ;; used to cover
1549 ((and old-exclusive (null exclusive))
1550 (auto-o-update-exclusive set-id old-start old-end old-priority nil))
1551
1552 ;; if overlay was and is exclusive, and has been moved to a
1553 ;; completely different location re-parse old location and delete
1554 ;; lower priority overlays within new location
1555 ((or (< end old-start) (> start old-start))
1556 (auto-o-update-exclusive set-id start end old-priority nil)
1557 (auto-o-update-exclusive set-id start end nil priority))
1558
1559 ;; if overlay was and is exclusive, and overlaps its old location...
1560 (t
1561 ;; if priority has changed, re-parse/delete in overlap region
1562 (when (/= old-priority priority)
1563 (auto-o-update-exclusive set-id
1564 (max start old-start) (min end old-end)
1565 old-priority priority))
1566 (cond
1567 ;; if overlay was exclusive and start has shrunk, re-parse
1568 ;; uncovered region
1569 ((and (> start old-start) old-exclusive)
1570 (auto-o-update-exclusive set-id old-start start old-priority nil))
1571 ;; if overlay is exclusive and has grown, delete lower priority
1572 ;; overlays in newly covered region
1573 ((and (< start old-start) exclusive)
1574 (auto-o-update-exclusive set-id start old-start nil priority)))
1575 (cond
1576 ;; if overlay was exclusive and end has shrunk, re-parse
1577 ((and (< end old-end) old-exclusive)
1578 (auto-o-update-exclusive set-id end old-end old-priority nil))
1579 ;; if overlay is exclusive and has grown, delete lower priority
1580 ((and (> end old-end) exclusive)
1581 (auto-o-update-exclusive set-id old-end end nil priority))))
1582 )))
1583 ))
1584
1585
1586
1587
1588 (defun auto-o-delete-overlay (overlay &optional no-parse protect-match)
1589 "Delete OVERLAY from buffer.
1590
1591 If PROTECT-MATCH is non-nil, don't modify any match overlays
1592 associated with OVERLAY (i.e. leave their 'parent properties
1593 alone). If NO-PARSE is non-nil, block re-parsing due to exclusive
1594 overlay changes."
1595
1596 (let ((start (overlay-start overlay))
1597 (end (overlay-end overlay))
1598 o-match)
1599 ;; delete overlay from buffer and `auto-overlay-list'
1600 (delete-overlay overlay)
1601 (unless (setq o-match (overlay-get overlay 'start))
1602 (setq o-match (overlay-get overlay 'end)))
1603 ;; (auto-o-delete-from-overlay-list overlay)
1604
1605 ;; unless blocked, if overlay's exclusive flag was set, re-parse region it
1606 ;; covered
1607 (when (and (null no-parse) (overlay-get overlay 'exclusive))
1608 (auto-o-update-exclusive (overlay-get overlay 'set-id) start end
1609 (overlay-get overlay 'priority) nil))
1610
1611 ;; Note: it's vital that the match overlays' parent properties are only
1612 ;; set to nil *after* `auto-update-exclusive' is run: if the overlay
1613 ;; overlapped one of its match overlays, the newly parentless match
1614 ;; overlay would be re-parsed by `auto-update-exclusive', which would
1615 ;; re-create the parent overlay that's just been deleted!
1616
1617 ;; unmatch match overlays
1618 (unless protect-match
1619 (when (setq o-match (overlay-get overlay 'start))
1620 (overlay-put o-match 'parent nil))
1621 (when (setq o-match (overlay-get overlay 'end))
1622 (overlay-put o-match 'parent nil)))
1623 ))
1624
1625
1626
1627
1628 (defun auto-o-matched-p (beg end set-id definition-id &optional regexp-id)
1629 ;; Determine if characters between BEG end END are already matched by a
1630 ;; match overlay corresponding to DEFINITION-ID (and optionally REGEXP-ID)
1631 ;; of regexp set SET-ID.
1632 (let (o-match)
1633 (catch 'match
1634 (mapc (lambda (o)
1635 (when (and (overlay-get o 'auto-overlay-match)
1636 (eq (overlay-get o 'set-id) set-id)
1637 (eq (overlay-get o 'definition-id) definition-id)
1638 (eq (overlay-get o 'regexp-id) regexp-id)
1639 (= (overlay-start o) beg)
1640 (= (overlay-end o) end))
1641 (setq o-match o)
1642 (throw 'match t)))
1643 (overlays-in beg end)))
1644 o-match))
1645
1646
1647
1648
1649 (defun auto-o-within-exclusive-p (match &optional end priority)
1650 ;; If MATCH is an overlay, determine if it is within a higher priority
1651 ;; exclusive overlay. If MATCH is a number or marker, determine whether
1652 ;; region between MATCH and END is within an exclusive overlay with higher
1653 ;; priority than PRIORITY.
1654
1655 (when (null end)
1656 (setq end (overlay-get match 'delim-end))
1657 (setq priority (overlay-get match 'priority))
1658 (setq match (overlay-get match 'delim-start)))
1659
1660 ;; look for higher priority exclusive overlays
1661 (auto-overlays-in
1662 match end
1663 (list '(identity auto-overlay)
1664 '(identity exclusive)
1665 (list (lambda (p q) (and p (or (null q) (> p q))))
1666 'priority priority)))
1667 )
1668
1669
1670
1671
1672 (defun auto-o-overlapping-match (beg end set-id definition-id regexp-id edge)
1673 ;; Returns any match overlay corresponding to same SET-ID, DEFINITION-ID and
1674 ;; EDGE but different REGEXP-ID whose delimiter overlaps region from BEG to
1675 ;; END. (Only returns first one it finds; which is returned if more than one
1676 ;; exists is undefined.)
1677 (let (o-overlap)
1678 (catch 'match
1679 (mapc (lambda (o)
1680 (when (and (overlay-get o 'auto-overlay-match)
1681 (eq (overlay-get o 'set-id) set-id)
1682 (eq (overlay-get o 'definition-id) definition-id)
1683 (not (eq (overlay-get o 'regexp-id) regexp-id))
1684 (eq (auto-o-edge o) edge)
1685 ;; check delimiter (not just o) overlaps BEG to END
1686 (< (overlay-get o 'delim-start) end)
1687 (> (overlay-get o 'delim-end) beg))
1688 (setq o-overlap o)
1689 (throw 'match t)))
1690 (overlays-in beg end)))
1691 o-overlap))
1692
1693
1694
1695
1696 ;;; ===============================================================
1697 ;;; Compatibility Stuff
1698
1699 (unless (fboundp 'line-number-at-pos)
1700 (require 'auto-overlays-compat)
1701 (defalias 'line-number-at-pos
1702 'auto-overlays-compat-line-number-at-pos))
1703
1704
1705 (unless (fboundp 'replace-regexp-in-string)
1706 (require 'auto-overlays-compat)
1707 (defalias 'replace-regexp-in-string
1708 'auto-overlays-compat-replace-regexp-in-string))
1709
1710 ;;; auto-overlays.el ends here