1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
23 #include "intervals.h"
28 #define NULL (void *)0
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
49 /* The rest of the file is within this conditional */
50 #ifdef USE_TEXT_PROPERTIES
53 Lisp_Object Qmouse_left
;
54 Lisp_Object Qmouse_entered
;
55 Lisp_Object Qpoint_left
;
56 Lisp_Object Qpoint_entered
;
57 Lisp_Object Qcategory
;
58 Lisp_Object Qlocal_map
;
60 /* Visual properties text (including strings) may have. */
61 Lisp_Object Qforeground
, Qbackground
, Qfont
, Qunderline
, Qstipple
;
62 Lisp_Object Qinvisible
, Qread_only
, Qintangible
;
64 /* Sticky properties */
65 Lisp_Object Qfront_sticky
, Qrear_nonsticky
;
67 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
68 the o1's cdr. Otherwise, return zero. This is handy for
70 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCONS (o1)->cdr, CONSP (o2)))
72 Lisp_Object Vinhibit_point_motion_hooks
;
73 Lisp_Object Vdefault_text_properties
;
75 /* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77 Lisp_Object interval_insert_behind_hooks
;
78 Lisp_Object interval_insert_in_front_hooks
;
80 /* Extract the interval at the position pointed to by BEGIN from
81 OBJECT, a string or buffer. Additionally, check that the positions
82 pointed to by BEGIN and END are within the bounds of OBJECT, and
83 reverse them if *BEGIN is greater than *END. The objects pointed
84 to by BEGIN and END may be integers or markers; if the latter, they
85 are coerced to integers.
87 When OBJECT is a string, we increment *BEGIN and *END
88 to make them origin-one.
90 Note that buffer points don't correspond to interval indices.
91 For example, point-max is 1 greater than the index of the last
92 character. This difference is handled in the caller, which uses
93 the validated points to determine a length, and operates on that.
94 Exceptions are Ftext_properties_at, Fnext_property_change, and
95 Fprevious_property_change which call this function with BEGIN == END.
96 Handle this case specially.
98 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
99 create an interval tree for OBJECT if one doesn't exist, provided
100 the object actually contains text. In the current design, if there
101 is no text, there can be no text properties. */
107 validate_interval_range (object
, begin
, end
, force
)
108 Lisp_Object object
, *begin
, *end
;
114 CHECK_STRING_OR_BUFFER (object
, 0);
115 CHECK_NUMBER_COERCE_MARKER (*begin
, 0);
116 CHECK_NUMBER_COERCE_MARKER (*end
, 0);
118 /* If we are asked for a point, but from a subr which operates
119 on a range, then return nothing. */
120 if (EQ (*begin
, *end
) && begin
!= end
)
121 return NULL_INTERVAL
;
123 if (XINT (*begin
) > XINT (*end
))
131 if (BUFFERP (object
))
133 register struct buffer
*b
= XBUFFER (object
);
135 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
136 && XINT (*end
) <= BUF_ZV (b
)))
137 args_out_of_range (*begin
, *end
);
138 i
= BUF_INTERVALS (b
);
140 /* If there's no text, there are no properties. */
141 if (BUF_BEGV (b
) == BUF_ZV (b
))
142 return NULL_INTERVAL
;
144 searchpos
= XINT (*begin
);
148 register struct Lisp_String
*s
= XSTRING (object
);
150 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
151 && XINT (*end
) <= s
->size
))
152 args_out_of_range (*begin
, *end
);
153 /* User-level Positions in strings start with 0,
154 but the interval code always wants positions starting with 1. */
155 XSETFASTINT (*begin
, XFASTINT (*begin
) + 1);
157 XSETFASTINT (*end
, XFASTINT (*end
) + 1);
161 return NULL_INTERVAL
;
163 searchpos
= XINT (*begin
);
166 if (NULL_INTERVAL_P (i
))
167 return (force
? create_root_interval (object
) : i
);
169 return find_interval (i
, searchpos
);
172 /* Validate LIST as a property list. If LIST is not a list, then
173 make one consisting of (LIST nil). Otherwise, verify that LIST
174 is even numbered and thus suitable as a plist. */
177 validate_plist (list
)
186 register Lisp_Object tail
;
187 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
193 error ("Odd length text property list");
197 return Fcons (list
, Fcons (Qnil
, Qnil
));
200 /* Return nonzero if interval I has all the properties,
201 with the same values, of list PLIST. */
204 interval_has_all_properties (plist
, i
)
208 register Lisp_Object tail1
, tail2
, sym1
, sym2
;
211 /* Go through each element of PLIST. */
212 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
217 /* Go through I's plist, looking for sym1 */
218 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
219 if (EQ (sym1
, Fcar (tail2
)))
221 /* Found the same property on both lists. If the
222 values are unequal, return zero. */
223 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
226 /* Property has same value on both lists; go to next one. */
238 /* Return nonzero if the plist of interval I has any of the
239 properties of PLIST, regardless of their values. */
242 interval_has_some_properties (plist
, i
)
246 register Lisp_Object tail1
, tail2
, sym
;
248 /* Go through each element of PLIST. */
249 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
253 /* Go through i's plist, looking for tail1 */
254 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
255 if (EQ (sym
, Fcar (tail2
)))
262 /* Changing the plists of individual intervals. */
264 /* Return the value of PROP in property-list PLIST, or Qunbound if it
267 property_value (plist
, prop
)
268 Lisp_Object plist
, prop
;
272 while (PLIST_ELT_P (plist
, value
))
273 if (EQ (XCONS (plist
)->car
, prop
))
274 return XCONS (value
)->car
;
276 plist
= XCONS (value
)->cdr
;
281 /* Set the properties of INTERVAL to PROPERTIES,
282 and record undo info for the previous values.
283 OBJECT is the string or buffer that INTERVAL belongs to. */
286 set_properties (properties
, interval
, object
)
287 Lisp_Object properties
, object
;
290 Lisp_Object sym
, value
;
292 if (BUFFERP (object
))
294 /* For each property in the old plist which is missing from PROPERTIES,
295 or has a different value in PROPERTIES, make an undo record. */
296 for (sym
= interval
->plist
;
297 PLIST_ELT_P (sym
, value
);
298 sym
= XCONS (value
)->cdr
)
299 if (! EQ (property_value (properties
, XCONS (sym
)->car
),
302 record_property_change (interval
->position
, LENGTH (interval
),
303 XCONS (sym
)->car
, XCONS (value
)->car
,
307 /* For each new property that has no value at all in the old plist,
308 make an undo record binding it to nil, so it will be removed. */
309 for (sym
= properties
;
310 PLIST_ELT_P (sym
, value
);
311 sym
= XCONS (value
)->cdr
)
312 if (EQ (property_value (interval
->plist
, XCONS (sym
)->car
), Qunbound
))
314 record_property_change (interval
->position
, LENGTH (interval
),
315 XCONS (sym
)->car
, Qnil
,
320 /* Store new properties. */
321 interval
->plist
= Fcopy_sequence (properties
);
324 /* Add the properties of PLIST to the interval I, or set
325 the value of I's property to the value of the property on PLIST
326 if they are different.
328 OBJECT should be the string or buffer the interval is in.
330 Return nonzero if this changes I (i.e., if any members of PLIST
331 are actually added to I's plist) */
334 add_properties (plist
, i
, object
)
339 Lisp_Object tail1
, tail2
, sym1
, val1
;
340 register int changed
= 0;
342 struct gcpro gcpro1
, gcpro2
, gcpro3
;
347 /* No need to protect OBJECT, because we can GC only in the case
348 where it is a buffer, and live buffers are always protected.
349 I and its plist are also protected, via OBJECT. */
350 GCPRO3 (tail1
, sym1
, val1
);
352 /* Go through each element of PLIST. */
353 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
356 val1
= Fcar (Fcdr (tail1
));
359 /* Go through I's plist, looking for sym1 */
360 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
361 if (EQ (sym1
, Fcar (tail2
)))
363 /* No need to gcpro, because tail2 protects this
364 and it must be a cons cell (we get an error otherwise). */
365 register Lisp_Object this_cdr
;
367 this_cdr
= Fcdr (tail2
);
368 /* Found the property. Now check its value. */
371 /* The properties have the same value on both lists.
372 Continue to the next property. */
373 if (EQ (val1
, Fcar (this_cdr
)))
376 /* Record this change in the buffer, for undo purposes. */
377 if (BUFFERP (object
))
379 record_property_change (i
->position
, LENGTH (i
),
380 sym1
, Fcar (this_cdr
), object
);
383 /* I's property has a different value -- change it */
384 Fsetcar (this_cdr
, val1
);
391 /* Record this change in the buffer, for undo purposes. */
392 if (BUFFERP (object
))
394 record_property_change (i
->position
, LENGTH (i
),
397 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
407 /* For any members of PLIST which are properties of I, remove them
409 OBJECT is the string or buffer containing I. */
412 remove_properties (plist
, i
, object
)
417 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
418 register int changed
= 0;
420 current_plist
= i
->plist
;
421 /* Go through each element of plist. */
422 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
426 /* First, remove the symbol if its at the head of the list */
427 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
429 if (BUFFERP (object
))
431 record_property_change (i
->position
, LENGTH (i
),
432 sym
, Fcar (Fcdr (current_plist
)),
436 current_plist
= Fcdr (Fcdr (current_plist
));
440 /* Go through i's plist, looking for sym */
441 tail2
= current_plist
;
442 while (! NILP (tail2
))
444 register Lisp_Object
this;
445 this = Fcdr (Fcdr (tail2
));
446 if (EQ (sym
, Fcar (this)))
448 if (BUFFERP (object
))
450 record_property_change (i
->position
, LENGTH (i
),
451 sym
, Fcar (Fcdr (this)), object
);
454 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
462 i
->plist
= current_plist
;
467 /* Remove all properties from interval I. Return non-zero
468 if this changes the interval. */
482 DEFUN ("text-properties-at", Ftext_properties_at
,
483 Stext_properties_at
, 1, 2, 0,
484 "Return the list of properties held by the character at POSITION\n\
485 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
486 defaults to the current buffer.\n\
487 If POSITION is at the end of OBJECT, the value is nil.")
489 Lisp_Object position
, object
;
494 XSETBUFFER (object
, current_buffer
);
496 i
= validate_interval_range (object
, &position
, &position
, soft
);
497 if (NULL_INTERVAL_P (i
))
499 /* If POSITION is at the end of the interval,
500 it means it's the end of OBJECT.
501 There are no properties at the very end,
502 since no character follows. */
503 if (XINT (position
) == LENGTH (i
) + i
->position
)
509 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
510 "Return the value of POSITION's property PROP, in OBJECT.\n\
511 OBJECT is optional and defaults to the current buffer.\n\
512 If POSITION is at the end of OBJECT, the value is nil.")
513 (position
, prop
, object
)
514 Lisp_Object position
, object
;
517 return textget (Ftext_properties_at (position
, object
), prop
);
520 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
521 "Return the value of POSITION's property PROP, in OBJECT.\n\
522 OBJECT is optional and defaults to the current buffer.\n\
523 If POSITION is at the end of OBJECT, the value is nil.\n\
524 If OBJECT is a buffer, then overlay properties are considered as well as\n\
526 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
527 overlays are considered only if they are associated with OBJECT.")
528 (position
, prop
, object
)
529 Lisp_Object position
, object
;
530 register Lisp_Object prop
;
532 struct window
*w
= 0;
534 CHECK_NUMBER_COERCE_MARKER (position
, 0);
537 XSETBUFFER (object
, current_buffer
);
539 if (WINDOWP (object
))
541 w
= XWINDOW (object
);
544 if (BUFFERP (object
))
546 int posn
= XINT (position
);
548 Lisp_Object
*overlay_vec
, tem
;
551 struct buffer
*obuf
= current_buffer
;
553 set_buffer_temp (XBUFFER (object
));
555 /* First try with room for 40 overlays. */
557 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
559 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
560 &next_overlay
, NULL
);
562 /* If there are more than 40,
563 make enough space for all, and try again. */
567 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
568 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
569 &next_overlay
, NULL
);
571 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
573 set_buffer_temp (obuf
);
575 /* Now check the overlays in order of decreasing priority. */
576 while (--noverlays
>= 0)
578 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
583 /* Not a buffer, or no appropriate overlay, so fall through to the
585 return (Fget_text_property (position
, prop
, object
));
588 DEFUN ("next-char-property-change", Fnext_char_property_change
,
589 Snext_char_property_change
, 1, 2, 0,
590 "Return the position of next text property or overlay change.\n\
591 This scans characters forward from POSITION in OBJECT till it finds\n\
592 a change in some text property, or the beginning or end of an overlay,\n\
593 and returns the position of that.\n\
594 If none is found, the function returns (point-max).\n\
596 If the optional third argument LIMIT is non-nil, don't search\n\
597 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
599 Lisp_Object position
, limit
;
603 temp
= Fnext_overlay_change (position
);
606 CHECK_NUMBER (limit
, 2);
607 if (XINT (limit
) < XINT (temp
))
610 return Fnext_property_change (position
, Qnil
, temp
);
613 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
614 Sprevious_char_property_change
, 1, 2, 0,
615 "Return the position of previous text property or overlay change.\n\
616 Scans characters backward from POSITION in OBJECT till it finds\n\
617 a change in some text property, or the beginning or end of an overlay,\n\
618 and returns the position of that.\n\
619 If none is found, the function returns (point-max).\n\
621 If the optional third argument LIMIT is non-nil, don't search\n\
622 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
624 Lisp_Object position
, limit
;
628 temp
= Fprevious_overlay_change (position
);
631 CHECK_NUMBER (limit
, 2);
632 if (XINT (limit
) > XINT (temp
))
635 return Fprevious_property_change (position
, Qnil
, temp
);
638 DEFUN ("next-property-change", Fnext_property_change
,
639 Snext_property_change
, 1, 3, 0,
640 "Return the position of next property change.\n\
641 Scans characters forward from POSITION in OBJECT till it finds\n\
642 a change in some text property, then returns the position of the change.\n\
643 The optional second argument OBJECT is the string or buffer to scan.\n\
644 Return nil if the property is constant all the way to the end of OBJECT.\n\
645 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
646 If the optional third argument LIMIT is non-nil, don't search\n\
647 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
648 (position
, object
, limit
)
649 Lisp_Object position
, object
, limit
;
651 register INTERVAL i
, next
;
654 XSETBUFFER (object
, current_buffer
);
656 if (! NILP (limit
) && ! EQ (limit
, Qt
))
657 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
659 i
= validate_interval_range (object
, &position
, &position
, soft
);
661 /* If LIMIT is t, return start of next interval--don't
662 bother checking further intervals. */
665 if (NULL_INTERVAL_P (i
))
668 next
= next_interval (i
);
670 if (NULL_INTERVAL_P (next
))
671 XSETFASTINT (position
, (STRINGP (object
)
672 ? XSTRING (object
)->size
673 : BUF_ZV (XBUFFER (object
))));
675 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
679 if (NULL_INTERVAL_P (i
))
682 next
= next_interval (i
);
684 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
685 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
686 next
= next_interval (next
);
688 if (NULL_INTERVAL_P (next
))
690 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
693 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
697 /* Return 1 if there's a change in some property between BEG and END. */
700 property_change_between_p (beg
, end
)
703 register INTERVAL i
, next
;
704 Lisp_Object object
, pos
;
706 XSETBUFFER (object
, current_buffer
);
707 XSETFASTINT (pos
, beg
);
709 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
710 if (NULL_INTERVAL_P (i
))
713 next
= next_interval (i
);
714 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
716 next
= next_interval (next
);
717 if (NULL_INTERVAL_P (next
))
719 if (next
->position
>= end
)
723 if (NULL_INTERVAL_P (next
))
729 DEFUN ("next-single-property-change", Fnext_single_property_change
,
730 Snext_single_property_change
, 2, 4, 0,
731 "Return the position of next property change for a specific property.\n\
732 Scans characters forward from POSITION till it finds\n\
733 a change in the PROP property, then returns the position of the change.\n\
734 The optional third argument OBJECT is the string or buffer to scan.\n\
735 The property values are compared with `eq'.\n\
736 Return nil if the property is constant all the way to the end of OBJECT.\n\
737 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
738 If the optional fourth argument LIMIT is non-nil, don't search\n\
739 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
740 (position
, prop
, object
, limit
)
741 Lisp_Object position
, prop
, object
, limit
;
743 register INTERVAL i
, next
;
744 register Lisp_Object here_val
;
747 XSETBUFFER (object
, current_buffer
);
750 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
752 i
= validate_interval_range (object
, &position
, &position
, soft
);
753 if (NULL_INTERVAL_P (i
))
756 here_val
= textget (i
->plist
, prop
);
757 next
= next_interval (i
);
758 while (! NULL_INTERVAL_P (next
)
759 && EQ (here_val
, textget (next
->plist
, prop
))
760 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
761 next
= next_interval (next
);
763 if (NULL_INTERVAL_P (next
))
765 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
768 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
772 DEFUN ("previous-property-change", Fprevious_property_change
,
773 Sprevious_property_change
, 1, 3, 0,
774 "Return the position of previous property change.\n\
775 Scans characters backwards from POSITION in OBJECT till it finds\n\
776 a change in some text property, then returns the position of the change.\n\
777 The optional second argument OBJECT is the string or buffer to scan.\n\
778 Return nil if the property is constant all the way to the start of OBJECT.\n\
779 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
780 If the optional third argument LIMIT is non-nil, don't search\n\
781 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
782 (position
, object
, limit
)
783 Lisp_Object position
, object
, limit
;
785 register INTERVAL i
, previous
;
788 XSETBUFFER (object
, current_buffer
);
791 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
793 i
= validate_interval_range (object
, &position
, &position
, soft
);
794 if (NULL_INTERVAL_P (i
))
797 /* Start with the interval containing the char before point. */
798 if (i
->position
== XFASTINT (position
))
799 i
= previous_interval (i
);
801 previous
= previous_interval (i
);
802 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
804 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
805 previous
= previous_interval (previous
);
806 if (NULL_INTERVAL_P (previous
))
809 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
812 XSETFASTINT (position
, (previous
->position
+ LENGTH (previous
)
813 - (STRINGP (object
))));
817 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
818 Sprevious_single_property_change
, 2, 4, 0,
819 "Return the position of previous property change for a specific property.\n\
820 Scans characters backward from POSITION till it finds\n\
821 a change in the PROP property, then returns the position of the change.\n\
822 The optional third argument OBJECT is the string or buffer to scan.\n\
823 The property values are compared with `eq'.\n\
824 Return nil if the property is constant all the way to the start of OBJECT.\n\
825 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
826 If the optional fourth argument LIMIT is non-nil, don't search\n\
827 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
828 (position
, prop
, object
, limit
)
829 Lisp_Object position
, prop
, object
, limit
;
831 register INTERVAL i
, previous
;
832 register Lisp_Object here_val
;
835 XSETBUFFER (object
, current_buffer
);
838 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
840 i
= validate_interval_range (object
, &position
, &position
, soft
);
842 /* Start with the interval containing the char before point. */
843 if (! NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
844 i
= previous_interval (i
);
846 if (NULL_INTERVAL_P (i
))
849 here_val
= textget (i
->plist
, prop
);
850 previous
= previous_interval (i
);
851 while (! NULL_INTERVAL_P (previous
)
852 && EQ (here_val
, textget (previous
->plist
, prop
))
854 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
855 previous
= previous_interval (previous
);
856 if (NULL_INTERVAL_P (previous
))
859 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
862 XSETFASTINT (position
, (previous
->position
+ LENGTH (previous
)
863 - (STRINGP (object
))));
867 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
869 DEFUN ("add-text-properties", Fadd_text_properties
,
870 Sadd_text_properties
, 3, 4, 0,
871 "Add properties to the text from START to END.\n\
872 The third argument PROPERTIES is a property list\n\
873 specifying the property values to add.\n\
874 The optional fourth argument, OBJECT,\n\
875 is the string or buffer containing the text.\n\
876 Return t if any property value actually changed, nil otherwise.")
877 (start
, end
, properties
, object
)
878 Lisp_Object start
, end
, properties
, object
;
880 register INTERVAL i
, unchanged
;
881 register int s
, len
, modified
= 0;
884 properties
= validate_plist (properties
);
885 if (NILP (properties
))
889 XSETBUFFER (object
, current_buffer
);
891 i
= validate_interval_range (object
, &start
, &end
, hard
);
892 if (NULL_INTERVAL_P (i
))
896 len
= XINT (end
) - s
;
898 /* No need to protect OBJECT, because we GC only if it's a buffer,
899 and live buffers are always protected. */
902 /* If we're not starting on an interval boundary, we have to
903 split this interval. */
904 if (i
->position
!= s
)
906 /* If this interval already has the properties, we can
908 if (interval_has_all_properties (properties
, i
))
910 int got
= (LENGTH (i
) - (s
- i
->position
));
912 RETURN_UNGCPRO (Qnil
);
914 i
= next_interval (i
);
919 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
920 copy_properties (unchanged
, i
);
924 if (BUFFERP (object
))
925 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
927 /* We are at the beginning of interval I, with LEN chars to scan. */
933 if (LENGTH (i
) >= len
)
935 /* We can UNGCPRO safely here, because there will be just
936 one more chance to gc, in the next call to add_properties,
937 and after that we will not need PROPERTIES or OBJECT again. */
940 if (interval_has_all_properties (properties
, i
))
942 if (BUFFERP (object
))
943 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
944 XINT (end
) - XINT (start
));
946 return modified
? Qt
: Qnil
;
949 if (LENGTH (i
) == len
)
951 add_properties (properties
, i
, object
);
952 if (BUFFERP (object
))
953 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
954 XINT (end
) - XINT (start
));
958 /* i doesn't have the properties, and goes past the change limit */
960 i
= split_interval_left (unchanged
, len
);
961 copy_properties (unchanged
, i
);
962 add_properties (properties
, i
, object
);
963 if (BUFFERP (object
))
964 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
965 XINT (end
) - XINT (start
));
970 modified
+= add_properties (properties
, i
, object
);
971 i
= next_interval (i
);
975 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
977 DEFUN ("put-text-property", Fput_text_property
,
978 Sput_text_property
, 4, 5, 0,
979 "Set one property of the text from START to END.\n\
980 The third and fourth arguments PROPERTY and VALUE\n\
981 specify the property to add.\n\
982 The optional fifth argument, OBJECT,\n\
983 is the string or buffer containing the text.")
984 (start
, end
, property
, value
, object
)
985 Lisp_Object start
, end
, property
, value
, object
;
987 Fadd_text_properties (start
, end
,
988 Fcons (property
, Fcons (value
, Qnil
)),
993 DEFUN ("set-text-properties", Fset_text_properties
,
994 Sset_text_properties
, 3, 4, 0,
995 "Completely replace properties of text from START to END.\n\
996 The third argument PROPERTIES is the new property list.\n\
997 The optional fourth argument, OBJECT,\n\
998 is the string or buffer containing the text.")
999 (start
, end
, properties
, object
)
1000 Lisp_Object start
, end
, properties
, object
;
1002 register INTERVAL i
, unchanged
;
1003 register INTERVAL prev_changed
= NULL_INTERVAL
;
1004 register int s
, len
;
1005 Lisp_Object ostart
, oend
;
1006 int have_modified
= 0;
1011 properties
= validate_plist (properties
);
1014 XSETBUFFER (object
, current_buffer
);
1016 /* If we want no properties for a whole string,
1017 get rid of its intervals. */
1018 if (NILP (properties
) && STRINGP (object
)
1019 && XFASTINT (start
) == 0
1020 && XFASTINT (end
) == XSTRING (object
)->size
)
1022 if (! XSTRING (object
)->intervals
)
1025 XSTRING (object
)->intervals
= 0;
1029 i
= validate_interval_range (object
, &start
, &end
, soft
);
1031 if (NULL_INTERVAL_P (i
))
1033 /* If buffer has no properties, and we want none, return now. */
1034 if (NILP (properties
))
1037 /* Restore the original START and END values
1038 because validate_interval_range increments them for strings. */
1042 i
= validate_interval_range (object
, &start
, &end
, hard
);
1043 /* This can return if start == end. */
1044 if (NULL_INTERVAL_P (i
))
1049 len
= XINT (end
) - s
;
1051 if (BUFFERP (object
))
1052 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1054 if (i
->position
!= s
)
1057 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1059 if (LENGTH (i
) > len
)
1061 copy_properties (unchanged
, i
);
1062 i
= split_interval_left (i
, len
);
1063 set_properties (properties
, i
, object
);
1064 if (BUFFERP (object
))
1065 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1066 XINT (end
) - XINT (start
));
1071 set_properties (properties
, i
, object
);
1073 if (LENGTH (i
) == len
)
1075 if (BUFFERP (object
))
1076 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1077 XINT (end
) - XINT (start
));
1084 i
= next_interval (i
);
1087 /* We are starting at the beginning of an interval, I */
1093 if (LENGTH (i
) >= len
)
1095 if (LENGTH (i
) > len
)
1096 i
= split_interval_left (i
, len
);
1098 /* We have to call set_properties even if we are going to
1099 merge the intervals, so as to make the undo records
1100 and cause redisplay to happen. */
1101 set_properties (properties
, i
, object
);
1102 if (!NULL_INTERVAL_P (prev_changed
))
1103 merge_interval_left (i
);
1104 if (BUFFERP (object
))
1105 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1106 XINT (end
) - XINT (start
));
1112 /* We have to call set_properties even if we are going to
1113 merge the intervals, so as to make the undo records
1114 and cause redisplay to happen. */
1115 set_properties (properties
, i
, object
);
1116 if (NULL_INTERVAL_P (prev_changed
))
1119 prev_changed
= i
= merge_interval_left (i
);
1121 i
= next_interval (i
);
1124 if (BUFFERP (object
))
1125 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1126 XINT (end
) - XINT (start
));
1130 DEFUN ("remove-text-properties", Fremove_text_properties
,
1131 Sremove_text_properties
, 3, 4, 0,
1132 "Remove some properties from text from START to END.\n\
1133 The third argument PROPERTIES is a property list\n\
1134 whose property names specify the properties to remove.\n\
1135 \(The values stored in PROPERTIES are ignored.)\n\
1136 The optional fourth argument, OBJECT,\n\
1137 is the string or buffer containing the text.\n\
1138 Return t if any property was actually removed, nil otherwise.")
1139 (start
, end
, properties
, object
)
1140 Lisp_Object start
, end
, properties
, object
;
1142 register INTERVAL i
, unchanged
;
1143 register int s
, len
, modified
= 0;
1146 XSETBUFFER (object
, current_buffer
);
1148 i
= validate_interval_range (object
, &start
, &end
, soft
);
1149 if (NULL_INTERVAL_P (i
))
1153 len
= XINT (end
) - s
;
1155 if (i
->position
!= s
)
1157 /* No properties on this first interval -- return if
1158 it covers the entire region. */
1159 if (! interval_has_some_properties (properties
, i
))
1161 int got
= (LENGTH (i
) - (s
- i
->position
));
1165 i
= next_interval (i
);
1167 /* Split away the beginning of this interval; what we don't
1172 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1173 copy_properties (unchanged
, i
);
1177 if (BUFFERP (object
))
1178 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1180 /* We are at the beginning of an interval, with len to scan */
1186 if (LENGTH (i
) >= len
)
1188 if (! interval_has_some_properties (properties
, i
))
1189 return modified
? Qt
: Qnil
;
1191 if (LENGTH (i
) == len
)
1193 remove_properties (properties
, i
, object
);
1194 if (BUFFERP (object
))
1195 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1196 XINT (end
) - XINT (start
));
1200 /* i has the properties, and goes past the change limit */
1202 i
= split_interval_left (i
, len
);
1203 copy_properties (unchanged
, i
);
1204 remove_properties (properties
, i
, object
);
1205 if (BUFFERP (object
))
1206 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1207 XINT (end
) - XINT (start
));
1212 modified
+= remove_properties (properties
, i
, object
);
1213 i
= next_interval (i
);
1217 DEFUN ("text-property-any", Ftext_property_any
,
1218 Stext_property_any
, 4, 5, 0,
1219 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1220 If so, return the position of the first character whose property PROPERTY\n\
1221 is `eq' to VALUE. Otherwise return nil.\n\
1222 The optional fifth argument, OBJECT, is the string or buffer\n\
1223 containing the text.")
1224 (start
, end
, property
, value
, object
)
1225 Lisp_Object start
, end
, property
, value
, object
;
1227 register INTERVAL i
;
1228 register int e
, pos
;
1231 XSETBUFFER (object
, current_buffer
);
1232 i
= validate_interval_range (object
, &start
, &end
, soft
);
1233 if (NULL_INTERVAL_P (i
))
1234 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1237 while (! NULL_INTERVAL_P (i
))
1239 if (i
->position
>= e
)
1241 if (EQ (textget (i
->plist
, property
), value
))
1244 if (pos
< XINT (start
))
1246 return make_number (pos
- (STRINGP (object
)));
1248 i
= next_interval (i
);
1253 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1254 Stext_property_not_all
, 4, 5, 0,
1255 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1256 If so, return the position of the first character whose property PROPERTY\n\
1257 is not `eq' to VALUE. Otherwise, return nil.\n\
1258 The optional fifth argument, OBJECT, is the string or buffer\n\
1259 containing the text.")
1260 (start
, end
, property
, value
, object
)
1261 Lisp_Object start
, end
, property
, value
, object
;
1263 register INTERVAL i
;
1267 XSETBUFFER (object
, current_buffer
);
1268 i
= validate_interval_range (object
, &start
, &end
, soft
);
1269 if (NULL_INTERVAL_P (i
))
1270 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1274 while (! NULL_INTERVAL_P (i
))
1276 if (i
->position
>= e
)
1278 if (! EQ (textget (i
->plist
, property
), value
))
1280 if (i
->position
> s
)
1282 return make_number (s
- (STRINGP (object
)));
1284 i
= next_interval (i
);
1289 #if 0 /* You can use set-text-properties for this. */
1291 DEFUN ("erase-text-properties", Ferase_text_properties
,
1292 Serase_text_properties
, 2, 3, 0,
1293 "Remove all properties from the text from START to END.\n\
1294 The optional third argument, OBJECT,\n\
1295 is the string or buffer containing the text.")
1296 (start
, end
, object
)
1297 Lisp_Object start
, end
, object
;
1299 register INTERVAL i
;
1300 register INTERVAL prev_changed
= NULL_INTERVAL
;
1301 register int s
, len
, modified
;
1304 XSETBUFFER (object
, current_buffer
);
1306 i
= validate_interval_range (object
, &start
, &end
, soft
);
1307 if (NULL_INTERVAL_P (i
))
1311 len
= XINT (end
) - s
;
1313 if (i
->position
!= s
)
1316 register INTERVAL unchanged
= i
;
1318 /* If there are properties here, then this text will be modified. */
1319 if (! NILP (i
->plist
))
1321 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1325 if (LENGTH (i
) > len
)
1327 i
= split_interval_right (i
, len
);
1328 copy_properties (unchanged
, i
);
1332 if (LENGTH (i
) == len
)
1337 /* If the text of I is without any properties, and contains
1338 LEN or more characters, then we may return without changing
1340 else if (LENGTH (i
) - (s
- i
->position
) <= len
)
1342 /* The amount of text to change extends past I, so just note
1343 how much we've gotten. */
1345 got
= LENGTH (i
) - (s
- i
->position
);
1349 i
= next_interval (i
);
1352 /* We are starting at the beginning of an interval, I. */
1355 if (LENGTH (i
) >= len
)
1357 /* If I has no properties, simply merge it if possible. */
1358 if (NILP (i
->plist
))
1360 if (! NULL_INTERVAL_P (prev_changed
))
1361 merge_interval_left (i
);
1363 return modified
? Qt
: Qnil
;
1366 if (LENGTH (i
) > len
)
1367 i
= split_interval_left (i
, len
);
1368 if (! NULL_INTERVAL_P (prev_changed
))
1369 merge_interval_left (i
);
1376 /* Here if we still need to erase past the end of I */
1378 if (NULL_INTERVAL_P (prev_changed
))
1380 modified
+= erase_properties (i
);
1385 modified
+= ! NILP (i
->plist
);
1386 /* Merging I will give it the properties of PREV_CHANGED. */
1387 prev_changed
= i
= merge_interval_left (i
);
1390 i
= next_interval (i
);
1393 return modified
? Qt
: Qnil
;
1397 /* I don't think this is the right interface to export; how often do you
1398 want to do something like this, other than when you're copying objects
1401 I think it would be better to have a pair of functions, one which
1402 returns the text properties of a region as a list of ranges and
1403 plists, and another which applies such a list to another object. */
1405 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1406 SRC and DEST may each refer to strings or buffers.
1407 Optional sixth argument PROP causes only that property to be copied.
1408 Properties are copied to DEST as if by `add-text-properties'.
1409 Return t if any property value actually changed, nil otherwise. */
1411 /* Note this can GC when DEST is a buffer. */
1414 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1415 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1421 int s
, e
, e2
, p
, len
, modified
= 0;
1422 struct gcpro gcpro1
, gcpro2
;
1424 i
= validate_interval_range (src
, &start
, &end
, soft
);
1425 if (NULL_INTERVAL_P (i
))
1428 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
1430 Lisp_Object dest_start
, dest_end
;
1433 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1434 /* Apply this to a copy of pos; it will try to increment its arguments,
1435 which we don't want. */
1436 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1447 e2
= i
->position
+ LENGTH (i
);
1454 while (! NILP (plist
))
1456 if (EQ (Fcar (plist
), prop
))
1458 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1461 plist
= Fcdr (Fcdr (plist
));
1465 /* Must defer modifications to the interval tree in case src
1466 and dest refer to the same string or buffer. */
1467 stuff
= Fcons (Fcons (make_number (p
),
1468 Fcons (make_number (p
+ len
),
1469 Fcons (plist
, Qnil
))),
1473 i
= next_interval (i
);
1474 if (NULL_INTERVAL_P (i
))
1481 GCPRO2 (stuff
, dest
);
1483 while (! NILP (stuff
))
1486 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1487 Fcar (Fcdr (Fcdr (res
))), dest
);
1490 stuff
= Fcdr (stuff
);
1495 return modified
? Qt
: Qnil
;
1498 /* Call the modification hook functions in LIST, each with START and END. */
1501 call_mod_hooks (list
, start
, end
)
1502 Lisp_Object list
, start
, end
;
1504 struct gcpro gcpro1
;
1506 while (!NILP (list
))
1508 call2 (Fcar (list
), start
, end
);
1514 /* Check for read-only intervals and signal an error if we find one.
1515 Then check for any modification hooks in the range START up to
1516 (but not including) END. Create a list of all these hooks in
1517 lexicographic order, eliminating consecutive extra copies of the
1518 same hook. Then call those hooks in order, with START and END - 1
1522 verify_interval_modification (buf
, start
, end
)
1526 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1527 register INTERVAL i
, prev
;
1529 register Lisp_Object prev_mod_hooks
;
1530 Lisp_Object mod_hooks
;
1531 struct gcpro gcpro1
;
1534 prev_mod_hooks
= Qnil
;
1537 interval_insert_behind_hooks
= Qnil
;
1538 interval_insert_in_front_hooks
= Qnil
;
1540 if (NULL_INTERVAL_P (intervals
))
1550 /* For an insert operation, check the two chars around the position. */
1554 Lisp_Object before
, after
;
1556 /* Set I to the interval containing the char after START,
1557 and PREV to the interval containing the char before START.
1558 Either one may be null. They may be equal. */
1559 i
= find_interval (intervals
, start
);
1561 if (start
== BUF_BEGV (buf
))
1563 else if (i
->position
== start
)
1564 prev
= previous_interval (i
);
1565 else if (i
->position
< start
)
1567 if (start
== BUF_ZV (buf
))
1570 /* If Vinhibit_read_only is set and is not a list, we can
1571 skip the read_only checks. */
1572 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
1574 /* If I and PREV differ we need to check for the read-only
1575 property together with its stickiness. If either I or
1576 PREV are 0, this check is all we need.
1577 We have to take special care, since read-only may be
1578 indirectly defined via the category property. */
1581 if (! NULL_INTERVAL_P (i
))
1583 after
= textget (i
->plist
, Qread_only
);
1585 /* If interval I is read-only and read-only is
1586 front-sticky, inhibit insertion.
1587 Check for read-only as well as category. */
1589 && NILP (Fmemq (after
, Vinhibit_read_only
)))
1593 tem
= textget (i
->plist
, Qfront_sticky
);
1594 if (TMEM (Qread_only
, tem
)
1595 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1596 && TMEM (Qcategory
, tem
)))
1597 error ("Attempt to insert within read-only text");
1601 if (! NULL_INTERVAL_P (prev
))
1603 before
= textget (prev
->plist
, Qread_only
);
1605 /* If interval PREV is read-only and read-only isn't
1606 rear-nonsticky, inhibit insertion.
1607 Check for read-only as well as category. */
1609 && NILP (Fmemq (before
, Vinhibit_read_only
)))
1613 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1614 if (! TMEM (Qread_only
, tem
)
1615 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
1616 || ! TMEM (Qcategory
, tem
)))
1617 error ("Attempt to insert within read-only text");
1621 else if (! NULL_INTERVAL_P (i
))
1623 after
= textget (i
->plist
, Qread_only
);
1625 /* If interval I is read-only and read-only is
1626 front-sticky, inhibit insertion.
1627 Check for read-only as well as category. */
1628 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
1632 tem
= textget (i
->plist
, Qfront_sticky
);
1633 if (TMEM (Qread_only
, tem
)
1634 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1635 && TMEM (Qcategory
, tem
)))
1636 error ("Attempt to insert within read-only text");
1638 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1639 if (! TMEM (Qread_only
, tem
)
1640 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
1641 || ! TMEM (Qcategory
, tem
)))
1642 error ("Attempt to insert within read-only text");
1647 /* Run both insert hooks (just once if they're the same). */
1648 if (!NULL_INTERVAL_P (prev
))
1649 interval_insert_behind_hooks
1650 = textget (prev
->plist
, Qinsert_behind_hooks
);
1651 if (!NULL_INTERVAL_P (i
))
1652 interval_insert_in_front_hooks
1653 = textget (i
->plist
, Qinsert_in_front_hooks
);
1657 /* Loop over intervals on or next to START...END,
1658 collecting their hooks. */
1660 i
= find_interval (intervals
, start
);
1663 if (! INTERVAL_WRITABLE_P (i
))
1664 error ("Attempt to modify read-only text");
1666 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
1667 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
1669 hooks
= Fcons (mod_hooks
, hooks
);
1670 prev_mod_hooks
= mod_hooks
;
1673 i
= next_interval (i
);
1675 /* Keep going thru the interval containing the char before END. */
1676 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
1679 hooks
= Fnreverse (hooks
);
1680 while (! EQ (hooks
, Qnil
))
1682 call_mod_hooks (Fcar (hooks
), make_number (start
),
1684 hooks
= Fcdr (hooks
);
1690 /* Run the interval hooks for an insertion.
1691 verify_interval_modification chose which hooks to run;
1692 this function is called after the insertion happens
1693 so it can indicate the range of inserted text. */
1696 report_interval_modification (start
, end
)
1697 Lisp_Object start
, end
;
1699 if (! NILP (interval_insert_behind_hooks
))
1700 call_mod_hooks (interval_insert_behind_hooks
,
1701 make_number (start
), make_number (end
));
1702 if (! NILP (interval_insert_in_front_hooks
)
1703 && ! EQ (interval_insert_in_front_hooks
,
1704 interval_insert_behind_hooks
))
1705 call_mod_hooks (interval_insert_in_front_hooks
,
1706 make_number (start
), make_number (end
));
1712 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
1713 "Property-list used as default values.\n\
1714 The value of a property in this list is seen as the value for every\n\
1715 character that does not have its own value for that property.");
1716 Vdefault_text_properties
= Qnil
;
1718 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
1719 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1720 This also inhibits the use of the `intangible' text property.");
1721 Vinhibit_point_motion_hooks
= Qnil
;
1723 staticpro (&interval_insert_behind_hooks
);
1724 staticpro (&interval_insert_in_front_hooks
);
1725 interval_insert_behind_hooks
= Qnil
;
1726 interval_insert_in_front_hooks
= Qnil
;
1729 /* Common attributes one might give text */
1731 staticpro (&Qforeground
);
1732 Qforeground
= intern ("foreground");
1733 staticpro (&Qbackground
);
1734 Qbackground
= intern ("background");
1736 Qfont
= intern ("font");
1737 staticpro (&Qstipple
);
1738 Qstipple
= intern ("stipple");
1739 staticpro (&Qunderline
);
1740 Qunderline
= intern ("underline");
1741 staticpro (&Qread_only
);
1742 Qread_only
= intern ("read-only");
1743 staticpro (&Qinvisible
);
1744 Qinvisible
= intern ("invisible");
1745 staticpro (&Qintangible
);
1746 Qintangible
= intern ("intangible");
1747 staticpro (&Qcategory
);
1748 Qcategory
= intern ("category");
1749 staticpro (&Qlocal_map
);
1750 Qlocal_map
= intern ("local-map");
1751 staticpro (&Qfront_sticky
);
1752 Qfront_sticky
= intern ("front-sticky");
1753 staticpro (&Qrear_nonsticky
);
1754 Qrear_nonsticky
= intern ("rear-nonsticky");
1756 /* Properties that text might use to specify certain actions */
1758 staticpro (&Qmouse_left
);
1759 Qmouse_left
= intern ("mouse-left");
1760 staticpro (&Qmouse_entered
);
1761 Qmouse_entered
= intern ("mouse-entered");
1762 staticpro (&Qpoint_left
);
1763 Qpoint_left
= intern ("point-left");
1764 staticpro (&Qpoint_entered
);
1765 Qpoint_entered
= intern ("point-entered");
1767 defsubr (&Stext_properties_at
);
1768 defsubr (&Sget_text_property
);
1769 defsubr (&Sget_char_property
);
1770 defsubr (&Snext_char_property_change
);
1771 defsubr (&Sprevious_char_property_change
);
1772 defsubr (&Snext_property_change
);
1773 defsubr (&Snext_single_property_change
);
1774 defsubr (&Sprevious_property_change
);
1775 defsubr (&Sprevious_single_property_change
);
1776 defsubr (&Sadd_text_properties
);
1777 defsubr (&Sput_text_property
);
1778 defsubr (&Sset_text_properties
);
1779 defsubr (&Sremove_text_properties
);
1780 defsubr (&Stext_property_any
);
1781 defsubr (&Stext_property_not_all
);
1782 /* defsubr (&Serase_text_properties); */
1783 /* defsubr (&Scopy_text_properties); */
1788 lose
-- this shouldn
't be compiled if USE_TEXT_PROPERTIES isn't defined
1790 #endif /* USE_TEXT_PROPERTIES */