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 modify_region (XBUFFER (object
),
303 make_number (interval
->position
),
304 make_number (interval
->position
+ LENGTH (interval
)));
305 record_property_change (interval
->position
, LENGTH (interval
),
306 XCONS (sym
)->car
, XCONS (value
)->car
,
308 signal_after_change (interval
->position
, LENGTH (interval
),
312 /* For each new property that has no value at all in the old plist,
313 make an undo record binding it to nil, so it will be removed. */
314 for (sym
= properties
;
315 PLIST_ELT_P (sym
, value
);
316 sym
= XCONS (value
)->cdr
)
317 if (EQ (property_value (interval
->plist
, XCONS (sym
)->car
), Qunbound
))
319 modify_region (XBUFFER (object
),
320 make_number (interval
->position
),
321 make_number (interval
->position
+ LENGTH (interval
)));
322 record_property_change (interval
->position
, LENGTH (interval
),
323 XCONS (sym
)->car
, Qnil
,
325 signal_after_change (interval
->position
, LENGTH (interval
),
330 /* Store new properties. */
331 interval
->plist
= Fcopy_sequence (properties
);
334 /* Add the properties of PLIST to the interval I, or set
335 the value of I's property to the value of the property on PLIST
336 if they are different.
338 OBJECT should be the string or buffer the interval is in.
340 Return nonzero if this changes I (i.e., if any members of PLIST
341 are actually added to I's plist) */
344 add_properties (plist
, i
, object
)
349 Lisp_Object tail1
, tail2
, sym1
, val1
;
350 register int changed
= 0;
352 struct gcpro gcpro1
, gcpro2
, gcpro3
;
357 /* No need to protect OBJECT, because we can GC only in the case
358 where it is a buffer, and live buffers are always protected.
359 I and its plist are also protected, via OBJECT. */
360 GCPRO3 (tail1
, sym1
, val1
);
362 /* Go through each element of PLIST. */
363 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
366 val1
= Fcar (Fcdr (tail1
));
369 /* Go through I's plist, looking for sym1 */
370 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
371 if (EQ (sym1
, Fcar (tail2
)))
373 /* No need to gcpro, because tail2 protects this
374 and it must be a cons cell (we get an error otherwise). */
375 register Lisp_Object this_cdr
;
377 this_cdr
= Fcdr (tail2
);
378 /* Found the property. Now check its value. */
381 /* The properties have the same value on both lists.
382 Continue to the next property. */
383 if (EQ (val1
, Fcar (this_cdr
)))
386 /* Record this change in the buffer, for undo purposes. */
387 if (BUFFERP (object
))
389 modify_region (XBUFFER (object
),
390 make_number (i
->position
),
391 make_number (i
->position
+ LENGTH (i
)));
392 record_property_change (i
->position
, LENGTH (i
),
393 sym1
, Fcar (this_cdr
), object
);
394 signal_after_change (i
->position
, LENGTH (i
), LENGTH (i
));
397 /* I's property has a different value -- change it */
398 Fsetcar (this_cdr
, val1
);
405 /* Record this change in the buffer, for undo purposes. */
406 if (BUFFERP (object
))
408 modify_region (XBUFFER (object
),
409 make_number (i
->position
),
410 make_number (i
->position
+ LENGTH (i
)));
411 record_property_change (i
->position
, LENGTH (i
),
413 signal_after_change (i
->position
, LENGTH (i
), LENGTH (i
));
415 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
425 /* For any members of PLIST which are properties of I, remove them
427 OBJECT is the string or buffer containing I. */
430 remove_properties (plist
, i
, object
)
435 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
436 register int changed
= 0;
438 current_plist
= i
->plist
;
439 /* Go through each element of plist. */
440 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
444 /* First, remove the symbol if its at the head of the list */
445 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
447 if (BUFFERP (object
))
449 modify_region (XBUFFER (object
),
450 make_number (i
->position
),
451 make_number (i
->position
+ LENGTH (i
)));
452 record_property_change (i
->position
, LENGTH (i
),
453 sym
, Fcar (Fcdr (current_plist
)),
455 signal_after_change (i
->position
, LENGTH (i
), LENGTH (i
));
458 current_plist
= Fcdr (Fcdr (current_plist
));
462 /* Go through i's plist, looking for sym */
463 tail2
= current_plist
;
464 while (! NILP (tail2
))
466 register Lisp_Object
this;
467 this = Fcdr (Fcdr (tail2
));
468 if (EQ (sym
, Fcar (this)))
470 if (BUFFERP (object
))
472 modify_region (XBUFFER (object
),
473 make_number (i
->position
),
474 make_number (i
->position
+ LENGTH (i
)));
475 record_property_change (i
->position
, LENGTH (i
),
476 sym
, Fcar (Fcdr (this)), object
);
477 signal_after_change (i
->position
, LENGTH (i
), LENGTH (i
));
480 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
488 i
->plist
= current_plist
;
493 /* Remove all properties from interval I. Return non-zero
494 if this changes the interval. */
508 DEFUN ("text-properties-at", Ftext_properties_at
,
509 Stext_properties_at
, 1, 2, 0,
510 "Return the list of properties held by the character at POSITION\n\
511 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
512 defaults to the current buffer.\n\
513 If POSITION is at the end of OBJECT, the value is nil.")
515 Lisp_Object position
, object
;
520 XSETBUFFER (object
, current_buffer
);
522 i
= validate_interval_range (object
, &position
, &position
, soft
);
523 if (NULL_INTERVAL_P (i
))
525 /* If POSITION is at the end of the interval,
526 it means it's the end of OBJECT.
527 There are no properties at the very end,
528 since no character follows. */
529 if (XINT (position
) == LENGTH (i
) + i
->position
)
535 DEFUN ("get-text-property", Fget_text_property
, Sget_text_property
, 2, 3, 0,
536 "Return the value of POSITION's property PROP, in OBJECT.\n\
537 OBJECT is optional and defaults to the current buffer.\n\
538 If POSITION is at the end of OBJECT, the value is nil.")
539 (position
, prop
, object
)
540 Lisp_Object position
, object
;
543 return textget (Ftext_properties_at (position
, object
), prop
);
546 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
547 "Return the value of POSITION's property PROP, in OBJECT.\n\
548 OBJECT is optional and defaults to the current buffer.\n\
549 If POSITION is at the end of OBJECT, the value is nil.\n\
550 If OBJECT is a buffer, then overlay properties are considered as well as\n\
552 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
553 overlays are considered only if they are associated with OBJECT.")
554 (position
, prop
, object
)
555 Lisp_Object position
, object
;
556 register Lisp_Object prop
;
558 struct window
*w
= 0;
560 CHECK_NUMBER_COERCE_MARKER (position
, 0);
563 XSETBUFFER (object
, current_buffer
);
565 if (WINDOWP (object
))
567 w
= XWINDOW (object
);
570 if (BUFFERP (object
))
572 int posn
= XINT (position
);
574 Lisp_Object
*overlay_vec
, tem
;
577 struct buffer
*obuf
= current_buffer
;
579 set_buffer_temp (XBUFFER (object
));
581 /* First try with room for 40 overlays. */
583 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
585 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
586 &next_overlay
, NULL
);
588 /* If there are more than 40,
589 make enough space for all, and try again. */
593 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
594 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
595 &next_overlay
, NULL
);
597 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
599 set_buffer_temp (obuf
);
601 /* Now check the overlays in order of decreasing priority. */
602 while (--noverlays
>= 0)
604 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
609 /* Not a buffer, or no appropriate overlay, so fall through to the
611 return (Fget_text_property (position
, prop
, object
));
614 DEFUN ("next-property-change", Fnext_property_change
,
615 Snext_property_change
, 1, 3, 0,
616 "Return the position of next property change.\n\
617 Scans characters forward from POSITION in OBJECT till it finds\n\
618 a change in some text property, then returns the position of the change.\n\
619 The optional second argument OBJECT is the string or buffer to scan.\n\
620 Return nil if the property is constant all the way to the end of OBJECT.\n\
621 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
622 If the optional third argument LIMIT is non-nil, don't search\n\
623 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
624 (position
, object
, limit
)
625 Lisp_Object position
, object
, limit
;
627 register INTERVAL i
, next
;
630 XSETBUFFER (object
, current_buffer
);
632 if (! NILP (limit
) && ! EQ (limit
, Qt
))
633 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
635 i
= validate_interval_range (object
, &position
, &position
, soft
);
637 /* If LIMIT is t, return start of next interval--don't
638 bother checking further intervals. */
641 if (NULL_INTERVAL_P (i
))
644 next
= next_interval (i
);
646 if (NULL_INTERVAL_P (next
))
647 XSETFASTINT (position
, (STRINGP (object
)
648 ? XSTRING (object
)->size
649 : BUF_ZV (XBUFFER (object
))));
651 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
655 if (NULL_INTERVAL_P (i
))
658 next
= next_interval (i
);
660 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
661 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
662 next
= next_interval (next
);
664 if (NULL_INTERVAL_P (next
))
666 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
669 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
673 /* Return 1 if there's a change in some property between BEG and END. */
676 property_change_between_p (beg
, end
)
679 register INTERVAL i
, next
;
680 Lisp_Object object
, pos
;
682 XSETBUFFER (object
, current_buffer
);
683 XSETFASTINT (pos
, beg
);
685 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
686 if (NULL_INTERVAL_P (i
))
689 next
= next_interval (i
);
690 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
692 next
= next_interval (next
);
693 if (NULL_INTERVAL_P (next
))
695 if (next
->position
>= end
)
699 if (NULL_INTERVAL_P (next
))
705 DEFUN ("next-single-property-change", Fnext_single_property_change
,
706 Snext_single_property_change
, 2, 4, 0,
707 "Return the position of next property change for a specific property.\n\
708 Scans characters forward from POSITION till it finds\n\
709 a change in the PROP property, then returns the position of the change.\n\
710 The optional third argument OBJECT is the string or buffer to scan.\n\
711 The property values are compared with `eq'.\n\
712 Return nil if the property is constant all the way to the end of OBJECT.\n\
713 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
714 If the optional fourth argument LIMIT is non-nil, don't search\n\
715 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
716 (position
, prop
, object
, limit
)
717 Lisp_Object position
, prop
, object
, limit
;
719 register INTERVAL i
, next
;
720 register Lisp_Object here_val
;
723 XSETBUFFER (object
, current_buffer
);
726 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
728 i
= validate_interval_range (object
, &position
, &position
, soft
);
729 if (NULL_INTERVAL_P (i
))
732 here_val
= textget (i
->plist
, prop
);
733 next
= next_interval (i
);
734 while (! NULL_INTERVAL_P (next
)
735 && EQ (here_val
, textget (next
->plist
, prop
))
736 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
737 next
= next_interval (next
);
739 if (NULL_INTERVAL_P (next
))
741 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
744 XSETFASTINT (position
, next
->position
- (STRINGP (object
)));
748 DEFUN ("previous-property-change", Fprevious_property_change
,
749 Sprevious_property_change
, 1, 3, 0,
750 "Return the position of previous property change.\n\
751 Scans characters backwards from POSITION in OBJECT till it finds\n\
752 a change in some text property, then returns the position of the change.\n\
753 The optional second argument OBJECT is the string or buffer to scan.\n\
754 Return nil if the property is constant all the way to the start of OBJECT.\n\
755 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
756 If the optional third argument LIMIT is non-nil, don't search\n\
757 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
758 (position
, object
, limit
)
759 Lisp_Object position
, object
, limit
;
761 register INTERVAL i
, previous
;
764 XSETBUFFER (object
, current_buffer
);
767 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
769 i
= validate_interval_range (object
, &position
, &position
, soft
);
770 if (NULL_INTERVAL_P (i
))
773 /* Start with the interval containing the char before point. */
774 if (i
->position
== XFASTINT (position
))
775 i
= previous_interval (i
);
777 previous
= previous_interval (i
);
778 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
780 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
781 previous
= previous_interval (previous
);
782 if (NULL_INTERVAL_P (previous
))
785 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
788 XSETFASTINT (position
, (previous
->position
+ LENGTH (previous
)
789 - (STRINGP (object
))));
793 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
794 Sprevious_single_property_change
, 2, 4, 0,
795 "Return the position of previous property change for a specific property.\n\
796 Scans characters backward from POSITION till it finds\n\
797 a change in the PROP property, then returns the position of the change.\n\
798 The optional third argument OBJECT is the string or buffer to scan.\n\
799 The property values are compared with `eq'.\n\
800 Return nil if the property is constant all the way to the start of OBJECT.\n\
801 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
802 If the optional fourth argument LIMIT is non-nil, don't search\n\
803 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
804 (position
, prop
, object
, limit
)
805 Lisp_Object position
, prop
, object
, limit
;
807 register INTERVAL i
, previous
;
808 register Lisp_Object here_val
;
811 XSETBUFFER (object
, current_buffer
);
814 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
816 i
= validate_interval_range (object
, &position
, &position
, soft
);
818 /* Start with the interval containing the char before point. */
819 if (! NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
820 i
= previous_interval (i
);
822 if (NULL_INTERVAL_P (i
))
825 here_val
= textget (i
->plist
, prop
);
826 previous
= previous_interval (i
);
827 while (! NULL_INTERVAL_P (previous
)
828 && EQ (here_val
, textget (previous
->plist
, prop
))
830 || previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
831 previous
= previous_interval (previous
);
832 if (NULL_INTERVAL_P (previous
))
835 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
838 XSETFASTINT (position
, (previous
->position
+ LENGTH (previous
)
839 - (STRINGP (object
))));
843 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
845 DEFUN ("add-text-properties", Fadd_text_properties
,
846 Sadd_text_properties
, 3, 4, 0,
847 "Add properties to the text from START to END.\n\
848 The third argument PROPERTIES is a property list\n\
849 specifying the property values to add.\n\
850 The optional fourth argument, OBJECT,\n\
851 is the string or buffer containing the text.\n\
852 Return t if any property value actually changed, nil otherwise.")
853 (start
, end
, properties
, object
)
854 Lisp_Object start
, end
, properties
, object
;
856 register INTERVAL i
, unchanged
;
857 register int s
, len
, modified
= 0;
860 properties
= validate_plist (properties
);
861 if (NILP (properties
))
865 XSETBUFFER (object
, current_buffer
);
867 i
= validate_interval_range (object
, &start
, &end
, hard
);
868 if (NULL_INTERVAL_P (i
))
872 len
= XINT (end
) - s
;
874 /* No need to protect OBJECT, because we GC only if it's a buffer,
875 and live buffers are always protected. */
878 /* If we're not starting on an interval boundary, we have to
879 split this interval. */
880 if (i
->position
!= s
)
882 /* If this interval already has the properties, we can
884 if (interval_has_all_properties (properties
, i
))
886 int got
= (LENGTH (i
) - (s
- i
->position
));
888 RETURN_UNGCPRO (Qnil
);
890 i
= next_interval (i
);
895 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
896 copy_properties (unchanged
, i
);
900 /* We are at the beginning of interval I, with LEN chars to scan. */
906 if (LENGTH (i
) >= len
)
908 /* We can UNGCPRO safely here, because there will be just
909 one more chance to gc, in the next call to add_properties,
910 and after that we will not need PROPERTIES or OBJECT again. */
913 if (interval_has_all_properties (properties
, i
))
914 return modified
? Qt
: Qnil
;
916 if (LENGTH (i
) == len
)
918 add_properties (properties
, i
, object
);
922 /* i doesn't have the properties, and goes past the change limit */
924 i
= split_interval_left (unchanged
, len
);
925 copy_properties (unchanged
, i
);
926 add_properties (properties
, i
, object
);
931 modified
+= add_properties (properties
, i
, object
);
932 i
= next_interval (i
);
936 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
938 DEFUN ("put-text-property", Fput_text_property
,
939 Sput_text_property
, 4, 5, 0,
940 "Set one property of the text from START to END.\n\
941 The third and fourth arguments PROPERTY and VALUE\n\
942 specify the property to add.\n\
943 The optional fifth argument, OBJECT,\n\
944 is the string or buffer containing the text.")
945 (start
, end
, property
, value
, object
)
946 Lisp_Object start
, end
, property
, value
, object
;
948 Fadd_text_properties (start
, end
,
949 Fcons (property
, Fcons (value
, Qnil
)),
954 DEFUN ("set-text-properties", Fset_text_properties
,
955 Sset_text_properties
, 3, 4, 0,
956 "Completely replace properties of text from START to END.\n\
957 The third argument PROPERTIES is the new property list.\n\
958 The optional fourth argument, OBJECT,\n\
959 is the string or buffer containing the text.")
960 (start
, end
, properties
, object
)
961 Lisp_Object start
, end
, properties
, object
;
963 register INTERVAL i
, unchanged
;
964 register INTERVAL prev_changed
= NULL_INTERVAL
;
966 Lisp_Object ostart
, oend
;
971 properties
= validate_plist (properties
);
974 XSETBUFFER (object
, current_buffer
);
976 /* If we want no properties for a whole string,
977 get rid of its intervals. */
978 if (NILP (properties
) && STRINGP (object
)
979 && XFASTINT (start
) == 0
980 && XFASTINT (end
) == XSTRING (object
)->size
)
982 XSTRING (object
)->intervals
= 0;
986 i
= validate_interval_range (object
, &start
, &end
, soft
);
988 if (NULL_INTERVAL_P (i
))
990 /* If buffer has no properties, and we want none, return now. */
991 if (NILP (properties
))
994 /* Restore the original START and END values
995 because validate_interval_range increments them for strings. */
999 i
= validate_interval_range (object
, &start
, &end
, hard
);
1000 /* This can return if start == end. */
1001 if (NULL_INTERVAL_P (i
))
1006 len
= XINT (end
) - s
;
1008 if (i
->position
!= s
)
1011 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1013 if (LENGTH (i
) > len
)
1015 copy_properties (unchanged
, i
);
1016 i
= split_interval_left (i
, len
);
1017 set_properties (properties
, i
, object
);
1021 set_properties (properties
, i
, object
);
1023 if (LENGTH (i
) == len
)
1028 i
= next_interval (i
);
1031 /* We are starting at the beginning of an interval, I */
1037 if (LENGTH (i
) >= len
)
1039 if (LENGTH (i
) > len
)
1040 i
= split_interval_left (i
, len
);
1042 /* We have to call set_properties even if we are going to
1043 merge the intervals, so as to make the undo records
1044 and cause redisplay to happen. */
1045 set_properties (properties
, i
, object
);
1046 if (!NULL_INTERVAL_P (prev_changed
))
1047 merge_interval_left (i
);
1053 /* We have to call set_properties even if we are going to
1054 merge the intervals, so as to make the undo records
1055 and cause redisplay to happen. */
1056 set_properties (properties
, i
, object
);
1057 if (NULL_INTERVAL_P (prev_changed
))
1060 prev_changed
= i
= merge_interval_left (i
);
1062 i
= next_interval (i
);
1068 DEFUN ("remove-text-properties", Fremove_text_properties
,
1069 Sremove_text_properties
, 3, 4, 0,
1070 "Remove some properties from text from START to END.\n\
1071 The third argument PROPERTIES is a property list\n\
1072 whose property names specify the properties to remove.\n\
1073 \(The values stored in PROPERTIES are ignored.)\n\
1074 The optional fourth argument, OBJECT,\n\
1075 is the string or buffer containing the text.\n\
1076 Return t if any property was actually removed, nil otherwise.")
1077 (start
, end
, properties
, object
)
1078 Lisp_Object start
, end
, properties
, object
;
1080 register INTERVAL i
, unchanged
;
1081 register int s
, len
, modified
= 0;
1084 XSETBUFFER (object
, current_buffer
);
1086 i
= validate_interval_range (object
, &start
, &end
, soft
);
1087 if (NULL_INTERVAL_P (i
))
1091 len
= XINT (end
) - s
;
1093 if (i
->position
!= s
)
1095 /* No properties on this first interval -- return if
1096 it covers the entire region. */
1097 if (! interval_has_some_properties (properties
, i
))
1099 int got
= (LENGTH (i
) - (s
- i
->position
));
1103 i
= next_interval (i
);
1105 /* Split away the beginning of this interval; what we don't
1110 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1111 copy_properties (unchanged
, i
);
1115 /* We are at the beginning of an interval, with len to scan */
1121 if (LENGTH (i
) >= len
)
1123 if (! interval_has_some_properties (properties
, i
))
1124 return modified
? Qt
: Qnil
;
1126 if (LENGTH (i
) == len
)
1128 remove_properties (properties
, i
, object
);
1132 /* i has the properties, and goes past the change limit */
1134 i
= split_interval_left (i
, len
);
1135 copy_properties (unchanged
, i
);
1136 remove_properties (properties
, i
, object
);
1141 modified
+= remove_properties (properties
, i
, object
);
1142 i
= next_interval (i
);
1146 DEFUN ("text-property-any", Ftext_property_any
,
1147 Stext_property_any
, 4, 5, 0,
1148 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1149 If so, return the position of the first character whose property PROPERTY\n\
1150 is `eq' to VALUE. Otherwise return nil.\n\
1151 The optional fifth argument, OBJECT, is the string or buffer\n\
1152 containing the text.")
1153 (start
, end
, property
, value
, object
)
1154 Lisp_Object start
, end
, property
, value
, object
;
1156 register INTERVAL i
;
1157 register int e
, pos
;
1160 XSETBUFFER (object
, current_buffer
);
1161 i
= validate_interval_range (object
, &start
, &end
, soft
);
1162 if (NULL_INTERVAL_P (i
))
1163 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1166 while (! NULL_INTERVAL_P (i
))
1168 if (i
->position
>= e
)
1170 if (EQ (textget (i
->plist
, property
), value
))
1173 if (pos
< XINT (start
))
1175 return make_number (pos
- (STRINGP (object
)));
1177 i
= next_interval (i
);
1182 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1183 Stext_property_not_all
, 4, 5, 0,
1184 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1185 If so, return the position of the first character whose property PROPERTY\n\
1186 is not `eq' to VALUE. Otherwise, return nil.\n\
1187 The optional fifth argument, OBJECT, is the string or buffer\n\
1188 containing the text.")
1189 (start
, end
, property
, value
, object
)
1190 Lisp_Object start
, end
, property
, value
, object
;
1192 register INTERVAL i
;
1196 XSETBUFFER (object
, current_buffer
);
1197 i
= validate_interval_range (object
, &start
, &end
, soft
);
1198 if (NULL_INTERVAL_P (i
))
1199 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1203 while (! NULL_INTERVAL_P (i
))
1205 if (i
->position
>= e
)
1207 if (! EQ (textget (i
->plist
, property
), value
))
1209 if (i
->position
> s
)
1211 return make_number (s
- (STRINGP (object
)));
1213 i
= next_interval (i
);
1218 #if 0 /* You can use set-text-properties for this. */
1220 DEFUN ("erase-text-properties", Ferase_text_properties
,
1221 Serase_text_properties
, 2, 3, 0,
1222 "Remove all properties from the text from START to END.\n\
1223 The optional third argument, OBJECT,\n\
1224 is the string or buffer containing the text.")
1225 (start
, end
, object
)
1226 Lisp_Object start
, end
, object
;
1228 register INTERVAL i
;
1229 register INTERVAL prev_changed
= NULL_INTERVAL
;
1230 register int s
, len
, modified
;
1233 XSETBUFFER (object
, current_buffer
);
1235 i
= validate_interval_range (object
, &start
, &end
, soft
);
1236 if (NULL_INTERVAL_P (i
))
1240 len
= XINT (end
) - s
;
1242 if (i
->position
!= s
)
1245 register INTERVAL unchanged
= i
;
1247 /* If there are properties here, then this text will be modified. */
1248 if (! NILP (i
->plist
))
1250 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1254 if (LENGTH (i
) > len
)
1256 i
= split_interval_right (i
, len
);
1257 copy_properties (unchanged
, i
);
1261 if (LENGTH (i
) == len
)
1266 /* If the text of I is without any properties, and contains
1267 LEN or more characters, then we may return without changing
1269 else if (LENGTH (i
) - (s
- i
->position
) <= len
)
1271 /* The amount of text to change extends past I, so just note
1272 how much we've gotten. */
1274 got
= LENGTH (i
) - (s
- i
->position
);
1278 i
= next_interval (i
);
1281 /* We are starting at the beginning of an interval, I. */
1284 if (LENGTH (i
) >= len
)
1286 /* If I has no properties, simply merge it if possible. */
1287 if (NILP (i
->plist
))
1289 if (! NULL_INTERVAL_P (prev_changed
))
1290 merge_interval_left (i
);
1292 return modified
? Qt
: Qnil
;
1295 if (LENGTH (i
) > len
)
1296 i
= split_interval_left (i
, len
);
1297 if (! NULL_INTERVAL_P (prev_changed
))
1298 merge_interval_left (i
);
1305 /* Here if we still need to erase past the end of I */
1307 if (NULL_INTERVAL_P (prev_changed
))
1309 modified
+= erase_properties (i
);
1314 modified
+= ! NILP (i
->plist
);
1315 /* Merging I will give it the properties of PREV_CHANGED. */
1316 prev_changed
= i
= merge_interval_left (i
);
1319 i
= next_interval (i
);
1322 return modified
? Qt
: Qnil
;
1326 /* I don't think this is the right interface to export; how often do you
1327 want to do something like this, other than when you're copying objects
1330 I think it would be better to have a pair of functions, one which
1331 returns the text properties of a region as a list of ranges and
1332 plists, and another which applies such a list to another object. */
1334 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1335 SRC and DEST may each refer to strings or buffers.
1336 Optional sixth argument PROP causes only that property to be copied.
1337 Properties are copied to DEST as if by `add-text-properties'.
1338 Return t if any property value actually changed, nil otherwise. */
1340 /* Note this can GC when DEST is a buffer. */
1343 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1344 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1350 int s
, e
, e2
, p
, len
, modified
= 0;
1351 struct gcpro gcpro1
, gcpro2
;
1353 i
= validate_interval_range (src
, &start
, &end
, soft
);
1354 if (NULL_INTERVAL_P (i
))
1357 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
1359 Lisp_Object dest_start
, dest_end
;
1362 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1363 /* Apply this to a copy of pos; it will try to increment its arguments,
1364 which we don't want. */
1365 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1376 e2
= i
->position
+ LENGTH (i
);
1383 while (! NILP (plist
))
1385 if (EQ (Fcar (plist
), prop
))
1387 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1390 plist
= Fcdr (Fcdr (plist
));
1394 /* Must defer modifications to the interval tree in case src
1395 and dest refer to the same string or buffer. */
1396 stuff
= Fcons (Fcons (make_number (p
),
1397 Fcons (make_number (p
+ len
),
1398 Fcons (plist
, Qnil
))),
1402 i
= next_interval (i
);
1403 if (NULL_INTERVAL_P (i
))
1410 GCPRO2 (stuff
, dest
);
1412 while (! NILP (stuff
))
1415 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1416 Fcar (Fcdr (Fcdr (res
))), dest
);
1419 stuff
= Fcdr (stuff
);
1424 return modified
? Qt
: Qnil
;
1427 /* Call the modification hook functions in LIST, each with START and END. */
1430 call_mod_hooks (list
, start
, end
)
1431 Lisp_Object list
, start
, end
;
1433 struct gcpro gcpro1
;
1435 while (!NILP (list
))
1437 call2 (Fcar (list
), start
, end
);
1443 /* Check for read-only intervals and signal an error if we find one.
1444 Then check for any modification hooks in the range START up to
1445 (but not including) END. Create a list of all these hooks in
1446 lexicographic order, eliminating consecutive extra copies of the
1447 same hook. Then call those hooks in order, with START and END - 1
1451 verify_interval_modification (buf
, start
, end
)
1455 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1456 register INTERVAL i
, prev
;
1458 register Lisp_Object prev_mod_hooks
;
1459 Lisp_Object mod_hooks
;
1460 struct gcpro gcpro1
;
1463 prev_mod_hooks
= Qnil
;
1466 interval_insert_behind_hooks
= Qnil
;
1467 interval_insert_in_front_hooks
= Qnil
;
1469 if (NULL_INTERVAL_P (intervals
))
1479 /* For an insert operation, check the two chars around the position. */
1483 Lisp_Object before
, after
;
1485 /* Set I to the interval containing the char after START,
1486 and PREV to the interval containing the char before START.
1487 Either one may be null. They may be equal. */
1488 i
= find_interval (intervals
, start
);
1490 if (start
== BUF_BEGV (buf
))
1492 else if (i
->position
== start
)
1493 prev
= previous_interval (i
);
1494 else if (i
->position
< start
)
1496 if (start
== BUF_ZV (buf
))
1499 /* If Vinhibit_read_only is set and is not a list, we can
1500 skip the read_only checks. */
1501 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
1503 /* If I and PREV differ we need to check for the read-only
1504 property together with its stickiness. If either I or
1505 PREV are 0, this check is all we need.
1506 We have to take special care, since read-only may be
1507 indirectly defined via the category property. */
1510 if (! NULL_INTERVAL_P (i
))
1512 after
= textget (i
->plist
, Qread_only
);
1514 /* If interval I is read-only and read-only is
1515 front-sticky, inhibit insertion.
1516 Check for read-only as well as category. */
1518 && NILP (Fmemq (after
, Vinhibit_read_only
)))
1522 tem
= textget (i
->plist
, Qfront_sticky
);
1523 if (TMEM (Qread_only
, tem
)
1524 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1525 && TMEM (Qcategory
, tem
)))
1526 error ("Attempt to insert within read-only text");
1530 if (! NULL_INTERVAL_P (prev
))
1532 before
= textget (prev
->plist
, Qread_only
);
1534 /* If interval PREV is read-only and read-only isn't
1535 rear-nonsticky, inhibit insertion.
1536 Check for read-only as well as category. */
1538 && NILP (Fmemq (before
, Vinhibit_read_only
)))
1542 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1543 if (! TMEM (Qread_only
, tem
)
1544 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
1545 || ! TMEM (Qcategory
, tem
)))
1546 error ("Attempt to insert within read-only text");
1550 else if (! NULL_INTERVAL_P (i
))
1552 after
= textget (i
->plist
, Qread_only
);
1554 /* If interval I is read-only and read-only is
1555 front-sticky, inhibit insertion.
1556 Check for read-only as well as category. */
1557 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
1561 tem
= textget (i
->plist
, Qfront_sticky
);
1562 if (TMEM (Qread_only
, tem
)
1563 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1564 && TMEM (Qcategory
, tem
)))
1565 error ("Attempt to insert within read-only text");
1567 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1568 if (! TMEM (Qread_only
, tem
)
1569 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
1570 || ! TMEM (Qcategory
, tem
)))
1571 error ("Attempt to insert within read-only text");
1576 /* Run both insert hooks (just once if they're the same). */
1577 if (!NULL_INTERVAL_P (prev
))
1578 interval_insert_behind_hooks
1579 = textget (prev
->plist
, Qinsert_behind_hooks
);
1580 if (!NULL_INTERVAL_P (i
))
1581 interval_insert_in_front_hooks
1582 = textget (i
->plist
, Qinsert_in_front_hooks
);
1586 /* Loop over intervals on or next to START...END,
1587 collecting their hooks. */
1589 i
= find_interval (intervals
, start
);
1592 if (! INTERVAL_WRITABLE_P (i
))
1593 error ("Attempt to modify read-only text");
1595 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
1596 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
1598 hooks
= Fcons (mod_hooks
, hooks
);
1599 prev_mod_hooks
= mod_hooks
;
1602 i
= next_interval (i
);
1604 /* Keep going thru the interval containing the char before END. */
1605 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
1608 hooks
= Fnreverse (hooks
);
1609 while (! EQ (hooks
, Qnil
))
1611 call_mod_hooks (Fcar (hooks
), make_number (start
),
1613 hooks
= Fcdr (hooks
);
1619 /* Run the interval hooks for an insertion.
1620 verify_interval_modification chose which hooks to run;
1621 this function is called after the insertion happens
1622 so it can indicate the range of inserted text. */
1625 report_interval_modification (start
, end
)
1626 Lisp_Object start
, end
;
1628 if (! NILP (interval_insert_behind_hooks
))
1629 call_mod_hooks (interval_insert_behind_hooks
,
1630 make_number (start
), make_number (end
));
1631 if (! NILP (interval_insert_in_front_hooks
)
1632 && ! EQ (interval_insert_in_front_hooks
,
1633 interval_insert_behind_hooks
))
1634 call_mod_hooks (interval_insert_in_front_hooks
,
1635 make_number (start
), make_number (end
));
1641 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
1642 "Property-list used as default values.\n\
1643 The value of a property in this list is seen as the value for every\n\
1644 character that does not have its own value for that property.");
1645 Vdefault_text_properties
= Qnil
;
1647 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
1648 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1649 This also inhibits the use of the `intangible' text property.");
1650 Vinhibit_point_motion_hooks
= Qnil
;
1652 staticpro (&interval_insert_behind_hooks
);
1653 staticpro (&interval_insert_in_front_hooks
);
1654 interval_insert_behind_hooks
= Qnil
;
1655 interval_insert_in_front_hooks
= Qnil
;
1658 /* Common attributes one might give text */
1660 staticpro (&Qforeground
);
1661 Qforeground
= intern ("foreground");
1662 staticpro (&Qbackground
);
1663 Qbackground
= intern ("background");
1665 Qfont
= intern ("font");
1666 staticpro (&Qstipple
);
1667 Qstipple
= intern ("stipple");
1668 staticpro (&Qunderline
);
1669 Qunderline
= intern ("underline");
1670 staticpro (&Qread_only
);
1671 Qread_only
= intern ("read-only");
1672 staticpro (&Qinvisible
);
1673 Qinvisible
= intern ("invisible");
1674 staticpro (&Qintangible
);
1675 Qintangible
= intern ("intangible");
1676 staticpro (&Qcategory
);
1677 Qcategory
= intern ("category");
1678 staticpro (&Qlocal_map
);
1679 Qlocal_map
= intern ("local-map");
1680 staticpro (&Qfront_sticky
);
1681 Qfront_sticky
= intern ("front-sticky");
1682 staticpro (&Qrear_nonsticky
);
1683 Qrear_nonsticky
= intern ("rear-nonsticky");
1685 /* Properties that text might use to specify certain actions */
1687 staticpro (&Qmouse_left
);
1688 Qmouse_left
= intern ("mouse-left");
1689 staticpro (&Qmouse_entered
);
1690 Qmouse_entered
= intern ("mouse-entered");
1691 staticpro (&Qpoint_left
);
1692 Qpoint_left
= intern ("point-left");
1693 staticpro (&Qpoint_entered
);
1694 Qpoint_entered
= intern ("point-entered");
1696 defsubr (&Stext_properties_at
);
1697 defsubr (&Sget_text_property
);
1698 defsubr (&Sget_char_property
);
1699 defsubr (&Snext_property_change
);
1700 defsubr (&Snext_single_property_change
);
1701 defsubr (&Sprevious_property_change
);
1702 defsubr (&Sprevious_single_property_change
);
1703 defsubr (&Sadd_text_properties
);
1704 defsubr (&Sput_text_property
);
1705 defsubr (&Sset_text_properties
);
1706 defsubr (&Sremove_text_properties
);
1707 defsubr (&Stext_property_any
);
1708 defsubr (&Stext_property_not_all
);
1709 /* defsubr (&Serase_text_properties); */
1710 /* defsubr (&Scopy_text_properties); */
1715 lose
-- this shouldn
't be compiled if USE_TEXT_PROPERTIES isn't defined
1717 #endif /* USE_TEXT_PROPERTIES */