]> code.delx.au - gnu-emacs/blob - src/textprop.c
(validate_interval_range, interval_of): Use string macros instead of
[gnu-emacs] / src / textprop.c
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 #include <config.h>
23 #include "lisp.h"
24 #include "intervals.h"
25 #include "buffer.h"
26 #include "window.h"
27
28 #ifndef NULL
29 #define NULL (void *)0
30 #endif
31
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
34
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
36 \f
37
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
41
42 set_properties needs to deal with the interval property cache.
43
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
49
50 \f
51 /* Types of hooks. */
52 Lisp_Object Qmouse_left;
53 Lisp_Object Qmouse_entered;
54 Lisp_Object Qpoint_left;
55 Lisp_Object Qpoint_entered;
56 Lisp_Object Qcategory;
57 Lisp_Object Qlocal_map;
58
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
61 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
62
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70
71 Lisp_Object Vinhibit_point_motion_hooks;
72 Lisp_Object Vdefault_text_properties;
73 Lisp_Object Vchar_property_alias_alist;
74 Lisp_Object Vtext_property_default_nonsticky;
75
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks;
79 Lisp_Object interval_insert_in_front_hooks;
80
81
82 /* Signal a `text-read-only' error. This function makes it easier
83 to capture that error in GDB by putting a breakpoint on it. */
84
85 static void
86 text_read_only ()
87 {
88 Fsignal (Qtext_read_only, Qnil);
89 }
90
91
92 \f
93 /* Extract the interval at the position pointed to by BEGIN from
94 OBJECT, a string or buffer. Additionally, check that the positions
95 pointed to by BEGIN and END are within the bounds of OBJECT, and
96 reverse them if *BEGIN is greater than *END. The objects pointed
97 to by BEGIN and END may be integers or markers; if the latter, they
98 are coerced to integers.
99
100 When OBJECT is a string, we increment *BEGIN and *END
101 to make them origin-one.
102
103 Note that buffer points don't correspond to interval indices.
104 For example, point-max is 1 greater than the index of the last
105 character. This difference is handled in the caller, which uses
106 the validated points to determine a length, and operates on that.
107 Exceptions are Ftext_properties_at, Fnext_property_change, and
108 Fprevious_property_change which call this function with BEGIN == END.
109 Handle this case specially.
110
111 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
112 create an interval tree for OBJECT if one doesn't exist, provided
113 the object actually contains text. In the current design, if there
114 is no text, there can be no text properties. */
115
116 #define soft 0
117 #define hard 1
118
119 INTERVAL
120 validate_interval_range (object, begin, end, force)
121 Lisp_Object object, *begin, *end;
122 int force;
123 {
124 register INTERVAL i;
125 int searchpos;
126
127 CHECK_STRING_OR_BUFFER (object);
128 CHECK_NUMBER_COERCE_MARKER (*begin);
129 CHECK_NUMBER_COERCE_MARKER (*end);
130
131 /* If we are asked for a point, but from a subr which operates
132 on a range, then return nothing. */
133 if (EQ (*begin, *end) && begin != end)
134 return NULL_INTERVAL;
135
136 if (XINT (*begin) > XINT (*end))
137 {
138 Lisp_Object n;
139 n = *begin;
140 *begin = *end;
141 *end = n;
142 }
143
144 if (BUFFERP (object))
145 {
146 register struct buffer *b = XBUFFER (object);
147
148 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
149 && XINT (*end) <= BUF_ZV (b)))
150 args_out_of_range (*begin, *end);
151 i = BUF_INTERVALS (b);
152
153 /* If there's no text, there are no properties. */
154 if (BUF_BEGV (b) == BUF_ZV (b))
155 return NULL_INTERVAL;
156
157 searchpos = XINT (*begin);
158 }
159 else
160 {
161 int len = SCHARS (object);
162
163 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
164 && XINT (*end) <= len))
165 args_out_of_range (*begin, *end);
166 XSETFASTINT (*begin, XFASTINT (*begin));
167 if (begin != end)
168 XSETFASTINT (*end, XFASTINT (*end));
169 i = STRING_INTERVALS (object);
170
171 if (len == 0)
172 return NULL_INTERVAL;
173
174 searchpos = XINT (*begin);
175 }
176
177 if (NULL_INTERVAL_P (i))
178 return (force ? create_root_interval (object) : i);
179
180 return find_interval (i, searchpos);
181 }
182
183 /* Validate LIST as a property list. If LIST is not a list, then
184 make one consisting of (LIST nil). Otherwise, verify that LIST
185 is even numbered and thus suitable as a plist. */
186
187 static Lisp_Object
188 validate_plist (list)
189 Lisp_Object list;
190 {
191 if (NILP (list))
192 return Qnil;
193
194 if (CONSP (list))
195 {
196 register int i;
197 register Lisp_Object tail;
198 for (i = 0, tail = list; !NILP (tail); i++)
199 {
200 tail = Fcdr (tail);
201 QUIT;
202 }
203 if (i & 1)
204 error ("Odd length text property list");
205 return list;
206 }
207
208 return Fcons (list, Fcons (Qnil, Qnil));
209 }
210
211 /* Return nonzero if interval I has all the properties,
212 with the same values, of list PLIST. */
213
214 static int
215 interval_has_all_properties (plist, i)
216 Lisp_Object plist;
217 INTERVAL i;
218 {
219 register Lisp_Object tail1, tail2, sym1;
220 register int found;
221
222 /* Go through each element of PLIST. */
223 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
224 {
225 sym1 = Fcar (tail1);
226 found = 0;
227
228 /* Go through I's plist, looking for sym1 */
229 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
230 if (EQ (sym1, Fcar (tail2)))
231 {
232 /* Found the same property on both lists. If the
233 values are unequal, return zero. */
234 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
235 return 0;
236
237 /* Property has same value on both lists; go to next one. */
238 found = 1;
239 break;
240 }
241
242 if (! found)
243 return 0;
244 }
245
246 return 1;
247 }
248
249 /* Return nonzero if the plist of interval I has any of the
250 properties of PLIST, regardless of their values. */
251
252 static INLINE int
253 interval_has_some_properties (plist, i)
254 Lisp_Object plist;
255 INTERVAL i;
256 {
257 register Lisp_Object tail1, tail2, sym;
258
259 /* Go through each element of PLIST. */
260 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
261 {
262 sym = Fcar (tail1);
263
264 /* Go through i's plist, looking for tail1 */
265 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
266 if (EQ (sym, Fcar (tail2)))
267 return 1;
268 }
269
270 return 0;
271 }
272
273 /* Return nonzero if the plist of interval I has any of the
274 property names in LIST, regardless of their values. */
275
276 static INLINE int
277 interval_has_some_properties_list (list, i)
278 Lisp_Object list;
279 INTERVAL i;
280 {
281 register Lisp_Object tail1, tail2, sym;
282
283 /* Go through each element of LIST. */
284 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
285 {
286 sym = Fcar (tail1);
287
288 /* Go through i's plist, looking for tail1 */
289 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
290 if (EQ (sym, XCAR (tail2)))
291 return 1;
292 }
293
294 return 0;
295 }
296 \f
297 /* Changing the plists of individual intervals. */
298
299 /* Return the value of PROP in property-list PLIST, or Qunbound if it
300 has none. */
301 static Lisp_Object
302 property_value (plist, prop)
303 Lisp_Object plist, prop;
304 {
305 Lisp_Object value;
306
307 while (PLIST_ELT_P (plist, value))
308 if (EQ (XCAR (plist), prop))
309 return XCAR (value);
310 else
311 plist = XCDR (value);
312
313 return Qunbound;
314 }
315
316 /* Set the properties of INTERVAL to PROPERTIES,
317 and record undo info for the previous values.
318 OBJECT is the string or buffer that INTERVAL belongs to. */
319
320 static void
321 set_properties (properties, interval, object)
322 Lisp_Object properties, object;
323 INTERVAL interval;
324 {
325 Lisp_Object sym, value;
326
327 if (BUFFERP (object))
328 {
329 /* For each property in the old plist which is missing from PROPERTIES,
330 or has a different value in PROPERTIES, make an undo record. */
331 for (sym = interval->plist;
332 PLIST_ELT_P (sym, value);
333 sym = XCDR (value))
334 if (! EQ (property_value (properties, XCAR (sym)),
335 XCAR (value)))
336 {
337 record_property_change (interval->position, LENGTH (interval),
338 XCAR (sym), XCAR (value),
339 object);
340 }
341
342 /* For each new property that has no value at all in the old plist,
343 make an undo record binding it to nil, so it will be removed. */
344 for (sym = properties;
345 PLIST_ELT_P (sym, value);
346 sym = XCDR (value))
347 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
348 {
349 record_property_change (interval->position, LENGTH (interval),
350 XCAR (sym), Qnil,
351 object);
352 }
353 }
354
355 /* Store new properties. */
356 interval->plist = Fcopy_sequence (properties);
357 }
358
359 /* Add the properties of PLIST to the interval I, or set
360 the value of I's property to the value of the property on PLIST
361 if they are different.
362
363 OBJECT should be the string or buffer the interval is in.
364
365 Return nonzero if this changes I (i.e., if any members of PLIST
366 are actually added to I's plist) */
367
368 static int
369 add_properties (plist, i, object)
370 Lisp_Object plist;
371 INTERVAL i;
372 Lisp_Object object;
373 {
374 Lisp_Object tail1, tail2, sym1, val1;
375 register int changed = 0;
376 register int found;
377 struct gcpro gcpro1, gcpro2, gcpro3;
378
379 tail1 = plist;
380 sym1 = Qnil;
381 val1 = Qnil;
382 /* No need to protect OBJECT, because we can GC only in the case
383 where it is a buffer, and live buffers are always protected.
384 I and its plist are also protected, via OBJECT. */
385 GCPRO3 (tail1, sym1, val1);
386
387 /* Go through each element of PLIST. */
388 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
389 {
390 sym1 = Fcar (tail1);
391 val1 = Fcar (Fcdr (tail1));
392 found = 0;
393
394 /* Go through I's plist, looking for sym1 */
395 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
396 if (EQ (sym1, Fcar (tail2)))
397 {
398 /* No need to gcpro, because tail2 protects this
399 and it must be a cons cell (we get an error otherwise). */
400 register Lisp_Object this_cdr;
401
402 this_cdr = Fcdr (tail2);
403 /* Found the property. Now check its value. */
404 found = 1;
405
406 /* The properties have the same value on both lists.
407 Continue to the next property. */
408 if (EQ (val1, Fcar (this_cdr)))
409 break;
410
411 /* Record this change in the buffer, for undo purposes. */
412 if (BUFFERP (object))
413 {
414 record_property_change (i->position, LENGTH (i),
415 sym1, Fcar (this_cdr), object);
416 }
417
418 /* I's property has a different value -- change it */
419 Fsetcar (this_cdr, val1);
420 changed++;
421 break;
422 }
423
424 if (! found)
425 {
426 /* Record this change in the buffer, for undo purposes. */
427 if (BUFFERP (object))
428 {
429 record_property_change (i->position, LENGTH (i),
430 sym1, Qnil, object);
431 }
432 i->plist = Fcons (sym1, Fcons (val1, i->plist));
433 changed++;
434 }
435 }
436
437 UNGCPRO;
438
439 return changed;
440 }
441
442 /* For any members of PLIST, or LIST,
443 which are properties of I, remove them from I's plist.
444 (If PLIST is non-nil, use that, otherwise use LIST.)
445 OBJECT is the string or buffer containing I. */
446
447 static int
448 remove_properties (plist, list, i, object)
449 Lisp_Object plist, list;
450 INTERVAL i;
451 Lisp_Object object;
452 {
453 register Lisp_Object tail1, tail2, sym, current_plist;
454 register int changed = 0;
455
456 /* Nonzero means tail1 is a plist, otherwise it is a list. */
457 int use_plist;
458
459 current_plist = i->plist;
460
461 if (! NILP (plist))
462 tail1 = plist, use_plist = 1;
463 else
464 tail1 = list, use_plist = 0;
465
466 /* Go through each element of LIST or PLIST. */
467 while (CONSP (tail1))
468 {
469 sym = XCAR (tail1);
470
471 /* First, remove the symbol if it's at the head of the list */
472 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
473 {
474 if (BUFFERP (object))
475 record_property_change (i->position, LENGTH (i),
476 sym, XCAR (XCDR (current_plist)),
477 object);
478
479 current_plist = XCDR (XCDR (current_plist));
480 changed++;
481 }
482
483 /* Go through I's plist, looking for SYM. */
484 tail2 = current_plist;
485 while (! NILP (tail2))
486 {
487 register Lisp_Object this;
488 this = XCDR (XCDR (tail2));
489 if (CONSP (this) && EQ (sym, XCAR (this)))
490 {
491 if (BUFFERP (object))
492 record_property_change (i->position, LENGTH (i),
493 sym, XCAR (XCDR (this)), object);
494
495 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
496 changed++;
497 }
498 tail2 = this;
499 }
500
501 /* Advance thru TAIL1 one way or the other. */
502 tail1 = XCDR (tail1);
503 if (use_plist && CONSP (tail1))
504 tail1 = XCDR (tail1);
505 }
506
507 if (changed)
508 i->plist = current_plist;
509 return changed;
510 }
511
512 #if 0
513 /* Remove all properties from interval I. Return non-zero
514 if this changes the interval. */
515
516 static INLINE int
517 erase_properties (i)
518 INTERVAL i;
519 {
520 if (NILP (i->plist))
521 return 0;
522
523 i->plist = Qnil;
524 return 1;
525 }
526 #endif
527 \f
528 /* Returns the interval of POSITION in OBJECT.
529 POSITION is BEG-based. */
530
531 INTERVAL
532 interval_of (position, object)
533 int position;
534 Lisp_Object object;
535 {
536 register INTERVAL i;
537 int beg, end;
538
539 if (NILP (object))
540 XSETBUFFER (object, current_buffer);
541 else if (EQ (object, Qt))
542 return NULL_INTERVAL;
543
544 CHECK_STRING_OR_BUFFER (object);
545
546 if (BUFFERP (object))
547 {
548 register struct buffer *b = XBUFFER (object);
549
550 beg = BUF_BEGV (b);
551 end = BUF_ZV (b);
552 i = BUF_INTERVALS (b);
553 }
554 else
555 {
556 beg = 0;
557 end = SCHARS (object);
558 i = STRING_INTERVALS (object);
559 }
560
561 if (!(beg <= position && position <= end))
562 args_out_of_range (make_number (position), make_number (position));
563 if (beg == end || NULL_INTERVAL_P (i))
564 return NULL_INTERVAL;
565
566 return find_interval (i, position);
567 }
568 \f
569 DEFUN ("text-properties-at", Ftext_properties_at,
570 Stext_properties_at, 1, 2, 0,
571 doc: /* Return the list of properties of the character at POSITION in OBJECT.
572 OBJECT is the string or buffer to look for the properties in;
573 nil means the current buffer.
574 If POSITION is at the end of OBJECT, the value is nil. */)
575 (position, object)
576 Lisp_Object position, object;
577 {
578 register INTERVAL i;
579
580 if (NILP (object))
581 XSETBUFFER (object, current_buffer);
582
583 i = validate_interval_range (object, &position, &position, soft);
584 if (NULL_INTERVAL_P (i))
585 return Qnil;
586 /* If POSITION is at the end of the interval,
587 it means it's the end of OBJECT.
588 There are no properties at the very end,
589 since no character follows. */
590 if (XINT (position) == LENGTH (i) + i->position)
591 return Qnil;
592
593 return i->plist;
594 }
595
596 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
597 doc: /* Return the value of POSITION's property PROP, in OBJECT.
598 OBJECT is optional and defaults to the current buffer.
599 If POSITION is at the end of OBJECT, the value is nil. */)
600 (position, prop, object)
601 Lisp_Object position, object;
602 Lisp_Object prop;
603 {
604 return textget (Ftext_properties_at (position, object), prop);
605 }
606
607 /* Return the value of POSITION's property PROP, in OBJECT.
608 OBJECT is optional and defaults to the current buffer.
609 If OVERLAY is non-0, then in the case that the returned property is from
610 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
611 returned in *OVERLAY.
612 If POSITION is at the end of OBJECT, the value is nil.
613 If OBJECT is a buffer, then overlay properties are considered as well as
614 text properties.
615 If OBJECT is a window, then that window's buffer is used, but
616 window-specific overlays are considered only if they are associated
617 with OBJECT. */
618 Lisp_Object
619 get_char_property_and_overlay (position, prop, object, overlay)
620 Lisp_Object position, object;
621 register Lisp_Object prop;
622 Lisp_Object *overlay;
623 {
624 struct window *w = 0;
625
626 CHECK_NUMBER_COERCE_MARKER (position);
627
628 if (NILP (object))
629 XSETBUFFER (object, current_buffer);
630
631 if (WINDOWP (object))
632 {
633 w = XWINDOW (object);
634 object = w->buffer;
635 }
636 if (BUFFERP (object))
637 {
638 int posn = XINT (position);
639 int noverlays;
640 Lisp_Object *overlay_vec, tem;
641 int next_overlay;
642 int len;
643 struct buffer *obuf = current_buffer;
644
645 set_buffer_temp (XBUFFER (object));
646
647 /* First try with room for 40 overlays. */
648 len = 40;
649 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
650
651 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
652 &next_overlay, NULL, 0);
653
654 /* If there are more than 40,
655 make enough space for all, and try again. */
656 if (noverlays > len)
657 {
658 len = noverlays;
659 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
660 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
661 &next_overlay, NULL, 0);
662 }
663 noverlays = sort_overlays (overlay_vec, noverlays, w);
664
665 set_buffer_temp (obuf);
666
667 /* Now check the overlays in order of decreasing priority. */
668 while (--noverlays >= 0)
669 {
670 tem = Foverlay_get (overlay_vec[noverlays], prop);
671 if (!NILP (tem))
672 {
673 if (overlay)
674 /* Return the overlay we got the property from. */
675 *overlay = overlay_vec[noverlays];
676 return tem;
677 }
678 }
679 }
680
681 if (overlay)
682 /* Indicate that the return value is not from an overlay. */
683 *overlay = Qnil;
684
685 /* Not a buffer, or no appropriate overlay, so fall through to the
686 simpler case. */
687 return Fget_text_property (position, prop, object);
688 }
689
690 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
691 doc: /* Return the value of POSITION's property PROP, in OBJECT.
692 Both overlay properties and text properties are checked.
693 OBJECT is optional and defaults to the current buffer.
694 If POSITION is at the end of OBJECT, the value is nil.
695 If OBJECT is a buffer, then overlay properties are considered as well as
696 text properties.
697 If OBJECT is a window, then that window's buffer is used, but window-specific
698 overlays are considered only if they are associated with OBJECT. */)
699 (position, prop, object)
700 Lisp_Object position, object;
701 register Lisp_Object prop;
702 {
703 return get_char_property_and_overlay (position, prop, object, 0);
704 }
705 \f
706 DEFUN ("next-char-property-change", Fnext_char_property_change,
707 Snext_char_property_change, 1, 2, 0,
708 doc: /* Return the position of next text property or overlay change.
709 This scans characters forward from POSITION till it finds a change in
710 some text property, or the beginning or end of an overlay, and returns
711 the position of that.
712 If none is found, the function returns (point-max).
713
714 If the optional third argument LIMIT is non-nil, don't search
715 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
716 (position, limit)
717 Lisp_Object position, limit;
718 {
719 Lisp_Object temp;
720
721 temp = Fnext_overlay_change (position);
722 if (! NILP (limit))
723 {
724 CHECK_NUMBER (limit);
725 if (XINT (limit) < XINT (temp))
726 temp = limit;
727 }
728 return Fnext_property_change (position, Qnil, temp);
729 }
730
731 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
732 Sprevious_char_property_change, 1, 2, 0,
733 doc: /* Return the position of previous text property or overlay change.
734 Scans characters backward from POSITION till it finds a change in some
735 text property, or the beginning or end of an overlay, and returns the
736 position of that.
737 If none is found, the function returns (point-max).
738
739 If the optional third argument LIMIT is non-nil, don't search
740 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
741 (position, limit)
742 Lisp_Object position, limit;
743 {
744 Lisp_Object temp;
745
746 temp = Fprevious_overlay_change (position);
747 if (! NILP (limit))
748 {
749 CHECK_NUMBER (limit);
750 if (XINT (limit) > XINT (temp))
751 temp = limit;
752 }
753 return Fprevious_property_change (position, Qnil, temp);
754 }
755
756
757 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
758 Snext_single_char_property_change, 2, 4, 0,
759 doc: /* Return the position of next text property or overlay change for a specific property.
760 Scans characters forward from POSITION till it finds
761 a change in the PROP property, then returns the position of the change.
762 The optional third argument OBJECT is the string or buffer to scan.
763 The property values are compared with `eq'.
764 If the property is constant all the way to the end of OBJECT, return the
765 last valid position in OBJECT.
766 If the optional fourth argument LIMIT is non-nil, don't search
767 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
768 (position, prop, object, limit)
769 Lisp_Object prop, position, object, limit;
770 {
771 if (STRINGP (object))
772 {
773 position = Fnext_single_property_change (position, prop, object, limit);
774 if (NILP (position))
775 {
776 if (NILP (limit))
777 position = make_number (SCHARS (object));
778 else
779 position = limit;
780 }
781 }
782 else
783 {
784 Lisp_Object initial_value, value;
785 int count = SPECPDL_INDEX ();
786
787 if (! NILP (object))
788 CHECK_BUFFER (object);
789
790 if (BUFFERP (object) && current_buffer != XBUFFER (object))
791 {
792 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
793 Fset_buffer (object);
794 }
795
796 initial_value = Fget_char_property (position, prop, object);
797
798 if (NILP (limit))
799 XSETFASTINT (limit, BUF_ZV (current_buffer));
800 else
801 CHECK_NUMBER_COERCE_MARKER (limit);
802
803 for (;;)
804 {
805 position = Fnext_char_property_change (position, limit);
806 if (XFASTINT (position) >= XFASTINT (limit)) {
807 position = limit;
808 break;
809 }
810
811 value = Fget_char_property (position, prop, object);
812 if (!EQ (value, initial_value))
813 break;
814 }
815
816 unbind_to (count, Qnil);
817 }
818
819 return position;
820 }
821
822 DEFUN ("previous-single-char-property-change",
823 Fprevious_single_char_property_change,
824 Sprevious_single_char_property_change, 2, 4, 0,
825 doc: /* Return the position of previous text property or overlay change for a specific property.
826 Scans characters backward from POSITION till it finds
827 a change in the PROP property, then returns the position of the change.
828 The optional third argument OBJECT is the string or buffer to scan.
829 The property values are compared with `eq'.
830 If the property is constant all the way to the start of OBJECT, return the
831 first valid position in OBJECT.
832 If the optional fourth argument LIMIT is non-nil, don't search
833 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
834 (position, prop, object, limit)
835 Lisp_Object prop, position, object, limit;
836 {
837 if (STRINGP (object))
838 {
839 position = Fprevious_single_property_change (position, prop, object, limit);
840 if (NILP (position))
841 {
842 if (NILP (limit))
843 position = make_number (SCHARS (object));
844 else
845 position = limit;
846 }
847 }
848 else
849 {
850 int count = SPECPDL_INDEX ();
851
852 if (! NILP (object))
853 CHECK_BUFFER (object);
854
855 if (BUFFERP (object) && current_buffer != XBUFFER (object))
856 {
857 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
858 Fset_buffer (object);
859 }
860
861 if (NILP (limit))
862 XSETFASTINT (limit, BUF_BEGV (current_buffer));
863 else
864 CHECK_NUMBER_COERCE_MARKER (limit);
865
866 if (XFASTINT (position) <= XFASTINT (limit))
867 position = limit;
868 else
869 {
870 Lisp_Object initial_value =
871 Fget_char_property (make_number (XFASTINT (position) - 1),
872 prop, object);
873
874 for (;;)
875 {
876 position = Fprevious_char_property_change (position, limit);
877
878 if (XFASTINT (position) <= XFASTINT (limit))
879 {
880 position = limit;
881 break;
882 }
883 else
884 {
885 Lisp_Object value =
886 Fget_char_property (make_number (XFASTINT (position) - 1),
887 prop, object);
888
889 if (!EQ (value, initial_value))
890 break;
891 }
892 }
893 }
894
895 unbind_to (count, Qnil);
896 }
897
898 return position;
899 }
900 \f
901 DEFUN ("next-property-change", Fnext_property_change,
902 Snext_property_change, 1, 3, 0,
903 doc: /* Return the position of next property change.
904 Scans characters forward from POSITION in OBJECT till it finds
905 a change in some text property, then returns the position of the change.
906 The optional second argument OBJECT is the string or buffer to scan.
907 Return nil if the property is constant all the way to the end of OBJECT.
908 If the value is non-nil, it is a position greater than POSITION, never equal.
909
910 If the optional third argument LIMIT is non-nil, don't search
911 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
912 (position, object, limit)
913 Lisp_Object position, object, limit;
914 {
915 register INTERVAL i, next;
916
917 if (NILP (object))
918 XSETBUFFER (object, current_buffer);
919
920 if (!NILP (limit) && !EQ (limit, Qt))
921 CHECK_NUMBER_COERCE_MARKER (limit);
922
923 i = validate_interval_range (object, &position, &position, soft);
924
925 /* If LIMIT is t, return start of next interval--don't
926 bother checking further intervals. */
927 if (EQ (limit, Qt))
928 {
929 if (NULL_INTERVAL_P (i))
930 next = i;
931 else
932 next = next_interval (i);
933
934 if (NULL_INTERVAL_P (next))
935 XSETFASTINT (position, (STRINGP (object)
936 ? SCHARS (object)
937 : BUF_ZV (XBUFFER (object))));
938 else
939 XSETFASTINT (position, next->position);
940 return position;
941 }
942
943 if (NULL_INTERVAL_P (i))
944 return limit;
945
946 next = next_interval (i);
947
948 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
949 && (NILP (limit) || next->position < XFASTINT (limit)))
950 next = next_interval (next);
951
952 if (NULL_INTERVAL_P (next))
953 return limit;
954 if (NILP (limit))
955 XSETFASTINT (limit, (STRINGP (object)
956 ? SCHARS (object)
957 : BUF_ZV (XBUFFER (object))));
958 if (!(next->position < XFASTINT (limit)))
959 return limit;
960
961 XSETFASTINT (position, next->position);
962 return position;
963 }
964
965 /* Return 1 if there's a change in some property between BEG and END. */
966
967 int
968 property_change_between_p (beg, end)
969 int beg, end;
970 {
971 register INTERVAL i, next;
972 Lisp_Object object, pos;
973
974 XSETBUFFER (object, current_buffer);
975 XSETFASTINT (pos, beg);
976
977 i = validate_interval_range (object, &pos, &pos, soft);
978 if (NULL_INTERVAL_P (i))
979 return 0;
980
981 next = next_interval (i);
982 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
983 {
984 next = next_interval (next);
985 if (NULL_INTERVAL_P (next))
986 return 0;
987 if (next->position >= end)
988 return 0;
989 }
990
991 if (NULL_INTERVAL_P (next))
992 return 0;
993
994 return 1;
995 }
996
997 DEFUN ("next-single-property-change", Fnext_single_property_change,
998 Snext_single_property_change, 2, 4, 0,
999 doc: /* Return the position of next property change for a specific property.
1000 Scans characters forward from POSITION till it finds
1001 a change in the PROP property, then returns the position of the change.
1002 The optional third argument OBJECT is the string or buffer to scan.
1003 The property values are compared with `eq'.
1004 Return nil if the property is constant all the way to the end of OBJECT.
1005 If the value is non-nil, it is a position greater than POSITION, never equal.
1006
1007 If the optional fourth argument LIMIT is non-nil, don't search
1008 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1009 (position, prop, object, limit)
1010 Lisp_Object position, prop, object, limit;
1011 {
1012 register INTERVAL i, next;
1013 register Lisp_Object here_val;
1014
1015 if (NILP (object))
1016 XSETBUFFER (object, current_buffer);
1017
1018 if (!NILP (limit))
1019 CHECK_NUMBER_COERCE_MARKER (limit);
1020
1021 i = validate_interval_range (object, &position, &position, soft);
1022 if (NULL_INTERVAL_P (i))
1023 return limit;
1024
1025 here_val = textget (i->plist, prop);
1026 next = next_interval (i);
1027 while (! NULL_INTERVAL_P (next)
1028 && EQ (here_val, textget (next->plist, prop))
1029 && (NILP (limit) || next->position < XFASTINT (limit)))
1030 next = next_interval (next);
1031
1032 if (NULL_INTERVAL_P (next))
1033 return limit;
1034 if (NILP (limit))
1035 XSETFASTINT (limit, (STRINGP (object)
1036 ? SCHARS (object)
1037 : BUF_ZV (XBUFFER (object))));
1038 if (!(next->position < XFASTINT (limit)))
1039 return limit;
1040
1041 return make_number (next->position);
1042 }
1043
1044 DEFUN ("previous-property-change", Fprevious_property_change,
1045 Sprevious_property_change, 1, 3, 0,
1046 doc: /* Return the position of previous property change.
1047 Scans characters backwards from POSITION in OBJECT till it finds
1048 a change in some text property, then returns the position of the change.
1049 The optional second argument OBJECT is the string or buffer to scan.
1050 Return nil if the property is constant all the way to the start of OBJECT.
1051 If the value is non-nil, it is a position less than POSITION, never equal.
1052
1053 If the optional third argument LIMIT is non-nil, don't search
1054 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1055 (position, object, limit)
1056 Lisp_Object position, object, limit;
1057 {
1058 register INTERVAL i, previous;
1059
1060 if (NILP (object))
1061 XSETBUFFER (object, current_buffer);
1062
1063 if (!NILP (limit))
1064 CHECK_NUMBER_COERCE_MARKER (limit);
1065
1066 i = validate_interval_range (object, &position, &position, soft);
1067 if (NULL_INTERVAL_P (i))
1068 return limit;
1069
1070 /* Start with the interval containing the char before point. */
1071 if (i->position == XFASTINT (position))
1072 i = previous_interval (i);
1073
1074 previous = previous_interval (i);
1075 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1076 && (NILP (limit)
1077 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1078 previous = previous_interval (previous);
1079 if (NULL_INTERVAL_P (previous))
1080 return limit;
1081 if (NILP (limit))
1082 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1083 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1084 return limit;
1085
1086 return make_number (previous->position + LENGTH (previous));
1087 }
1088
1089 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1090 Sprevious_single_property_change, 2, 4, 0,
1091 doc: /* Return the position of previous property change for a specific property.
1092 Scans characters backward from POSITION till it finds
1093 a change in the PROP property, then returns the position of the change.
1094 The optional third argument OBJECT is the string or buffer to scan.
1095 The property values are compared with `eq'.
1096 Return nil if the property is constant all the way to the start of OBJECT.
1097 If the value is non-nil, it is a position less than POSITION, never equal.
1098
1099 If the optional fourth argument LIMIT is non-nil, don't search
1100 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1101 (position, prop, object, limit)
1102 Lisp_Object position, prop, object, limit;
1103 {
1104 register INTERVAL i, previous;
1105 register Lisp_Object here_val;
1106
1107 if (NILP (object))
1108 XSETBUFFER (object, current_buffer);
1109
1110 if (!NILP (limit))
1111 CHECK_NUMBER_COERCE_MARKER (limit);
1112
1113 i = validate_interval_range (object, &position, &position, soft);
1114
1115 /* Start with the interval containing the char before point. */
1116 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1117 i = previous_interval (i);
1118
1119 if (NULL_INTERVAL_P (i))
1120 return limit;
1121
1122 here_val = textget (i->plist, prop);
1123 previous = previous_interval (i);
1124 while (!NULL_INTERVAL_P (previous)
1125 && EQ (here_val, textget (previous->plist, prop))
1126 && (NILP (limit)
1127 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1128 previous = previous_interval (previous);
1129 if (NULL_INTERVAL_P (previous))
1130 return limit;
1131 if (NILP (limit))
1132 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1133 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1134 return limit;
1135
1136 return make_number (previous->position + LENGTH (previous));
1137 }
1138 \f
1139 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1140
1141 DEFUN ("add-text-properties", Fadd_text_properties,
1142 Sadd_text_properties, 3, 4, 0,
1143 doc: /* Add properties to the text from START to END.
1144 The third argument PROPERTIES is a property list
1145 specifying the property values to add.
1146 The optional fourth argument, OBJECT,
1147 is the string or buffer containing the text.
1148 Return t if any property value actually changed, nil otherwise. */)
1149 (start, end, properties, object)
1150 Lisp_Object start, end, properties, object;
1151 {
1152 register INTERVAL i, unchanged;
1153 register int s, len, modified = 0;
1154 struct gcpro gcpro1;
1155
1156 properties = validate_plist (properties);
1157 if (NILP (properties))
1158 return Qnil;
1159
1160 if (NILP (object))
1161 XSETBUFFER (object, current_buffer);
1162
1163 i = validate_interval_range (object, &start, &end, hard);
1164 if (NULL_INTERVAL_P (i))
1165 return Qnil;
1166
1167 s = XINT (start);
1168 len = XINT (end) - s;
1169
1170 /* No need to protect OBJECT, because we GC only if it's a buffer,
1171 and live buffers are always protected. */
1172 GCPRO1 (properties);
1173
1174 /* If we're not starting on an interval boundary, we have to
1175 split this interval. */
1176 if (i->position != s)
1177 {
1178 /* If this interval already has the properties, we can
1179 skip it. */
1180 if (interval_has_all_properties (properties, i))
1181 {
1182 int got = (LENGTH (i) - (s - i->position));
1183 if (got >= len)
1184 RETURN_UNGCPRO (Qnil);
1185 len -= got;
1186 i = next_interval (i);
1187 }
1188 else
1189 {
1190 unchanged = i;
1191 i = split_interval_right (unchanged, s - unchanged->position);
1192 copy_properties (unchanged, i);
1193 }
1194 }
1195
1196 if (BUFFERP (object))
1197 modify_region (XBUFFER (object), XINT (start), XINT (end));
1198
1199 /* We are at the beginning of interval I, with LEN chars to scan. */
1200 for (;;)
1201 {
1202 if (i == 0)
1203 abort ();
1204
1205 if (LENGTH (i) >= len)
1206 {
1207 /* We can UNGCPRO safely here, because there will be just
1208 one more chance to gc, in the next call to add_properties,
1209 and after that we will not need PROPERTIES or OBJECT again. */
1210 UNGCPRO;
1211
1212 if (interval_has_all_properties (properties, i))
1213 {
1214 if (BUFFERP (object))
1215 signal_after_change (XINT (start), XINT (end) - XINT (start),
1216 XINT (end) - XINT (start));
1217
1218 return modified ? Qt : Qnil;
1219 }
1220
1221 if (LENGTH (i) == len)
1222 {
1223 add_properties (properties, i, object);
1224 if (BUFFERP (object))
1225 signal_after_change (XINT (start), XINT (end) - XINT (start),
1226 XINT (end) - XINT (start));
1227 return Qt;
1228 }
1229
1230 /* i doesn't have the properties, and goes past the change limit */
1231 unchanged = i;
1232 i = split_interval_left (unchanged, len);
1233 copy_properties (unchanged, i);
1234 add_properties (properties, i, object);
1235 if (BUFFERP (object))
1236 signal_after_change (XINT (start), XINT (end) - XINT (start),
1237 XINT (end) - XINT (start));
1238 return Qt;
1239 }
1240
1241 len -= LENGTH (i);
1242 modified += add_properties (properties, i, object);
1243 i = next_interval (i);
1244 }
1245 }
1246
1247 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1248
1249 DEFUN ("put-text-property", Fput_text_property,
1250 Sput_text_property, 4, 5, 0,
1251 doc: /* Set one property of the text from START to END.
1252 The third and fourth arguments PROPERTY and VALUE
1253 specify the property to add.
1254 The optional fifth argument, OBJECT,
1255 is the string or buffer containing the text. */)
1256 (start, end, property, value, object)
1257 Lisp_Object start, end, property, value, object;
1258 {
1259 Fadd_text_properties (start, end,
1260 Fcons (property, Fcons (value, Qnil)),
1261 object);
1262 return Qnil;
1263 }
1264
1265 DEFUN ("set-text-properties", Fset_text_properties,
1266 Sset_text_properties, 3, 4, 0,
1267 doc: /* Completely replace properties of text from START to END.
1268 The third argument PROPERTIES is the new property list.
1269 The optional fourth argument, OBJECT,
1270 is the string or buffer containing the text.
1271 If OBJECT is omitted or nil, it defaults to the current buffer.
1272 If PROPERTIES is nil, the effect is to remove all properties from
1273 the designated part of OBJECT. */)
1274 (start, end, properties, object)
1275 Lisp_Object start, end, properties, object;
1276 {
1277 return set_text_properties (start, end, properties, object, Qt);
1278 }
1279
1280
1281 /* Replace properties of text from START to END with new list of
1282 properties PROPERTIES. OBJECT is the buffer or string containing
1283 the text. OBJECT nil means use the current buffer.
1284 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1285 is non-nil if properties were replaced; it is nil if there weren't
1286 any properties to replace. */
1287
1288 Lisp_Object
1289 set_text_properties (start, end, properties, object, signal_after_change_p)
1290 Lisp_Object start, end, properties, object, signal_after_change_p;
1291 {
1292 register INTERVAL i;
1293 Lisp_Object ostart, oend;
1294
1295 ostart = start;
1296 oend = end;
1297
1298 properties = validate_plist (properties);
1299
1300 if (NILP (object))
1301 XSETBUFFER (object, current_buffer);
1302
1303 /* If we want no properties for a whole string,
1304 get rid of its intervals. */
1305 if (NILP (properties) && STRINGP (object)
1306 && XFASTINT (start) == 0
1307 && XFASTINT (end) == SCHARS (object))
1308 {
1309 if (! STRING_INTERVALS (object))
1310 return Qt;
1311
1312 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1313 return Qt;
1314 }
1315
1316 i = validate_interval_range (object, &start, &end, soft);
1317
1318 if (NULL_INTERVAL_P (i))
1319 {
1320 /* If buffer has no properties, and we want none, return now. */
1321 if (NILP (properties))
1322 return Qnil;
1323
1324 /* Restore the original START and END values
1325 because validate_interval_range increments them for strings. */
1326 start = ostart;
1327 end = oend;
1328
1329 i = validate_interval_range (object, &start, &end, hard);
1330 /* This can return if start == end. */
1331 if (NULL_INTERVAL_P (i))
1332 return Qnil;
1333 }
1334
1335 if (BUFFERP (object))
1336 modify_region (XBUFFER (object), XINT (start), XINT (end));
1337
1338 set_text_properties_1 (start, end, properties, object, i);
1339
1340 if (BUFFERP (object) && !NILP (signal_after_change_p))
1341 signal_after_change (XINT (start), XINT (end) - XINT (start),
1342 XINT (end) - XINT (start));
1343 return Qt;
1344 }
1345
1346 /* Replace properties of text from START to END with new list of
1347 properties PROPERTIES. BUFFER is the buffer containing
1348 the text. This does not obey any hooks.
1349 You can provide the interval that START is located in as I,
1350 or pass NULL for I and this function will find it.
1351 START and END can be in any order. */
1352
1353 void
1354 set_text_properties_1 (start, end, properties, buffer, i)
1355 Lisp_Object start, end, properties, buffer;
1356 INTERVAL i;
1357 {
1358 register INTERVAL prev_changed = NULL_INTERVAL;
1359 register int s, len;
1360 INTERVAL unchanged;
1361
1362 s = XINT (start);
1363 len = XINT (end) - s;
1364 if (len == 0)
1365 return;
1366 if (len < 0)
1367 {
1368 s = s + len;
1369 len = - len;
1370 }
1371
1372 if (i == 0)
1373 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1374
1375 if (i->position != s)
1376 {
1377 unchanged = i;
1378 i = split_interval_right (unchanged, s - unchanged->position);
1379
1380 if (LENGTH (i) > len)
1381 {
1382 copy_properties (unchanged, i);
1383 i = split_interval_left (i, len);
1384 set_properties (properties, i, buffer);
1385 return;
1386 }
1387
1388 set_properties (properties, i, buffer);
1389
1390 if (LENGTH (i) == len)
1391 return;
1392
1393 prev_changed = i;
1394 len -= LENGTH (i);
1395 i = next_interval (i);
1396 }
1397
1398 /* We are starting at the beginning of an interval, I */
1399 while (len > 0)
1400 {
1401 if (i == 0)
1402 abort ();
1403
1404 if (LENGTH (i) >= len)
1405 {
1406 if (LENGTH (i) > len)
1407 i = split_interval_left (i, len);
1408
1409 /* We have to call set_properties even if we are going to
1410 merge the intervals, so as to make the undo records
1411 and cause redisplay to happen. */
1412 set_properties (properties, i, buffer);
1413 if (!NULL_INTERVAL_P (prev_changed))
1414 merge_interval_left (i);
1415 return;
1416 }
1417
1418 len -= LENGTH (i);
1419
1420 /* We have to call set_properties even if we are going to
1421 merge the intervals, so as to make the undo records
1422 and cause redisplay to happen. */
1423 set_properties (properties, i, buffer);
1424 if (NULL_INTERVAL_P (prev_changed))
1425 prev_changed = i;
1426 else
1427 prev_changed = i = merge_interval_left (i);
1428
1429 i = next_interval (i);
1430 }
1431 }
1432
1433 DEFUN ("remove-text-properties", Fremove_text_properties,
1434 Sremove_text_properties, 3, 4, 0,
1435 doc: /* Remove some properties from text from START to END.
1436 The third argument PROPERTIES is a property list
1437 whose property names specify the properties to remove.
1438 \(The values stored in PROPERTIES are ignored.)
1439 The optional fourth argument, OBJECT,
1440 is the string or buffer containing the text.
1441 Return t if any property was actually removed, nil otherwise. */)
1442 (start, end, properties, object)
1443 Lisp_Object start, end, properties, object;
1444 {
1445 register INTERVAL i, unchanged;
1446 register int s, len, modified = 0;
1447
1448 if (NILP (object))
1449 XSETBUFFER (object, current_buffer);
1450
1451 i = validate_interval_range (object, &start, &end, soft);
1452 if (NULL_INTERVAL_P (i))
1453 return Qnil;
1454
1455 s = XINT (start);
1456 len = XINT (end) - s;
1457
1458 if (i->position != s)
1459 {
1460 /* No properties on this first interval -- return if
1461 it covers the entire region. */
1462 if (! interval_has_some_properties (properties, i))
1463 {
1464 int got = (LENGTH (i) - (s - i->position));
1465 if (got >= len)
1466 return Qnil;
1467 len -= got;
1468 i = next_interval (i);
1469 }
1470 /* Split away the beginning of this interval; what we don't
1471 want to modify. */
1472 else
1473 {
1474 unchanged = i;
1475 i = split_interval_right (unchanged, s - unchanged->position);
1476 copy_properties (unchanged, i);
1477 }
1478 }
1479
1480 if (BUFFERP (object))
1481 modify_region (XBUFFER (object), XINT (start), XINT (end));
1482
1483 /* We are at the beginning of an interval, with len to scan */
1484 for (;;)
1485 {
1486 if (i == 0)
1487 abort ();
1488
1489 if (LENGTH (i) >= len)
1490 {
1491 if (! interval_has_some_properties (properties, i))
1492 return modified ? Qt : Qnil;
1493
1494 if (LENGTH (i) == len)
1495 {
1496 remove_properties (properties, Qnil, i, object);
1497 if (BUFFERP (object))
1498 signal_after_change (XINT (start), XINT (end) - XINT (start),
1499 XINT (end) - XINT (start));
1500 return Qt;
1501 }
1502
1503 /* i has the properties, and goes past the change limit */
1504 unchanged = i;
1505 i = split_interval_left (i, len);
1506 copy_properties (unchanged, i);
1507 remove_properties (properties, Qnil, i, object);
1508 if (BUFFERP (object))
1509 signal_after_change (XINT (start), XINT (end) - XINT (start),
1510 XINT (end) - XINT (start));
1511 return Qt;
1512 }
1513
1514 len -= LENGTH (i);
1515 modified += remove_properties (properties, Qnil, i, object);
1516 i = next_interval (i);
1517 }
1518 }
1519
1520 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1521 Sremove_list_of_text_properties, 3, 4, 0,
1522 doc: /* Remove some properties from text from START to END.
1523 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1524 The optional fourth argument, OBJECT,
1525 is the string or buffer containing the text, defaulting to the current buffer.
1526 Return t if any property was actually removed, nil otherwise. */)
1527 (start, end, list_of_properties, object)
1528 Lisp_Object start, end, list_of_properties, object;
1529 {
1530 register INTERVAL i, unchanged;
1531 register int s, len, modified = 0;
1532 Lisp_Object properties;
1533 properties = list_of_properties;
1534
1535 if (NILP (object))
1536 XSETBUFFER (object, current_buffer);
1537
1538 i = validate_interval_range (object, &start, &end, soft);
1539 if (NULL_INTERVAL_P (i))
1540 return Qnil;
1541
1542 s = XINT (start);
1543 len = XINT (end) - s;
1544
1545 if (i->position != s)
1546 {
1547 /* No properties on this first interval -- return if
1548 it covers the entire region. */
1549 if (! interval_has_some_properties_list (properties, i))
1550 {
1551 int got = (LENGTH (i) - (s - i->position));
1552 if (got >= len)
1553 return Qnil;
1554 len -= got;
1555 i = next_interval (i);
1556 }
1557 /* Split away the beginning of this interval; what we don't
1558 want to modify. */
1559 else
1560 {
1561 unchanged = i;
1562 i = split_interval_right (unchanged, s - unchanged->position);
1563 copy_properties (unchanged, i);
1564 }
1565 }
1566
1567 if (BUFFERP (object))
1568 modify_region (XBUFFER (object), XINT (start), XINT (end));
1569
1570 /* We are at the beginning of an interval, with len to scan */
1571 for (;;)
1572 {
1573 if (i == 0)
1574 abort ();
1575
1576 if (LENGTH (i) >= len)
1577 {
1578 if (! interval_has_some_properties_list (properties, i))
1579 return modified ? Qt : Qnil;
1580
1581 if (LENGTH (i) == len)
1582 {
1583 remove_properties (Qnil, properties, i, object);
1584 if (BUFFERP (object))
1585 signal_after_change (XINT (start), XINT (end) - XINT (start),
1586 XINT (end) - XINT (start));
1587 return Qt;
1588 }
1589
1590 /* i has the properties, and goes past the change limit */
1591 unchanged = i;
1592 i = split_interval_left (i, len);
1593 copy_properties (unchanged, i);
1594 remove_properties (Qnil, properties, i, object);
1595 if (BUFFERP (object))
1596 signal_after_change (XINT (start), XINT (end) - XINT (start),
1597 XINT (end) - XINT (start));
1598 return Qt;
1599 }
1600
1601 len -= LENGTH (i);
1602 modified += remove_properties (Qnil, properties, i, object);
1603 i = next_interval (i);
1604 }
1605 }
1606 \f
1607 DEFUN ("text-property-any", Ftext_property_any,
1608 Stext_property_any, 4, 5, 0,
1609 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1610 If so, return the position of the first character whose property PROPERTY
1611 is `eq' to VALUE. Otherwise return nil.
1612 The optional fifth argument, OBJECT, is the string or buffer
1613 containing the text. */)
1614 (start, end, property, value, object)
1615 Lisp_Object start, end, property, value, object;
1616 {
1617 register INTERVAL i;
1618 register int e, pos;
1619
1620 if (NILP (object))
1621 XSETBUFFER (object, current_buffer);
1622 i = validate_interval_range (object, &start, &end, soft);
1623 if (NULL_INTERVAL_P (i))
1624 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1625 e = XINT (end);
1626
1627 while (! NULL_INTERVAL_P (i))
1628 {
1629 if (i->position >= e)
1630 break;
1631 if (EQ (textget (i->plist, property), value))
1632 {
1633 pos = i->position;
1634 if (pos < XINT (start))
1635 pos = XINT (start);
1636 return make_number (pos);
1637 }
1638 i = next_interval (i);
1639 }
1640 return Qnil;
1641 }
1642
1643 DEFUN ("text-property-not-all", Ftext_property_not_all,
1644 Stext_property_not_all, 4, 5, 0,
1645 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1646 If so, return the position of the first character whose property PROPERTY
1647 is not `eq' to VALUE. Otherwise, return nil.
1648 The optional fifth argument, OBJECT, is the string or buffer
1649 containing the text. */)
1650 (start, end, property, value, object)
1651 Lisp_Object start, end, property, value, object;
1652 {
1653 register INTERVAL i;
1654 register int s, e;
1655
1656 if (NILP (object))
1657 XSETBUFFER (object, current_buffer);
1658 i = validate_interval_range (object, &start, &end, soft);
1659 if (NULL_INTERVAL_P (i))
1660 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1661 s = XINT (start);
1662 e = XINT (end);
1663
1664 while (! NULL_INTERVAL_P (i))
1665 {
1666 if (i->position >= e)
1667 break;
1668 if (! EQ (textget (i->plist, property), value))
1669 {
1670 if (i->position > s)
1671 s = i->position;
1672 return make_number (s);
1673 }
1674 i = next_interval (i);
1675 }
1676 return Qnil;
1677 }
1678
1679 \f
1680 /* Return the direction from which the text-property PROP would be
1681 inherited by any new text inserted at POS: 1 if it would be
1682 inherited from the char after POS, -1 if it would be inherited from
1683 the char before POS, and 0 if from neither. */
1684
1685 int
1686 text_property_stickiness (prop, pos)
1687 Lisp_Object prop;
1688 Lisp_Object pos;
1689 {
1690 Lisp_Object prev_pos, front_sticky;
1691 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1692
1693 if (XINT (pos) > BEGV)
1694 /* Consider previous character. */
1695 {
1696 Lisp_Object rear_non_sticky;
1697
1698 prev_pos = make_number (XINT (pos) - 1);
1699 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1700
1701 if (!NILP (CONSP (rear_non_sticky)
1702 ? Fmemq (prop, rear_non_sticky)
1703 : rear_non_sticky))
1704 /* PROP is rear-non-sticky. */
1705 is_rear_sticky = 0;
1706 }
1707
1708 /* Consider following character. */
1709 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1710
1711 if (EQ (front_sticky, Qt)
1712 || (CONSP (front_sticky)
1713 && !NILP (Fmemq (prop, front_sticky))))
1714 /* PROP is inherited from after. */
1715 is_front_sticky = 1;
1716
1717 /* Simple cases, where the properties are consistent. */
1718 if (is_rear_sticky && !is_front_sticky)
1719 return -1;
1720 else if (!is_rear_sticky && is_front_sticky)
1721 return 1;
1722 else if (!is_rear_sticky && !is_front_sticky)
1723 return 0;
1724
1725 /* The stickiness properties are inconsistent, so we have to
1726 disambiguate. Basically, rear-sticky wins, _except_ if the
1727 property that would be inherited has a value of nil, in which case
1728 front-sticky wins. */
1729 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1730 return 1;
1731 else
1732 return -1;
1733 }
1734
1735 \f
1736 /* I don't think this is the right interface to export; how often do you
1737 want to do something like this, other than when you're copying objects
1738 around?
1739
1740 I think it would be better to have a pair of functions, one which
1741 returns the text properties of a region as a list of ranges and
1742 plists, and another which applies such a list to another object. */
1743
1744 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1745 SRC and DEST may each refer to strings or buffers.
1746 Optional sixth argument PROP causes only that property to be copied.
1747 Properties are copied to DEST as if by `add-text-properties'.
1748 Return t if any property value actually changed, nil otherwise. */
1749
1750 /* Note this can GC when DEST is a buffer. */
1751
1752 Lisp_Object
1753 copy_text_properties (start, end, src, pos, dest, prop)
1754 Lisp_Object start, end, src, pos, dest, prop;
1755 {
1756 INTERVAL i;
1757 Lisp_Object res;
1758 Lisp_Object stuff;
1759 Lisp_Object plist;
1760 int s, e, e2, p, len, modified = 0;
1761 struct gcpro gcpro1, gcpro2;
1762
1763 i = validate_interval_range (src, &start, &end, soft);
1764 if (NULL_INTERVAL_P (i))
1765 return Qnil;
1766
1767 CHECK_NUMBER_COERCE_MARKER (pos);
1768 {
1769 Lisp_Object dest_start, dest_end;
1770
1771 dest_start = pos;
1772 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1773 /* Apply this to a copy of pos; it will try to increment its arguments,
1774 which we don't want. */
1775 validate_interval_range (dest, &dest_start, &dest_end, soft);
1776 }
1777
1778 s = XINT (start);
1779 e = XINT (end);
1780 p = XINT (pos);
1781
1782 stuff = Qnil;
1783
1784 while (s < e)
1785 {
1786 e2 = i->position + LENGTH (i);
1787 if (e2 > e)
1788 e2 = e;
1789 len = e2 - s;
1790
1791 plist = i->plist;
1792 if (! NILP (prop))
1793 while (! NILP (plist))
1794 {
1795 if (EQ (Fcar (plist), prop))
1796 {
1797 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1798 break;
1799 }
1800 plist = Fcdr (Fcdr (plist));
1801 }
1802 if (! NILP (plist))
1803 {
1804 /* Must defer modifications to the interval tree in case src
1805 and dest refer to the same string or buffer. */
1806 stuff = Fcons (Fcons (make_number (p),
1807 Fcons (make_number (p + len),
1808 Fcons (plist, Qnil))),
1809 stuff);
1810 }
1811
1812 i = next_interval (i);
1813 if (NULL_INTERVAL_P (i))
1814 break;
1815
1816 p += len;
1817 s = i->position;
1818 }
1819
1820 GCPRO2 (stuff, dest);
1821
1822 while (! NILP (stuff))
1823 {
1824 res = Fcar (stuff);
1825 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1826 Fcar (Fcdr (Fcdr (res))), dest);
1827 if (! NILP (res))
1828 modified++;
1829 stuff = Fcdr (stuff);
1830 }
1831
1832 UNGCPRO;
1833
1834 return modified ? Qt : Qnil;
1835 }
1836
1837
1838 /* Return a list representing the text properties of OBJECT between
1839 START and END. if PROP is non-nil, report only on that property.
1840 Each result list element has the form (S E PLIST), where S and E
1841 are positions in OBJECT and PLIST is a property list containing the
1842 text properties of OBJECT between S and E. Value is nil if OBJECT
1843 doesn't contain text properties between START and END. */
1844
1845 Lisp_Object
1846 text_property_list (object, start, end, prop)
1847 Lisp_Object object, start, end, prop;
1848 {
1849 struct interval *i;
1850 Lisp_Object result;
1851
1852 result = Qnil;
1853
1854 i = validate_interval_range (object, &start, &end, soft);
1855 if (!NULL_INTERVAL_P (i))
1856 {
1857 int s = XINT (start);
1858 int e = XINT (end);
1859
1860 while (s < e)
1861 {
1862 int interval_end, len;
1863 Lisp_Object plist;
1864
1865 interval_end = i->position + LENGTH (i);
1866 if (interval_end > e)
1867 interval_end = e;
1868 len = interval_end - s;
1869
1870 plist = i->plist;
1871
1872 if (!NILP (prop))
1873 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1874 if (EQ (Fcar (plist), prop))
1875 {
1876 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1877 break;
1878 }
1879
1880 if (!NILP (plist))
1881 result = Fcons (Fcons (make_number (s),
1882 Fcons (make_number (s + len),
1883 Fcons (plist, Qnil))),
1884 result);
1885
1886 i = next_interval (i);
1887 if (NULL_INTERVAL_P (i))
1888 break;
1889 s = i->position;
1890 }
1891 }
1892
1893 return result;
1894 }
1895
1896
1897 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1898 (START END PLIST), where START and END are positions and PLIST is a
1899 property list containing the text properties to add. Adjust START
1900 and END positions by DELTA before adding properties. Value is
1901 non-zero if OBJECT was modified. */
1902
1903 int
1904 add_text_properties_from_list (object, list, delta)
1905 Lisp_Object object, list, delta;
1906 {
1907 struct gcpro gcpro1, gcpro2;
1908 int modified_p = 0;
1909
1910 GCPRO2 (list, object);
1911
1912 for (; CONSP (list); list = XCDR (list))
1913 {
1914 Lisp_Object item, start, end, plist, tem;
1915
1916 item = XCAR (list);
1917 start = make_number (XINT (XCAR (item)) + XINT (delta));
1918 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1919 plist = XCAR (XCDR (XCDR (item)));
1920
1921 tem = Fadd_text_properties (start, end, plist, object);
1922 if (!NILP (tem))
1923 modified_p = 1;
1924 }
1925
1926 UNGCPRO;
1927 return modified_p;
1928 }
1929
1930
1931
1932 /* Modify end-points of ranges in LIST destructively. LIST is a list
1933 as returned from text_property_list. Change end-points equal to
1934 OLD_END to NEW_END. */
1935
1936 void
1937 extend_property_ranges (list, old_end, new_end)
1938 Lisp_Object list, old_end, new_end;
1939 {
1940 for (; CONSP (list); list = XCDR (list))
1941 {
1942 Lisp_Object item, end;
1943
1944 item = XCAR (list);
1945 end = XCAR (XCDR (item));
1946
1947 if (EQ (end, old_end))
1948 XSETCAR (XCDR (item), new_end);
1949 }
1950 }
1951
1952
1953 \f
1954 /* Call the modification hook functions in LIST, each with START and END. */
1955
1956 static void
1957 call_mod_hooks (list, start, end)
1958 Lisp_Object list, start, end;
1959 {
1960 struct gcpro gcpro1;
1961 GCPRO1 (list);
1962 while (!NILP (list))
1963 {
1964 call2 (Fcar (list), start, end);
1965 list = Fcdr (list);
1966 }
1967 UNGCPRO;
1968 }
1969
1970 /* Check for read-only intervals between character positions START ... END,
1971 in BUF, and signal an error if we find one.
1972
1973 Then check for any modification hooks in the range.
1974 Create a list of all these hooks in lexicographic order,
1975 eliminating consecutive extra copies of the same hook. Then call
1976 those hooks in order, with START and END - 1 as arguments. */
1977
1978 void
1979 verify_interval_modification (buf, start, end)
1980 struct buffer *buf;
1981 int start, end;
1982 {
1983 register INTERVAL intervals = BUF_INTERVALS (buf);
1984 register INTERVAL i;
1985 Lisp_Object hooks;
1986 register Lisp_Object prev_mod_hooks;
1987 Lisp_Object mod_hooks;
1988 struct gcpro gcpro1;
1989
1990 hooks = Qnil;
1991 prev_mod_hooks = Qnil;
1992 mod_hooks = Qnil;
1993
1994 interval_insert_behind_hooks = Qnil;
1995 interval_insert_in_front_hooks = Qnil;
1996
1997 if (NULL_INTERVAL_P (intervals))
1998 return;
1999
2000 if (start > end)
2001 {
2002 int temp = start;
2003 start = end;
2004 end = temp;
2005 }
2006
2007 /* For an insert operation, check the two chars around the position. */
2008 if (start == end)
2009 {
2010 INTERVAL prev = NULL;
2011 Lisp_Object before, after;
2012
2013 /* Set I to the interval containing the char after START,
2014 and PREV to the interval containing the char before START.
2015 Either one may be null. They may be equal. */
2016 i = find_interval (intervals, start);
2017
2018 if (start == BUF_BEGV (buf))
2019 prev = 0;
2020 else if (i->position == start)
2021 prev = previous_interval (i);
2022 else if (i->position < start)
2023 prev = i;
2024 if (start == BUF_ZV (buf))
2025 i = 0;
2026
2027 /* If Vinhibit_read_only is set and is not a list, we can
2028 skip the read_only checks. */
2029 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2030 {
2031 /* If I and PREV differ we need to check for the read-only
2032 property together with its stickiness. If either I or
2033 PREV are 0, this check is all we need.
2034 We have to take special care, since read-only may be
2035 indirectly defined via the category property. */
2036 if (i != prev)
2037 {
2038 if (! NULL_INTERVAL_P (i))
2039 {
2040 after = textget (i->plist, Qread_only);
2041
2042 /* If interval I is read-only and read-only is
2043 front-sticky, inhibit insertion.
2044 Check for read-only as well as category. */
2045 if (! NILP (after)
2046 && NILP (Fmemq (after, Vinhibit_read_only)))
2047 {
2048 Lisp_Object tem;
2049
2050 tem = textget (i->plist, Qfront_sticky);
2051 if (TMEM (Qread_only, tem)
2052 || (NILP (Fplist_get (i->plist, Qread_only))
2053 && TMEM (Qcategory, tem)))
2054 text_read_only ();
2055 }
2056 }
2057
2058 if (! NULL_INTERVAL_P (prev))
2059 {
2060 before = textget (prev->plist, Qread_only);
2061
2062 /* If interval PREV is read-only and read-only isn't
2063 rear-nonsticky, inhibit insertion.
2064 Check for read-only as well as category. */
2065 if (! NILP (before)
2066 && NILP (Fmemq (before, Vinhibit_read_only)))
2067 {
2068 Lisp_Object tem;
2069
2070 tem = textget (prev->plist, Qrear_nonsticky);
2071 if (! TMEM (Qread_only, tem)
2072 && (! NILP (Fplist_get (prev->plist,Qread_only))
2073 || ! TMEM (Qcategory, tem)))
2074 text_read_only ();
2075 }
2076 }
2077 }
2078 else if (! NULL_INTERVAL_P (i))
2079 {
2080 after = textget (i->plist, Qread_only);
2081
2082 /* If interval I is read-only and read-only is
2083 front-sticky, inhibit insertion.
2084 Check for read-only as well as category. */
2085 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2086 {
2087 Lisp_Object tem;
2088
2089 tem = textget (i->plist, Qfront_sticky);
2090 if (TMEM (Qread_only, tem)
2091 || (NILP (Fplist_get (i->plist, Qread_only))
2092 && TMEM (Qcategory, tem)))
2093 text_read_only ();
2094
2095 tem = textget (prev->plist, Qrear_nonsticky);
2096 if (! TMEM (Qread_only, tem)
2097 && (! NILP (Fplist_get (prev->plist, Qread_only))
2098 || ! TMEM (Qcategory, tem)))
2099 text_read_only ();
2100 }
2101 }
2102 }
2103
2104 /* Run both insert hooks (just once if they're the same). */
2105 if (!NULL_INTERVAL_P (prev))
2106 interval_insert_behind_hooks
2107 = textget (prev->plist, Qinsert_behind_hooks);
2108 if (!NULL_INTERVAL_P (i))
2109 interval_insert_in_front_hooks
2110 = textget (i->plist, Qinsert_in_front_hooks);
2111 }
2112 else
2113 {
2114 /* Loop over intervals on or next to START...END,
2115 collecting their hooks. */
2116
2117 i = find_interval (intervals, start);
2118 do
2119 {
2120 if (! INTERVAL_WRITABLE_P (i))
2121 text_read_only ();
2122
2123 if (!inhibit_modification_hooks)
2124 {
2125 mod_hooks = textget (i->plist, Qmodification_hooks);
2126 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2127 {
2128 hooks = Fcons (mod_hooks, hooks);
2129 prev_mod_hooks = mod_hooks;
2130 }
2131 }
2132
2133 i = next_interval (i);
2134 }
2135 /* Keep going thru the interval containing the char before END. */
2136 while (! NULL_INTERVAL_P (i) && i->position < end);
2137
2138 if (!inhibit_modification_hooks)
2139 {
2140 GCPRO1 (hooks);
2141 hooks = Fnreverse (hooks);
2142 while (! EQ (hooks, Qnil))
2143 {
2144 call_mod_hooks (Fcar (hooks), make_number (start),
2145 make_number (end));
2146 hooks = Fcdr (hooks);
2147 }
2148 UNGCPRO;
2149 }
2150 }
2151 }
2152
2153 /* Run the interval hooks for an insertion on character range START ... END.
2154 verify_interval_modification chose which hooks to run;
2155 this function is called after the insertion happens
2156 so it can indicate the range of inserted text. */
2157
2158 void
2159 report_interval_modification (start, end)
2160 Lisp_Object start, end;
2161 {
2162 if (! NILP (interval_insert_behind_hooks))
2163 call_mod_hooks (interval_insert_behind_hooks, start, end);
2164 if (! NILP (interval_insert_in_front_hooks)
2165 && ! EQ (interval_insert_in_front_hooks,
2166 interval_insert_behind_hooks))
2167 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2168 }
2169 \f
2170 void
2171 syms_of_textprop ()
2172 {
2173 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2174 doc: /* Property-list used as default values.
2175 The value of a property in this list is seen as the value for every
2176 character that does not have its own value for that property. */);
2177 Vdefault_text_properties = Qnil;
2178
2179 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2180 doc: /* Alist of alternative properties for properties without a value.
2181 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2182 If a piece of text has no direct value for a particular property, then
2183 this alist is consulted. If that property appears in the alist, then
2184 the first non-nil value from the associated alternative properties is
2185 returned. */);
2186 Vchar_property_alias_alist = Qnil;
2187
2188 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2189 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2190 This also inhibits the use of the `intangible' text property. */);
2191 Vinhibit_point_motion_hooks = Qnil;
2192
2193 DEFVAR_LISP ("text-property-default-nonsticky",
2194 &Vtext_property_default_nonsticky,
2195 doc: /* Alist of properties vs the corresponding non-stickinesses.
2196 Each element has the form (PROPERTY . NONSTICKINESS).
2197
2198 If a character in a buffer has PROPERTY, new text inserted adjacent to
2199 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2200 inherits it if NONSTICKINESS is nil. The front-sticky and
2201 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2202 Vtext_property_default_nonsticky = Qnil;
2203
2204 staticpro (&interval_insert_behind_hooks);
2205 staticpro (&interval_insert_in_front_hooks);
2206 interval_insert_behind_hooks = Qnil;
2207 interval_insert_in_front_hooks = Qnil;
2208
2209
2210 /* Common attributes one might give text */
2211
2212 staticpro (&Qforeground);
2213 Qforeground = intern ("foreground");
2214 staticpro (&Qbackground);
2215 Qbackground = intern ("background");
2216 staticpro (&Qfont);
2217 Qfont = intern ("font");
2218 staticpro (&Qstipple);
2219 Qstipple = intern ("stipple");
2220 staticpro (&Qunderline);
2221 Qunderline = intern ("underline");
2222 staticpro (&Qread_only);
2223 Qread_only = intern ("read-only");
2224 staticpro (&Qinvisible);
2225 Qinvisible = intern ("invisible");
2226 staticpro (&Qintangible);
2227 Qintangible = intern ("intangible");
2228 staticpro (&Qcategory);
2229 Qcategory = intern ("category");
2230 staticpro (&Qlocal_map);
2231 Qlocal_map = intern ("local-map");
2232 staticpro (&Qfront_sticky);
2233 Qfront_sticky = intern ("front-sticky");
2234 staticpro (&Qrear_nonsticky);
2235 Qrear_nonsticky = intern ("rear-nonsticky");
2236 staticpro (&Qmouse_face);
2237 Qmouse_face = intern ("mouse-face");
2238
2239 /* Properties that text might use to specify certain actions */
2240
2241 staticpro (&Qmouse_left);
2242 Qmouse_left = intern ("mouse-left");
2243 staticpro (&Qmouse_entered);
2244 Qmouse_entered = intern ("mouse-entered");
2245 staticpro (&Qpoint_left);
2246 Qpoint_left = intern ("point-left");
2247 staticpro (&Qpoint_entered);
2248 Qpoint_entered = intern ("point-entered");
2249
2250 defsubr (&Stext_properties_at);
2251 defsubr (&Sget_text_property);
2252 defsubr (&Sget_char_property);
2253 defsubr (&Snext_char_property_change);
2254 defsubr (&Sprevious_char_property_change);
2255 defsubr (&Snext_single_char_property_change);
2256 defsubr (&Sprevious_single_char_property_change);
2257 defsubr (&Snext_property_change);
2258 defsubr (&Snext_single_property_change);
2259 defsubr (&Sprevious_property_change);
2260 defsubr (&Sprevious_single_property_change);
2261 defsubr (&Sadd_text_properties);
2262 defsubr (&Sput_text_property);
2263 defsubr (&Sset_text_properties);
2264 defsubr (&Sremove_text_properties);
2265 defsubr (&Sremove_list_of_text_properties);
2266 defsubr (&Stext_property_any);
2267 defsubr (&Stext_property_not_all);
2268 /* defsubr (&Serase_text_properties); */
2269 /* defsubr (&Scopy_text_properties); */
2270 }
2271