(auto-revert-set-timer): Minor doc fix.
[emacs.git] / src / textprop.c
blob4f13aefb1be17778af836a137fe4610116e97ab9
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
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.
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.
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. */
22 #include <config.h>
23 #include "lisp.h"
24 #include "intervals.h"
25 #include "buffer.h"
26 #include "window.h"
28 #ifndef NULL
29 #define NULL (void *)0
30 #endif
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
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.
42 set_properties needs to deal with the interval property cache.
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. */
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;
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;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
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)))
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;
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;
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. */
85 static void
86 text_read_only (propval)
87 Lisp_Object propval;
89 Fsignal (Qtext_read_only, STRINGP (propval) ? Fcons (propval, Qnil) : Qnil);
94 /* Extract the interval at the position pointed to by BEGIN from
95 OBJECT, a string or buffer. Additionally, check that the positions
96 pointed to by BEGIN and END are within the bounds of OBJECT, and
97 reverse them if *BEGIN is greater than *END. The objects pointed
98 to by BEGIN and END may be integers or markers; if the latter, they
99 are coerced to integers.
101 When OBJECT is a string, we increment *BEGIN and *END
102 to make them origin-one.
104 Note that buffer points don't correspond to interval indices.
105 For example, point-max is 1 greater than the index of the last
106 character. This difference is handled in the caller, which uses
107 the validated points to determine a length, and operates on that.
108 Exceptions are Ftext_properties_at, Fnext_property_change, and
109 Fprevious_property_change which call this function with BEGIN == END.
110 Handle this case specially.
112 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
113 create an interval tree for OBJECT if one doesn't exist, provided
114 the object actually contains text. In the current design, if there
115 is no text, there can be no text properties. */
117 #define soft 0
118 #define hard 1
120 INTERVAL
121 validate_interval_range (object, begin, end, force)
122 Lisp_Object object, *begin, *end;
123 int force;
125 register INTERVAL i;
126 int searchpos;
128 CHECK_STRING_OR_BUFFER (object);
129 CHECK_NUMBER_COERCE_MARKER (*begin);
130 CHECK_NUMBER_COERCE_MARKER (*end);
132 /* If we are asked for a point, but from a subr which operates
133 on a range, then return nothing. */
134 if (EQ (*begin, *end) && begin != end)
135 return NULL_INTERVAL;
137 if (XINT (*begin) > XINT (*end))
139 Lisp_Object n;
140 n = *begin;
141 *begin = *end;
142 *end = n;
145 if (BUFFERP (object))
147 register struct buffer *b = XBUFFER (object);
149 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
150 && XINT (*end) <= BUF_ZV (b)))
151 args_out_of_range (*begin, *end);
152 i = BUF_INTERVALS (b);
154 /* If there's no text, there are no properties. */
155 if (BUF_BEGV (b) == BUF_ZV (b))
156 return NULL_INTERVAL;
158 searchpos = XINT (*begin);
160 else
162 int len = SCHARS (object);
164 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
165 && XINT (*end) <= len))
166 args_out_of_range (*begin, *end);
167 XSETFASTINT (*begin, XFASTINT (*begin));
168 if (begin != end)
169 XSETFASTINT (*end, XFASTINT (*end));
170 i = STRING_INTERVALS (object);
172 if (len == 0)
173 return NULL_INTERVAL;
175 searchpos = XINT (*begin);
178 if (NULL_INTERVAL_P (i))
179 return (force ? create_root_interval (object) : i);
181 return find_interval (i, searchpos);
184 /* Validate LIST as a property list. If LIST is not a list, then
185 make one consisting of (LIST nil). Otherwise, verify that LIST
186 is even numbered and thus suitable as a plist. */
188 static Lisp_Object
189 validate_plist (list)
190 Lisp_Object list;
192 if (NILP (list))
193 return Qnil;
195 if (CONSP (list))
197 register int i;
198 register Lisp_Object tail;
199 for (i = 0, tail = list; !NILP (tail); i++)
201 tail = Fcdr (tail);
202 QUIT;
204 if (i & 1)
205 error ("Odd length text property list");
206 return list;
209 return Fcons (list, Fcons (Qnil, Qnil));
212 /* Return nonzero if interval I has all the properties,
213 with the same values, of list PLIST. */
215 static int
216 interval_has_all_properties (plist, i)
217 Lisp_Object plist;
218 INTERVAL i;
220 register Lisp_Object tail1, tail2, sym1;
221 register int found;
223 /* Go through each element of PLIST. */
224 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
226 sym1 = Fcar (tail1);
227 found = 0;
229 /* Go through I's plist, looking for sym1 */
230 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
231 if (EQ (sym1, Fcar (tail2)))
233 /* Found the same property on both lists. If the
234 values are unequal, return zero. */
235 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
236 return 0;
238 /* Property has same value on both lists; go to next one. */
239 found = 1;
240 break;
243 if (! found)
244 return 0;
247 return 1;
250 /* Return nonzero if the plist of interval I has any of the
251 properties of PLIST, regardless of their values. */
253 static INLINE int
254 interval_has_some_properties (plist, i)
255 Lisp_Object plist;
256 INTERVAL i;
258 register Lisp_Object tail1, tail2, sym;
260 /* Go through each element of PLIST. */
261 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
263 sym = Fcar (tail1);
265 /* Go through i's plist, looking for tail1 */
266 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
267 if (EQ (sym, Fcar (tail2)))
268 return 1;
271 return 0;
274 /* Return nonzero if the plist of interval I has any of the
275 property names in LIST, regardless of their values. */
277 static INLINE int
278 interval_has_some_properties_list (list, i)
279 Lisp_Object list;
280 INTERVAL i;
282 register Lisp_Object tail1, tail2, sym;
284 /* Go through each element of LIST. */
285 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
287 sym = Fcar (tail1);
289 /* Go through i's plist, looking for tail1 */
290 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
291 if (EQ (sym, XCAR (tail2)))
292 return 1;
295 return 0;
298 /* Changing the plists of individual intervals. */
300 /* Return the value of PROP in property-list PLIST, or Qunbound if it
301 has none. */
302 static Lisp_Object
303 property_value (plist, prop)
304 Lisp_Object plist, prop;
306 Lisp_Object value;
308 while (PLIST_ELT_P (plist, value))
309 if (EQ (XCAR (plist), prop))
310 return XCAR (value);
311 else
312 plist = XCDR (value);
314 return Qunbound;
317 /* Set the properties of INTERVAL to PROPERTIES,
318 and record undo info for the previous values.
319 OBJECT is the string or buffer that INTERVAL belongs to. */
321 static void
322 set_properties (properties, interval, object)
323 Lisp_Object properties, object;
324 INTERVAL interval;
326 Lisp_Object sym, value;
328 if (BUFFERP (object))
330 /* For each property in the old plist which is missing from PROPERTIES,
331 or has a different value in PROPERTIES, make an undo record. */
332 for (sym = interval->plist;
333 PLIST_ELT_P (sym, value);
334 sym = XCDR (value))
335 if (! EQ (property_value (properties, XCAR (sym)),
336 XCAR (value)))
338 record_property_change (interval->position, LENGTH (interval),
339 XCAR (sym), XCAR (value),
340 object);
343 /* For each new property that has no value at all in the old plist,
344 make an undo record binding it to nil, so it will be removed. */
345 for (sym = properties;
346 PLIST_ELT_P (sym, value);
347 sym = XCDR (value))
348 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
350 record_property_change (interval->position, LENGTH (interval),
351 XCAR (sym), Qnil,
352 object);
356 /* Store new properties. */
357 interval->plist = Fcopy_sequence (properties);
360 /* Add the properties of PLIST to the interval I, or set
361 the value of I's property to the value of the property on PLIST
362 if they are different.
364 OBJECT should be the string or buffer the interval is in.
366 Return nonzero if this changes I (i.e., if any members of PLIST
367 are actually added to I's plist) */
369 static int
370 add_properties (plist, i, object)
371 Lisp_Object plist;
372 INTERVAL i;
373 Lisp_Object object;
375 Lisp_Object tail1, tail2, sym1, val1;
376 register int changed = 0;
377 register int found;
378 struct gcpro gcpro1, gcpro2, gcpro3;
380 tail1 = plist;
381 sym1 = Qnil;
382 val1 = Qnil;
383 /* No need to protect OBJECT, because we can GC only in the case
384 where it is a buffer, and live buffers are always protected.
385 I and its plist are also protected, via OBJECT. */
386 GCPRO3 (tail1, sym1, val1);
388 /* Go through each element of PLIST. */
389 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
391 sym1 = Fcar (tail1);
392 val1 = Fcar (Fcdr (tail1));
393 found = 0;
395 /* Go through I's plist, looking for sym1 */
396 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
397 if (EQ (sym1, Fcar (tail2)))
399 /* No need to gcpro, because tail2 protects this
400 and it must be a cons cell (we get an error otherwise). */
401 register Lisp_Object this_cdr;
403 this_cdr = Fcdr (tail2);
404 /* Found the property. Now check its value. */
405 found = 1;
407 /* The properties have the same value on both lists.
408 Continue to the next property. */
409 if (EQ (val1, Fcar (this_cdr)))
410 break;
412 /* Record this change in the buffer, for undo purposes. */
413 if (BUFFERP (object))
415 record_property_change (i->position, LENGTH (i),
416 sym1, Fcar (this_cdr), object);
419 /* I's property has a different value -- change it */
420 Fsetcar (this_cdr, val1);
421 changed++;
422 break;
425 if (! found)
427 /* Record this change in the buffer, for undo purposes. */
428 if (BUFFERP (object))
430 record_property_change (i->position, LENGTH (i),
431 sym1, Qnil, object);
433 i->plist = Fcons (sym1, Fcons (val1, i->plist));
434 changed++;
438 UNGCPRO;
440 return changed;
443 /* For any members of PLIST, or LIST,
444 which are properties of I, remove them from I's plist.
445 (If PLIST is non-nil, use that, otherwise use LIST.)
446 OBJECT is the string or buffer containing I. */
448 static int
449 remove_properties (plist, list, i, object)
450 Lisp_Object plist, list;
451 INTERVAL i;
452 Lisp_Object object;
454 register Lisp_Object tail1, tail2, sym, current_plist;
455 register int changed = 0;
457 /* Nonzero means tail1 is a plist, otherwise it is a list. */
458 int use_plist;
460 current_plist = i->plist;
462 if (! NILP (plist))
463 tail1 = plist, use_plist = 1;
464 else
465 tail1 = list, use_plist = 0;
467 /* Go through each element of LIST or PLIST. */
468 while (CONSP (tail1))
470 sym = XCAR (tail1);
472 /* First, remove the symbol if it's at the head of the list */
473 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
475 if (BUFFERP (object))
476 record_property_change (i->position, LENGTH (i),
477 sym, XCAR (XCDR (current_plist)),
478 object);
480 current_plist = XCDR (XCDR (current_plist));
481 changed++;
484 /* Go through I's plist, looking for SYM. */
485 tail2 = current_plist;
486 while (! NILP (tail2))
488 register Lisp_Object this;
489 this = XCDR (XCDR (tail2));
490 if (CONSP (this) && EQ (sym, XCAR (this)))
492 if (BUFFERP (object))
493 record_property_change (i->position, LENGTH (i),
494 sym, XCAR (XCDR (this)), object);
496 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
497 changed++;
499 tail2 = this;
502 /* Advance thru TAIL1 one way or the other. */
503 tail1 = XCDR (tail1);
504 if (use_plist && CONSP (tail1))
505 tail1 = XCDR (tail1);
508 if (changed)
509 i->plist = current_plist;
510 return changed;
513 #if 0
514 /* Remove all properties from interval I. Return non-zero
515 if this changes the interval. */
517 static INLINE int
518 erase_properties (i)
519 INTERVAL i;
521 if (NILP (i->plist))
522 return 0;
524 i->plist = Qnil;
525 return 1;
527 #endif
529 /* Returns the interval of POSITION in OBJECT.
530 POSITION is BEG-based. */
532 INTERVAL
533 interval_of (position, object)
534 int position;
535 Lisp_Object object;
537 register INTERVAL i;
538 int beg, end;
540 if (NILP (object))
541 XSETBUFFER (object, current_buffer);
542 else if (EQ (object, Qt))
543 return NULL_INTERVAL;
545 CHECK_STRING_OR_BUFFER (object);
547 if (BUFFERP (object))
549 register struct buffer *b = XBUFFER (object);
551 beg = BUF_BEGV (b);
552 end = BUF_ZV (b);
553 i = BUF_INTERVALS (b);
555 else
557 beg = 0;
558 end = SCHARS (object);
559 i = STRING_INTERVALS (object);
562 if (!(beg <= position && position <= end))
563 args_out_of_range (make_number (position), make_number (position));
564 if (beg == end || NULL_INTERVAL_P (i))
565 return NULL_INTERVAL;
567 return find_interval (i, position);
570 DEFUN ("text-properties-at", Ftext_properties_at,
571 Stext_properties_at, 1, 2, 0,
572 doc: /* Return the list of properties of the character at POSITION in OBJECT.
573 If the optional second argument OBJECT is a buffer (or nil, which means
574 the current buffer), POSITION is a buffer position (integer or marker).
575 If OBJECT is a string, POSITION is a 0-based index into it.
576 If POSITION is at the end of OBJECT, the value is nil. */)
577 (position, object)
578 Lisp_Object position, object;
580 register INTERVAL i;
582 if (NILP (object))
583 XSETBUFFER (object, current_buffer);
585 i = validate_interval_range (object, &position, &position, soft);
586 if (NULL_INTERVAL_P (i))
587 return Qnil;
588 /* If POSITION is at the end of the interval,
589 it means it's the end of OBJECT.
590 There are no properties at the very end,
591 since no character follows. */
592 if (XINT (position) == LENGTH (i) + i->position)
593 return Qnil;
595 return i->plist;
598 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
599 doc: /* Return the value of POSITION's property PROP, in OBJECT.
600 OBJECT is optional and defaults to the current buffer.
601 If POSITION is at the end of OBJECT, the value is nil. */)
602 (position, prop, object)
603 Lisp_Object position, object;
604 Lisp_Object prop;
606 return textget (Ftext_properties_at (position, object), prop);
609 /* Return the value of char's property PROP, in OBJECT at POSITION.
610 OBJECT is optional and defaults to the current buffer.
611 If OVERLAY is non-0, then in the case that the returned property is from
612 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
613 returned in *OVERLAY.
614 If POSITION is at the end of OBJECT, the value is nil.
615 If OBJECT is a buffer, then overlay properties are considered as well as
616 text properties.
617 If OBJECT is a window, then that window's buffer is used, but
618 window-specific overlays are considered only if they are associated
619 with OBJECT. */
620 Lisp_Object
621 get_char_property_and_overlay (position, prop, object, overlay)
622 Lisp_Object position, object;
623 register Lisp_Object prop;
624 Lisp_Object *overlay;
626 struct window *w = 0;
628 CHECK_NUMBER_COERCE_MARKER (position);
630 if (NILP (object))
631 XSETBUFFER (object, current_buffer);
633 if (WINDOWP (object))
635 w = XWINDOW (object);
636 object = w->buffer;
638 if (BUFFERP (object))
640 int posn = XINT (position);
641 int noverlays;
642 Lisp_Object *overlay_vec, tem;
643 int len;
644 struct buffer *obuf = current_buffer;
646 set_buffer_temp (XBUFFER (object));
648 /* First try with room for 40 overlays. */
649 len = 40;
650 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
652 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
653 NULL, NULL, 0);
655 /* If there are more than 40,
656 make enough space for all, and try again. */
657 if (noverlays > len)
659 len = noverlays;
660 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
661 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
662 NULL, NULL, 0);
664 noverlays = sort_overlays (overlay_vec, noverlays, w);
666 set_buffer_temp (obuf);
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays >= 0)
671 tem = Foverlay_get (overlay_vec[noverlays], prop);
672 if (!NILP (tem))
674 if (overlay)
675 /* Return the overlay we got the property from. */
676 *overlay = overlay_vec[noverlays];
677 return tem;
682 if (overlay)
683 /* Indicate that the return value is not from an overlay. */
684 *overlay = Qnil;
686 /* Not a buffer, or no appropriate overlay, so fall through to the
687 simpler case. */
688 return Fget_text_property (position, prop, object);
691 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
692 doc: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
697 text properties.
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (position, prop, object)
701 Lisp_Object position, object;
702 register Lisp_Object prop;
704 return get_char_property_and_overlay (position, prop, object, 0);
707 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
708 Sget_char_property_and_overlay, 2, 3, 0,
709 doc: /* Like `get-char-property', but with extra overlay information.
710 Return a cons whose car is the return value of `get-char-property'
711 with the same arguments, that is, the value of POSITION's property
712 PROP in OBJECT, and whose cdr is the overlay in which the property was
713 found, or nil, if it was found as a text property or not found at all.
714 OBJECT is optional and defaults to the current buffer. OBJECT may be
715 a string, a buffer or a window. For strings, the cdr of the return
716 value is always nil, since strings do not have overlays. If OBJECT is
717 a window, then that window's buffer is used, but window-specific
718 overlays are considered only if they are associated with OBJECT. If
719 POSITION is at the end of OBJECT, both car and cdr are nil. */)
720 (position, prop, object)
721 Lisp_Object position, object;
722 register Lisp_Object prop;
724 Lisp_Object overlay;
725 Lisp_Object val
726 = get_char_property_and_overlay (position, prop, object, &overlay);
727 return Fcons(val, overlay);
731 DEFUN ("next-char-property-change", Fnext_char_property_change,
732 Snext_char_property_change, 1, 2, 0,
733 doc: /* Return the position of next text property or overlay change.
734 This scans characters forward in the current buffer from POSITION till
735 it finds a change in some text property, or the beginning or end of an
736 overlay, and returns the position of that.
737 If none is found, the function returns (point-max).
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;
744 Lisp_Object temp;
746 temp = Fnext_overlay_change (position);
747 if (! NILP (limit))
749 CHECK_NUMBER (limit);
750 if (XINT (limit) < XINT (temp))
751 temp = limit;
753 return Fnext_property_change (position, Qnil, temp);
756 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
757 Sprevious_char_property_change, 1, 2, 0,
758 doc: /* Return the position of previous text property or overlay change.
759 Scans characters backward in the current buffer from POSITION till it
760 finds a change in some text property, or the beginning or end of an
761 overlay, and returns the position of that.
762 If none is found, the function returns (point-max).
764 If the optional third argument LIMIT is non-nil, don't search
765 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
766 (position, limit)
767 Lisp_Object position, limit;
769 Lisp_Object temp;
771 temp = Fprevious_overlay_change (position);
772 if (! NILP (limit))
774 CHECK_NUMBER (limit);
775 if (XINT (limit) > XINT (temp))
776 temp = limit;
778 return Fprevious_property_change (position, Qnil, temp);
782 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
783 Snext_single_char_property_change, 2, 4, 0,
784 doc: /* Return the position of next text property or overlay change for a specific property.
785 Scans characters forward from POSITION till it finds
786 a change in the PROP property, then returns the position of the change.
787 If the optional third argument OBJECT is a buffer (or nil, which means
788 the current buffer), POSITION is a buffer position (integer or marker).
789 If OBJECT is a string, POSITION is a 0-based index into it.
791 The property values are compared with `eq'.
792 If the property is constant all the way to the end of OBJECT, return the
793 last valid position in OBJECT.
794 If the optional fourth argument LIMIT is non-nil, don't search
795 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
796 (position, prop, object, limit)
797 Lisp_Object prop, position, object, limit;
799 if (STRINGP (object))
801 position = Fnext_single_property_change (position, prop, object, limit);
802 if (NILP (position))
804 if (NILP (limit))
805 position = make_number (SCHARS (object));
806 else
807 position = limit;
810 else
812 Lisp_Object initial_value, value;
813 int count = SPECPDL_INDEX ();
815 if (! NILP (object))
816 CHECK_BUFFER (object);
818 if (BUFFERP (object) && current_buffer != XBUFFER (object))
820 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
821 Fset_buffer (object);
824 initial_value = Fget_char_property (position, prop, object);
826 if (NILP (limit))
827 XSETFASTINT (limit, BUF_ZV (current_buffer));
828 else
829 CHECK_NUMBER_COERCE_MARKER (limit);
831 for (;;)
833 position = Fnext_char_property_change (position, limit);
834 if (XFASTINT (position) >= XFASTINT (limit)) {
835 position = limit;
836 break;
839 value = Fget_char_property (position, prop, object);
840 if (!EQ (value, initial_value))
841 break;
844 unbind_to (count, Qnil);
847 return position;
850 DEFUN ("previous-single-char-property-change",
851 Fprevious_single_char_property_change,
852 Sprevious_single_char_property_change, 2, 4, 0,
853 doc: /* Return the position of previous text property or overlay change for a specific property.
854 Scans characters backward from POSITION till it finds
855 a change in the PROP property, then returns the position of the change.
856 If the optional third argument OBJECT is a buffer (or nil, which means
857 the current buffer), POSITION is a buffer position (integer or marker).
858 If OBJECT is a string, POSITION is a 0-based index into it.
860 The property values are compared with `eq'.
861 If the property is constant all the way to the start of OBJECT, return the
862 first valid position in OBJECT.
863 If the optional fourth argument LIMIT is non-nil, don't search
864 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
865 (position, prop, object, limit)
866 Lisp_Object prop, position, object, limit;
868 if (STRINGP (object))
870 position = Fprevious_single_property_change (position, prop, object, limit);
871 if (NILP (position))
873 if (NILP (limit))
874 position = make_number (SCHARS (object));
875 else
876 position = limit;
879 else
881 int count = SPECPDL_INDEX ();
883 if (! NILP (object))
884 CHECK_BUFFER (object);
886 if (BUFFERP (object) && current_buffer != XBUFFER (object))
888 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
889 Fset_buffer (object);
892 if (NILP (limit))
893 XSETFASTINT (limit, BUF_BEGV (current_buffer));
894 else
895 CHECK_NUMBER_COERCE_MARKER (limit);
897 if (XFASTINT (position) <= XFASTINT (limit))
898 position = limit;
899 else
901 Lisp_Object initial_value =
902 Fget_char_property (make_number (XFASTINT (position) - 1),
903 prop, object);
905 for (;;)
907 position = Fprevious_char_property_change (position, limit);
909 if (XFASTINT (position) <= XFASTINT (limit))
911 position = limit;
912 break;
914 else
916 Lisp_Object value =
917 Fget_char_property (make_number (XFASTINT (position) - 1),
918 prop, object);
920 if (!EQ (value, initial_value))
921 break;
926 unbind_to (count, Qnil);
929 return position;
932 DEFUN ("next-property-change", Fnext_property_change,
933 Snext_property_change, 1, 3, 0,
934 doc: /* Return the position of next property change.
935 Scans characters forward from POSITION in OBJECT till it finds
936 a change in some text property, then returns the position of the change.
937 If the optional second argument OBJECT is a buffer (or nil, which means
938 the current buffer), POSITION is a buffer position (integer or marker).
939 If OBJECT is a string, POSITION is a 0-based index into it.
940 Return nil if the property is constant all the way to the end of OBJECT.
941 If the value is non-nil, it is a position greater than POSITION, never equal.
943 If the optional third argument LIMIT is non-nil, don't search
944 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
945 (position, object, limit)
946 Lisp_Object position, object, limit;
948 register INTERVAL i, next;
950 if (NILP (object))
951 XSETBUFFER (object, current_buffer);
953 if (!NILP (limit) && !EQ (limit, Qt))
954 CHECK_NUMBER_COERCE_MARKER (limit);
956 i = validate_interval_range (object, &position, &position, soft);
958 /* If LIMIT is t, return start of next interval--don't
959 bother checking further intervals. */
960 if (EQ (limit, Qt))
962 if (NULL_INTERVAL_P (i))
963 next = i;
964 else
965 next = next_interval (i);
967 if (NULL_INTERVAL_P (next))
968 XSETFASTINT (position, (STRINGP (object)
969 ? SCHARS (object)
970 : BUF_ZV (XBUFFER (object))));
971 else
972 XSETFASTINT (position, next->position);
973 return position;
976 if (NULL_INTERVAL_P (i))
977 return limit;
979 next = next_interval (i);
981 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
982 && (NILP (limit) || next->position < XFASTINT (limit)))
983 next = next_interval (next);
985 if (NULL_INTERVAL_P (next))
986 return limit;
987 if (NILP (limit))
988 XSETFASTINT (limit, (STRINGP (object)
989 ? SCHARS (object)
990 : BUF_ZV (XBUFFER (object))));
991 if (!(next->position < XFASTINT (limit)))
992 return limit;
994 XSETFASTINT (position, next->position);
995 return position;
998 /* Return 1 if there's a change in some property between BEG and END. */
1001 property_change_between_p (beg, end)
1002 int beg, end;
1004 register INTERVAL i, next;
1005 Lisp_Object object, pos;
1007 XSETBUFFER (object, current_buffer);
1008 XSETFASTINT (pos, beg);
1010 i = validate_interval_range (object, &pos, &pos, soft);
1011 if (NULL_INTERVAL_P (i))
1012 return 0;
1014 next = next_interval (i);
1015 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1017 next = next_interval (next);
1018 if (NULL_INTERVAL_P (next))
1019 return 0;
1020 if (next->position >= end)
1021 return 0;
1024 if (NULL_INTERVAL_P (next))
1025 return 0;
1027 return 1;
1030 DEFUN ("next-single-property-change", Fnext_single_property_change,
1031 Snext_single_property_change, 2, 4, 0,
1032 doc: /* Return the position of next property change for a specific property.
1033 Scans characters forward from POSITION till it finds
1034 a change in the PROP property, then returns the position of the change.
1035 If the optional third argument OBJECT is a buffer (or nil, which means
1036 the current buffer), POSITION is a buffer position (integer or marker).
1037 If OBJECT is a string, POSITION is a 0-based index into it.
1038 The property values are compared with `eq'.
1039 Return nil if the property is constant all the way to the end of OBJECT.
1040 If the value is non-nil, it is a position greater than POSITION, never equal.
1042 If the optional fourth argument LIMIT is non-nil, don't search
1043 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1044 (position, prop, object, limit)
1045 Lisp_Object position, prop, object, limit;
1047 register INTERVAL i, next;
1048 register Lisp_Object here_val;
1050 if (NILP (object))
1051 XSETBUFFER (object, current_buffer);
1053 if (!NILP (limit))
1054 CHECK_NUMBER_COERCE_MARKER (limit);
1056 i = validate_interval_range (object, &position, &position, soft);
1057 if (NULL_INTERVAL_P (i))
1058 return limit;
1060 here_val = textget (i->plist, prop);
1061 next = next_interval (i);
1062 while (! NULL_INTERVAL_P (next)
1063 && EQ (here_val, textget (next->plist, prop))
1064 && (NILP (limit) || next->position < XFASTINT (limit)))
1065 next = next_interval (next);
1067 if (NULL_INTERVAL_P (next))
1068 return limit;
1069 if (NILP (limit))
1070 XSETFASTINT (limit, (STRINGP (object)
1071 ? SCHARS (object)
1072 : BUF_ZV (XBUFFER (object))));
1073 if (!(next->position < XFASTINT (limit)))
1074 return limit;
1076 return make_number (next->position);
1079 DEFUN ("previous-property-change", Fprevious_property_change,
1080 Sprevious_property_change, 1, 3, 0,
1081 doc: /* Return the position of previous property change.
1082 Scans characters backwards from POSITION in OBJECT till it finds
1083 a change in some text property, then returns the position of the change.
1084 If the optional second argument OBJECT is a buffer (or nil, which means
1085 the current buffer), POSITION is a buffer position (integer or marker).
1086 If OBJECT is a string, POSITION is a 0-based index into it.
1087 Return nil if the property is constant all the way to the start of OBJECT.
1088 If the value is non-nil, it is a position less than POSITION, never equal.
1090 If the optional third argument LIMIT is non-nil, don't search
1091 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1092 (position, object, limit)
1093 Lisp_Object position, object, limit;
1095 register INTERVAL i, previous;
1097 if (NILP (object))
1098 XSETBUFFER (object, current_buffer);
1100 if (!NILP (limit))
1101 CHECK_NUMBER_COERCE_MARKER (limit);
1103 i = validate_interval_range (object, &position, &position, soft);
1104 if (NULL_INTERVAL_P (i))
1105 return limit;
1107 /* Start with the interval containing the char before point. */
1108 if (i->position == XFASTINT (position))
1109 i = previous_interval (i);
1111 previous = previous_interval (i);
1112 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1113 && (NILP (limit)
1114 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1115 previous = previous_interval (previous);
1116 if (NULL_INTERVAL_P (previous))
1117 return limit;
1118 if (NILP (limit))
1119 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1120 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1121 return limit;
1123 return make_number (previous->position + LENGTH (previous));
1126 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1127 Sprevious_single_property_change, 2, 4, 0,
1128 doc: /* Return the position of previous property change for a specific property.
1129 Scans characters backward from POSITION till it finds
1130 a change in the PROP property, then returns the position of the change.
1131 If the optional third argument OBJECT is a buffer (or nil, which means
1132 the current buffer), POSITION is a buffer position (integer or marker).
1133 If OBJECT is a string, POSITION is a 0-based index into it.
1134 The property values are compared with `eq'.
1135 Return nil if the property is constant all the way to the start of OBJECT.
1136 If the value is non-nil, it is a position less than POSITION, never equal.
1138 If the optional fourth argument LIMIT is non-nil, don't search
1139 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1140 (position, prop, object, limit)
1141 Lisp_Object position, prop, object, limit;
1143 register INTERVAL i, previous;
1144 register Lisp_Object here_val;
1146 if (NILP (object))
1147 XSETBUFFER (object, current_buffer);
1149 if (!NILP (limit))
1150 CHECK_NUMBER_COERCE_MARKER (limit);
1152 i = validate_interval_range (object, &position, &position, soft);
1154 /* Start with the interval containing the char before point. */
1155 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1156 i = previous_interval (i);
1158 if (NULL_INTERVAL_P (i))
1159 return limit;
1161 here_val = textget (i->plist, prop);
1162 previous = previous_interval (i);
1163 while (!NULL_INTERVAL_P (previous)
1164 && EQ (here_val, textget (previous->plist, prop))
1165 && (NILP (limit)
1166 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1167 previous = previous_interval (previous);
1168 if (NULL_INTERVAL_P (previous))
1169 return limit;
1170 if (NILP (limit))
1171 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1172 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1173 return limit;
1175 return make_number (previous->position + LENGTH (previous));
1178 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1180 DEFUN ("add-text-properties", Fadd_text_properties,
1181 Sadd_text_properties, 3, 4, 0,
1182 doc: /* Add properties to the text from START to END.
1183 The third argument PROPERTIES is a property list
1184 specifying the property values to add. If the optional fourth argument
1185 OBJECT is a buffer (or nil, which means the current buffer),
1186 START and END are buffer positions (integers or markers).
1187 If OBJECT is a string, START and END are 0-based indices into it.
1188 Return t if any property value actually changed, nil otherwise. */)
1189 (start, end, properties, object)
1190 Lisp_Object start, end, properties, object;
1192 register INTERVAL i, unchanged;
1193 register int s, len, modified = 0;
1194 struct gcpro gcpro1;
1196 properties = validate_plist (properties);
1197 if (NILP (properties))
1198 return Qnil;
1200 if (NILP (object))
1201 XSETBUFFER (object, current_buffer);
1203 i = validate_interval_range (object, &start, &end, hard);
1204 if (NULL_INTERVAL_P (i))
1205 return Qnil;
1207 s = XINT (start);
1208 len = XINT (end) - s;
1210 /* No need to protect OBJECT, because we GC only if it's a buffer,
1211 and live buffers are always protected. */
1212 GCPRO1 (properties);
1214 /* If we're not starting on an interval boundary, we have to
1215 split this interval. */
1216 if (i->position != s)
1218 /* If this interval already has the properties, we can
1219 skip it. */
1220 if (interval_has_all_properties (properties, i))
1222 int got = (LENGTH (i) - (s - i->position));
1223 if (got >= len)
1224 RETURN_UNGCPRO (Qnil);
1225 len -= got;
1226 i = next_interval (i);
1228 else
1230 unchanged = i;
1231 i = split_interval_right (unchanged, s - unchanged->position);
1232 copy_properties (unchanged, i);
1236 if (BUFFERP (object))
1237 modify_region (XBUFFER (object), XINT (start), XINT (end));
1239 /* We are at the beginning of interval I, with LEN chars to scan. */
1240 for (;;)
1242 if (i == 0)
1243 abort ();
1245 if (LENGTH (i) >= len)
1247 /* We can UNGCPRO safely here, because there will be just
1248 one more chance to gc, in the next call to add_properties,
1249 and after that we will not need PROPERTIES or OBJECT again. */
1250 UNGCPRO;
1252 if (interval_has_all_properties (properties, i))
1254 if (BUFFERP (object))
1255 signal_after_change (XINT (start), XINT (end) - XINT (start),
1256 XINT (end) - XINT (start));
1258 return modified ? Qt : Qnil;
1261 if (LENGTH (i) == len)
1263 add_properties (properties, i, object);
1264 if (BUFFERP (object))
1265 signal_after_change (XINT (start), XINT (end) - XINT (start),
1266 XINT (end) - XINT (start));
1267 return Qt;
1270 /* i doesn't have the properties, and goes past the change limit */
1271 unchanged = i;
1272 i = split_interval_left (unchanged, len);
1273 copy_properties (unchanged, i);
1274 add_properties (properties, i, object);
1275 if (BUFFERP (object))
1276 signal_after_change (XINT (start), XINT (end) - XINT (start),
1277 XINT (end) - XINT (start));
1278 return Qt;
1281 len -= LENGTH (i);
1282 modified += add_properties (properties, i, object);
1283 i = next_interval (i);
1287 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1289 DEFUN ("put-text-property", Fput_text_property,
1290 Sput_text_property, 4, 5, 0,
1291 doc: /* Set one property of the text from START to END.
1292 The third and fourth arguments PROPERTY and VALUE
1293 specify the property to add.
1294 If the optional fifth argument OBJECT is a buffer (or nil, which means
1295 the current buffer), START and END are buffer positions (integers or
1296 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1297 (start, end, property, value, object)
1298 Lisp_Object start, end, property, value, object;
1300 Fadd_text_properties (start, end,
1301 Fcons (property, Fcons (value, Qnil)),
1302 object);
1303 return Qnil;
1306 DEFUN ("set-text-properties", Fset_text_properties,
1307 Sset_text_properties, 3, 4, 0,
1308 doc: /* Completely replace properties of text from START to END.
1309 The third argument PROPERTIES is the new property list.
1310 If the optional fourth argument OBJECT is a buffer (or nil, which means
1311 the current buffer), START and END are buffer positions (integers or
1312 markers). If OBJECT is a string, START and END are 0-based indices into it.
1313 If PROPERTIES is nil, the effect is to remove all properties from
1314 the designated part of OBJECT. */)
1315 (start, end, properties, object)
1316 Lisp_Object start, end, properties, object;
1318 return set_text_properties (start, end, properties, object, Qt);
1322 /* Replace properties of text from START to END with new list of
1323 properties PROPERTIES. OBJECT is the buffer or string containing
1324 the text. OBJECT nil means use the current buffer.
1325 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1326 is non-nil if properties were replaced; it is nil if there weren't
1327 any properties to replace. */
1329 Lisp_Object
1330 set_text_properties (start, end, properties, object, signal_after_change_p)
1331 Lisp_Object start, end, properties, object, signal_after_change_p;
1333 register INTERVAL i;
1334 Lisp_Object ostart, oend;
1336 ostart = start;
1337 oend = end;
1339 properties = validate_plist (properties);
1341 if (NILP (object))
1342 XSETBUFFER (object, current_buffer);
1344 /* If we want no properties for a whole string,
1345 get rid of its intervals. */
1346 if (NILP (properties) && STRINGP (object)
1347 && XFASTINT (start) == 0
1348 && XFASTINT (end) == SCHARS (object))
1350 if (! STRING_INTERVALS (object))
1351 return Qt;
1353 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1354 return Qt;
1357 i = validate_interval_range (object, &start, &end, soft);
1359 if (NULL_INTERVAL_P (i))
1361 /* If buffer has no properties, and we want none, return now. */
1362 if (NILP (properties))
1363 return Qnil;
1365 /* Restore the original START and END values
1366 because validate_interval_range increments them for strings. */
1367 start = ostart;
1368 end = oend;
1370 i = validate_interval_range (object, &start, &end, hard);
1371 /* This can return if start == end. */
1372 if (NULL_INTERVAL_P (i))
1373 return Qnil;
1376 if (BUFFERP (object))
1377 modify_region (XBUFFER (object), XINT (start), XINT (end));
1379 set_text_properties_1 (start, end, properties, object, i);
1381 if (BUFFERP (object) && !NILP (signal_after_change_p))
1382 signal_after_change (XINT (start), XINT (end) - XINT (start),
1383 XINT (end) - XINT (start));
1384 return Qt;
1387 /* Replace properties of text from START to END with new list of
1388 properties PROPERTIES. BUFFER is the buffer containing
1389 the text. This does not obey any hooks.
1390 You can provide the interval that START is located in as I,
1391 or pass NULL for I and this function will find it.
1392 START and END can be in any order. */
1394 void
1395 set_text_properties_1 (start, end, properties, buffer, i)
1396 Lisp_Object start, end, properties, buffer;
1397 INTERVAL i;
1399 register INTERVAL prev_changed = NULL_INTERVAL;
1400 register int s, len;
1401 INTERVAL unchanged;
1403 s = XINT (start);
1404 len = XINT (end) - s;
1405 if (len == 0)
1406 return;
1407 if (len < 0)
1409 s = s + len;
1410 len = - len;
1413 if (i == 0)
1414 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1416 if (i->position != s)
1418 unchanged = i;
1419 i = split_interval_right (unchanged, s - unchanged->position);
1421 if (LENGTH (i) > len)
1423 copy_properties (unchanged, i);
1424 i = split_interval_left (i, len);
1425 set_properties (properties, i, buffer);
1426 return;
1429 set_properties (properties, i, buffer);
1431 if (LENGTH (i) == len)
1432 return;
1434 prev_changed = i;
1435 len -= LENGTH (i);
1436 i = next_interval (i);
1439 /* We are starting at the beginning of an interval, I */
1440 while (len > 0)
1442 if (i == 0)
1443 abort ();
1445 if (LENGTH (i) >= len)
1447 if (LENGTH (i) > len)
1448 i = split_interval_left (i, len);
1450 /* We have to call set_properties even if we are going to
1451 merge the intervals, so as to make the undo records
1452 and cause redisplay to happen. */
1453 set_properties (properties, i, buffer);
1454 if (!NULL_INTERVAL_P (prev_changed))
1455 merge_interval_left (i);
1456 return;
1459 len -= LENGTH (i);
1461 /* We have to call set_properties even if we are going to
1462 merge the intervals, so as to make the undo records
1463 and cause redisplay to happen. */
1464 set_properties (properties, i, buffer);
1465 if (NULL_INTERVAL_P (prev_changed))
1466 prev_changed = i;
1467 else
1468 prev_changed = i = merge_interval_left (i);
1470 i = next_interval (i);
1474 DEFUN ("remove-text-properties", Fremove_text_properties,
1475 Sremove_text_properties, 3, 4, 0,
1476 doc: /* Remove some properties from text from START to END.
1477 The third argument PROPERTIES is a property list
1478 whose property names specify the properties to remove.
1479 \(The values stored in PROPERTIES are ignored.)
1480 If the optional fourth argument OBJECT is a buffer (or nil, which means
1481 the current buffer), START and END are buffer positions (integers or
1482 markers). If OBJECT is a string, START and END are 0-based indices into it.
1483 Return t if any property was actually removed, nil otherwise.
1485 Use set-text-properties if you want to remove all text properties. */)
1486 (start, end, properties, object)
1487 Lisp_Object start, end, properties, object;
1489 register INTERVAL i, unchanged;
1490 register int s, len, modified = 0;
1492 if (NILP (object))
1493 XSETBUFFER (object, current_buffer);
1495 i = validate_interval_range (object, &start, &end, soft);
1496 if (NULL_INTERVAL_P (i))
1497 return Qnil;
1499 s = XINT (start);
1500 len = XINT (end) - s;
1502 if (i->position != s)
1504 /* No properties on this first interval -- return if
1505 it covers the entire region. */
1506 if (! interval_has_some_properties (properties, i))
1508 int got = (LENGTH (i) - (s - i->position));
1509 if (got >= len)
1510 return Qnil;
1511 len -= got;
1512 i = next_interval (i);
1514 /* Split away the beginning of this interval; what we don't
1515 want to modify. */
1516 else
1518 unchanged = i;
1519 i = split_interval_right (unchanged, s - unchanged->position);
1520 copy_properties (unchanged, i);
1524 if (BUFFERP (object))
1525 modify_region (XBUFFER (object), XINT (start), XINT (end));
1527 /* We are at the beginning of an interval, with len to scan */
1528 for (;;)
1530 if (i == 0)
1531 abort ();
1533 if (LENGTH (i) >= len)
1535 if (! interval_has_some_properties (properties, i))
1536 return modified ? Qt : Qnil;
1538 if (LENGTH (i) == len)
1540 remove_properties (properties, Qnil, i, object);
1541 if (BUFFERP (object))
1542 signal_after_change (XINT (start), XINT (end) - XINT (start),
1543 XINT (end) - XINT (start));
1544 return Qt;
1547 /* i has the properties, and goes past the change limit */
1548 unchanged = i;
1549 i = split_interval_left (i, len);
1550 copy_properties (unchanged, i);
1551 remove_properties (properties, Qnil, i, object);
1552 if (BUFFERP (object))
1553 signal_after_change (XINT (start), XINT (end) - XINT (start),
1554 XINT (end) - XINT (start));
1555 return Qt;
1558 len -= LENGTH (i);
1559 modified += remove_properties (properties, Qnil, i, object);
1560 i = next_interval (i);
1564 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1565 Sremove_list_of_text_properties, 3, 4, 0,
1566 doc: /* Remove some properties from text from START to END.
1567 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1568 If the optional fourth argument OBJECT is a buffer (or nil, which means
1569 the current buffer), START and END are buffer positions (integers or
1570 markers). If OBJECT is a string, START and END are 0-based indices into it.
1571 Return t if any property was actually removed, nil otherwise. */)
1572 (start, end, list_of_properties, object)
1573 Lisp_Object start, end, list_of_properties, object;
1575 register INTERVAL i, unchanged;
1576 register int s, len, modified = 0;
1577 Lisp_Object properties;
1578 properties = list_of_properties;
1580 if (NILP (object))
1581 XSETBUFFER (object, current_buffer);
1583 i = validate_interval_range (object, &start, &end, soft);
1584 if (NULL_INTERVAL_P (i))
1585 return Qnil;
1587 s = XINT (start);
1588 len = XINT (end) - s;
1590 if (i->position != s)
1592 /* No properties on this first interval -- return if
1593 it covers the entire region. */
1594 if (! interval_has_some_properties_list (properties, i))
1596 int got = (LENGTH (i) - (s - i->position));
1597 if (got >= len)
1598 return Qnil;
1599 len -= got;
1600 i = next_interval (i);
1602 /* Split away the beginning of this interval; what we don't
1603 want to modify. */
1604 else
1606 unchanged = i;
1607 i = split_interval_right (unchanged, s - unchanged->position);
1608 copy_properties (unchanged, i);
1612 if (BUFFERP (object))
1613 modify_region (XBUFFER (object), XINT (start), XINT (end));
1615 /* We are at the beginning of an interval, with len to scan */
1616 for (;;)
1618 if (i == 0)
1619 abort ();
1621 if (LENGTH (i) >= len)
1623 if (! interval_has_some_properties_list (properties, i))
1624 return modified ? Qt : Qnil;
1626 if (LENGTH (i) == len)
1628 remove_properties (Qnil, properties, i, object);
1629 if (BUFFERP (object))
1630 signal_after_change (XINT (start), XINT (end) - XINT (start),
1631 XINT (end) - XINT (start));
1632 return Qt;
1635 /* i has the properties, and goes past the change limit */
1636 unchanged = i;
1637 i = split_interval_left (i, len);
1638 copy_properties (unchanged, i);
1639 remove_properties (Qnil, properties, i, object);
1640 if (BUFFERP (object))
1641 signal_after_change (XINT (start), XINT (end) - XINT (start),
1642 XINT (end) - XINT (start));
1643 return Qt;
1646 len -= LENGTH (i);
1647 modified += remove_properties (Qnil, properties, i, object);
1648 i = next_interval (i);
1652 DEFUN ("text-property-any", Ftext_property_any,
1653 Stext_property_any, 4, 5, 0,
1654 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1655 If so, return the position of the first character whose property PROPERTY
1656 is `eq' to VALUE. Otherwise return nil.
1657 If the optional fifth argument OBJECT is a buffer (or nil, which means
1658 the current buffer), START and END are buffer positions (integers or
1659 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1660 (start, end, property, value, object)
1661 Lisp_Object start, end, property, value, object;
1663 register INTERVAL i;
1664 register int e, pos;
1666 if (NILP (object))
1667 XSETBUFFER (object, current_buffer);
1668 i = validate_interval_range (object, &start, &end, soft);
1669 if (NULL_INTERVAL_P (i))
1670 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1671 e = XINT (end);
1673 while (! NULL_INTERVAL_P (i))
1675 if (i->position >= e)
1676 break;
1677 if (EQ (textget (i->plist, property), value))
1679 pos = i->position;
1680 if (pos < XINT (start))
1681 pos = XINT (start);
1682 return make_number (pos);
1684 i = next_interval (i);
1686 return Qnil;
1689 DEFUN ("text-property-not-all", Ftext_property_not_all,
1690 Stext_property_not_all, 4, 5, 0,
1691 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1692 If so, return the position of the first character whose property PROPERTY
1693 is not `eq' to VALUE. Otherwise, return nil.
1694 If the optional fifth argument OBJECT is a buffer (or nil, which means
1695 the current buffer), START and END are buffer positions (integers or
1696 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1697 (start, end, property, value, object)
1698 Lisp_Object start, end, property, value, object;
1700 register INTERVAL i;
1701 register int s, e;
1703 if (NILP (object))
1704 XSETBUFFER (object, current_buffer);
1705 i = validate_interval_range (object, &start, &end, soft);
1706 if (NULL_INTERVAL_P (i))
1707 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1708 s = XINT (start);
1709 e = XINT (end);
1711 while (! NULL_INTERVAL_P (i))
1713 if (i->position >= e)
1714 break;
1715 if (! EQ (textget (i->plist, property), value))
1717 if (i->position > s)
1718 s = i->position;
1719 return make_number (s);
1721 i = next_interval (i);
1723 return Qnil;
1727 /* Return the direction from which the text-property PROP would be
1728 inherited by any new text inserted at POS: 1 if it would be
1729 inherited from the char after POS, -1 if it would be inherited from
1730 the char before POS, and 0 if from neither.
1731 BUFFER can be either a buffer or nil (meaning current buffer). */
1734 text_property_stickiness (prop, pos, buffer)
1735 Lisp_Object prop, pos, buffer;
1737 Lisp_Object prev_pos, front_sticky;
1738 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1740 if (NILP (buffer))
1741 XSETBUFFER (buffer, current_buffer);
1743 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1744 /* Consider previous character. */
1746 Lisp_Object rear_non_sticky;
1748 prev_pos = make_number (XINT (pos) - 1);
1749 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1751 if (!NILP (CONSP (rear_non_sticky)
1752 ? Fmemq (prop, rear_non_sticky)
1753 : rear_non_sticky))
1754 /* PROP is rear-non-sticky. */
1755 is_rear_sticky = 0;
1758 /* Consider following character. */
1759 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1761 if (EQ (front_sticky, Qt)
1762 || (CONSP (front_sticky)
1763 && !NILP (Fmemq (prop, front_sticky))))
1764 /* PROP is inherited from after. */
1765 is_front_sticky = 1;
1767 /* Simple cases, where the properties are consistent. */
1768 if (is_rear_sticky && !is_front_sticky)
1769 return -1;
1770 else if (!is_rear_sticky && is_front_sticky)
1771 return 1;
1772 else if (!is_rear_sticky && !is_front_sticky)
1773 return 0;
1775 /* The stickiness properties are inconsistent, so we have to
1776 disambiguate. Basically, rear-sticky wins, _except_ if the
1777 property that would be inherited has a value of nil, in which case
1778 front-sticky wins. */
1779 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1780 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1781 return 1;
1782 else
1783 return -1;
1787 /* I don't think this is the right interface to export; how often do you
1788 want to do something like this, other than when you're copying objects
1789 around?
1791 I think it would be better to have a pair of functions, one which
1792 returns the text properties of a region as a list of ranges and
1793 plists, and another which applies such a list to another object. */
1795 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1796 SRC and DEST may each refer to strings or buffers.
1797 Optional sixth argument PROP causes only that property to be copied.
1798 Properties are copied to DEST as if by `add-text-properties'.
1799 Return t if any property value actually changed, nil otherwise. */
1801 /* Note this can GC when DEST is a buffer. */
1803 Lisp_Object
1804 copy_text_properties (start, end, src, pos, dest, prop)
1805 Lisp_Object start, end, src, pos, dest, prop;
1807 INTERVAL i;
1808 Lisp_Object res;
1809 Lisp_Object stuff;
1810 Lisp_Object plist;
1811 int s, e, e2, p, len, modified = 0;
1812 struct gcpro gcpro1, gcpro2;
1814 i = validate_interval_range (src, &start, &end, soft);
1815 if (NULL_INTERVAL_P (i))
1816 return Qnil;
1818 CHECK_NUMBER_COERCE_MARKER (pos);
1820 Lisp_Object dest_start, dest_end;
1822 dest_start = pos;
1823 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1824 /* Apply this to a copy of pos; it will try to increment its arguments,
1825 which we don't want. */
1826 validate_interval_range (dest, &dest_start, &dest_end, soft);
1829 s = XINT (start);
1830 e = XINT (end);
1831 p = XINT (pos);
1833 stuff = Qnil;
1835 while (s < e)
1837 e2 = i->position + LENGTH (i);
1838 if (e2 > e)
1839 e2 = e;
1840 len = e2 - s;
1842 plist = i->plist;
1843 if (! NILP (prop))
1844 while (! NILP (plist))
1846 if (EQ (Fcar (plist), prop))
1848 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1849 break;
1851 plist = Fcdr (Fcdr (plist));
1853 if (! NILP (plist))
1855 /* Must defer modifications to the interval tree in case src
1856 and dest refer to the same string or buffer. */
1857 stuff = Fcons (Fcons (make_number (p),
1858 Fcons (make_number (p + len),
1859 Fcons (plist, Qnil))),
1860 stuff);
1863 i = next_interval (i);
1864 if (NULL_INTERVAL_P (i))
1865 break;
1867 p += len;
1868 s = i->position;
1871 GCPRO2 (stuff, dest);
1873 while (! NILP (stuff))
1875 res = Fcar (stuff);
1876 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1877 Fcar (Fcdr (Fcdr (res))), dest);
1878 if (! NILP (res))
1879 modified++;
1880 stuff = Fcdr (stuff);
1883 UNGCPRO;
1885 return modified ? Qt : Qnil;
1889 /* Return a list representing the text properties of OBJECT between
1890 START and END. if PROP is non-nil, report only on that property.
1891 Each result list element has the form (S E PLIST), where S and E
1892 are positions in OBJECT and PLIST is a property list containing the
1893 text properties of OBJECT between S and E. Value is nil if OBJECT
1894 doesn't contain text properties between START and END. */
1896 Lisp_Object
1897 text_property_list (object, start, end, prop)
1898 Lisp_Object object, start, end, prop;
1900 struct interval *i;
1901 Lisp_Object result;
1903 result = Qnil;
1905 i = validate_interval_range (object, &start, &end, soft);
1906 if (!NULL_INTERVAL_P (i))
1908 int s = XINT (start);
1909 int e = XINT (end);
1911 while (s < e)
1913 int interval_end, len;
1914 Lisp_Object plist;
1916 interval_end = i->position + LENGTH (i);
1917 if (interval_end > e)
1918 interval_end = e;
1919 len = interval_end - s;
1921 plist = i->plist;
1923 if (!NILP (prop))
1924 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1925 if (EQ (Fcar (plist), prop))
1927 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1928 break;
1931 if (!NILP (plist))
1932 result = Fcons (Fcons (make_number (s),
1933 Fcons (make_number (s + len),
1934 Fcons (plist, Qnil))),
1935 result);
1937 i = next_interval (i);
1938 if (NULL_INTERVAL_P (i))
1939 break;
1940 s = i->position;
1944 return result;
1948 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1949 (START END PLIST), where START and END are positions and PLIST is a
1950 property list containing the text properties to add. Adjust START
1951 and END positions by DELTA before adding properties. Value is
1952 non-zero if OBJECT was modified. */
1955 add_text_properties_from_list (object, list, delta)
1956 Lisp_Object object, list, delta;
1958 struct gcpro gcpro1, gcpro2;
1959 int modified_p = 0;
1961 GCPRO2 (list, object);
1963 for (; CONSP (list); list = XCDR (list))
1965 Lisp_Object item, start, end, plist, tem;
1967 item = XCAR (list);
1968 start = make_number (XINT (XCAR (item)) + XINT (delta));
1969 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1970 plist = XCAR (XCDR (XCDR (item)));
1972 tem = Fadd_text_properties (start, end, plist, object);
1973 if (!NILP (tem))
1974 modified_p = 1;
1977 UNGCPRO;
1978 return modified_p;
1983 /* Modify end-points of ranges in LIST destructively. LIST is a list
1984 as returned from text_property_list. Change end-points equal to
1985 OLD_END to NEW_END. */
1987 void
1988 extend_property_ranges (list, old_end, new_end)
1989 Lisp_Object list, old_end, new_end;
1991 for (; CONSP (list); list = XCDR (list))
1993 Lisp_Object item, end;
1995 item = XCAR (list);
1996 end = XCAR (XCDR (item));
1998 if (EQ (end, old_end))
1999 XSETCAR (XCDR (item), new_end);
2005 /* Call the modification hook functions in LIST, each with START and END. */
2007 static void
2008 call_mod_hooks (list, start, end)
2009 Lisp_Object list, start, end;
2011 struct gcpro gcpro1;
2012 GCPRO1 (list);
2013 while (!NILP (list))
2015 call2 (Fcar (list), start, end);
2016 list = Fcdr (list);
2018 UNGCPRO;
2021 /* Check for read-only intervals between character positions START ... END,
2022 in BUF, and signal an error if we find one.
2024 Then check for any modification hooks in the range.
2025 Create a list of all these hooks in lexicographic order,
2026 eliminating consecutive extra copies of the same hook. Then call
2027 those hooks in order, with START and END - 1 as arguments. */
2029 void
2030 verify_interval_modification (buf, start, end)
2031 struct buffer *buf;
2032 int start, end;
2034 register INTERVAL intervals = BUF_INTERVALS (buf);
2035 register INTERVAL i;
2036 Lisp_Object hooks;
2037 register Lisp_Object prev_mod_hooks;
2038 Lisp_Object mod_hooks;
2039 struct gcpro gcpro1;
2041 hooks = Qnil;
2042 prev_mod_hooks = Qnil;
2043 mod_hooks = Qnil;
2045 interval_insert_behind_hooks = Qnil;
2046 interval_insert_in_front_hooks = Qnil;
2048 if (NULL_INTERVAL_P (intervals))
2049 return;
2051 if (start > end)
2053 int temp = start;
2054 start = end;
2055 end = temp;
2058 /* For an insert operation, check the two chars around the position. */
2059 if (start == end)
2061 INTERVAL prev = NULL;
2062 Lisp_Object before, after;
2064 /* Set I to the interval containing the char after START,
2065 and PREV to the interval containing the char before START.
2066 Either one may be null. They may be equal. */
2067 i = find_interval (intervals, start);
2069 if (start == BUF_BEGV (buf))
2070 prev = 0;
2071 else if (i->position == start)
2072 prev = previous_interval (i);
2073 else if (i->position < start)
2074 prev = i;
2075 if (start == BUF_ZV (buf))
2076 i = 0;
2078 /* If Vinhibit_read_only is set and is not a list, we can
2079 skip the read_only checks. */
2080 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2082 /* If I and PREV differ we need to check for the read-only
2083 property together with its stickiness. If either I or
2084 PREV are 0, this check is all we need.
2085 We have to take special care, since read-only may be
2086 indirectly defined via the category property. */
2087 if (i != prev)
2089 if (! NULL_INTERVAL_P (i))
2091 after = textget (i->plist, Qread_only);
2093 /* If interval I is read-only and read-only is
2094 front-sticky, inhibit insertion.
2095 Check for read-only as well as category. */
2096 if (! NILP (after)
2097 && NILP (Fmemq (after, Vinhibit_read_only)))
2099 Lisp_Object tem;
2101 tem = textget (i->plist, Qfront_sticky);
2102 if (TMEM (Qread_only, tem)
2103 || (NILP (Fplist_get (i->plist, Qread_only))
2104 && TMEM (Qcategory, tem)))
2105 text_read_only (after);
2109 if (! NULL_INTERVAL_P (prev))
2111 before = textget (prev->plist, Qread_only);
2113 /* If interval PREV is read-only and read-only isn't
2114 rear-nonsticky, inhibit insertion.
2115 Check for read-only as well as category. */
2116 if (! NILP (before)
2117 && NILP (Fmemq (before, Vinhibit_read_only)))
2119 Lisp_Object tem;
2121 tem = textget (prev->plist, Qrear_nonsticky);
2122 if (! TMEM (Qread_only, tem)
2123 && (! NILP (Fplist_get (prev->plist,Qread_only))
2124 || ! TMEM (Qcategory, tem)))
2125 text_read_only (before);
2129 else if (! NULL_INTERVAL_P (i))
2131 after = textget (i->plist, Qread_only);
2133 /* If interval I is read-only and read-only is
2134 front-sticky, inhibit insertion.
2135 Check for read-only as well as category. */
2136 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2138 Lisp_Object tem;
2140 tem = textget (i->plist, Qfront_sticky);
2141 if (TMEM (Qread_only, tem)
2142 || (NILP (Fplist_get (i->plist, Qread_only))
2143 && TMEM (Qcategory, tem)))
2144 text_read_only (after);
2146 tem = textget (prev->plist, Qrear_nonsticky);
2147 if (! TMEM (Qread_only, tem)
2148 && (! NILP (Fplist_get (prev->plist, Qread_only))
2149 || ! TMEM (Qcategory, tem)))
2150 text_read_only (after);
2155 /* Run both insert hooks (just once if they're the same). */
2156 if (!NULL_INTERVAL_P (prev))
2157 interval_insert_behind_hooks
2158 = textget (prev->plist, Qinsert_behind_hooks);
2159 if (!NULL_INTERVAL_P (i))
2160 interval_insert_in_front_hooks
2161 = textget (i->plist, Qinsert_in_front_hooks);
2163 else
2165 /* Loop over intervals on or next to START...END,
2166 collecting their hooks. */
2168 i = find_interval (intervals, start);
2171 if (! INTERVAL_WRITABLE_P (i))
2172 text_read_only (textget (i->plist, Qread_only));
2174 if (!inhibit_modification_hooks)
2176 mod_hooks = textget (i->plist, Qmodification_hooks);
2177 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2179 hooks = Fcons (mod_hooks, hooks);
2180 prev_mod_hooks = mod_hooks;
2184 i = next_interval (i);
2186 /* Keep going thru the interval containing the char before END. */
2187 while (! NULL_INTERVAL_P (i) && i->position < end);
2189 if (!inhibit_modification_hooks)
2191 GCPRO1 (hooks);
2192 hooks = Fnreverse (hooks);
2193 while (! EQ (hooks, Qnil))
2195 call_mod_hooks (Fcar (hooks), make_number (start),
2196 make_number (end));
2197 hooks = Fcdr (hooks);
2199 UNGCPRO;
2204 /* Run the interval hooks for an insertion on character range START ... END.
2205 verify_interval_modification chose which hooks to run;
2206 this function is called after the insertion happens
2207 so it can indicate the range of inserted text. */
2209 void
2210 report_interval_modification (start, end)
2211 Lisp_Object start, end;
2213 if (! NILP (interval_insert_behind_hooks))
2214 call_mod_hooks (interval_insert_behind_hooks, start, end);
2215 if (! NILP (interval_insert_in_front_hooks)
2216 && ! EQ (interval_insert_in_front_hooks,
2217 interval_insert_behind_hooks))
2218 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2221 void
2222 syms_of_textprop ()
2224 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2225 doc: /* Property-list used as default values.
2226 The value of a property in this list is seen as the value for every
2227 character that does not have its own value for that property. */);
2228 Vdefault_text_properties = Qnil;
2230 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2231 doc: /* Alist of alternative properties for properties without a value.
2232 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2233 If a piece of text has no direct value for a particular property, then
2234 this alist is consulted. If that property appears in the alist, then
2235 the first non-nil value from the associated alternative properties is
2236 returned. */);
2237 Vchar_property_alias_alist = Qnil;
2239 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2240 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2241 This also inhibits the use of the `intangible' text property. */);
2242 Vinhibit_point_motion_hooks = Qnil;
2244 DEFVAR_LISP ("text-property-default-nonsticky",
2245 &Vtext_property_default_nonsticky,
2246 doc: /* Alist of properties vs the corresponding non-stickinesses.
2247 Each element has the form (PROPERTY . NONSTICKINESS).
2249 If a character in a buffer has PROPERTY, new text inserted adjacent to
2250 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2251 inherits it if NONSTICKINESS is nil. The front-sticky and
2252 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2253 Vtext_property_default_nonsticky = Qnil;
2255 staticpro (&interval_insert_behind_hooks);
2256 staticpro (&interval_insert_in_front_hooks);
2257 interval_insert_behind_hooks = Qnil;
2258 interval_insert_in_front_hooks = Qnil;
2261 /* Common attributes one might give text */
2263 staticpro (&Qforeground);
2264 Qforeground = intern ("foreground");
2265 staticpro (&Qbackground);
2266 Qbackground = intern ("background");
2267 staticpro (&Qfont);
2268 Qfont = intern ("font");
2269 staticpro (&Qstipple);
2270 Qstipple = intern ("stipple");
2271 staticpro (&Qunderline);
2272 Qunderline = intern ("underline");
2273 staticpro (&Qread_only);
2274 Qread_only = intern ("read-only");
2275 staticpro (&Qinvisible);
2276 Qinvisible = intern ("invisible");
2277 staticpro (&Qintangible);
2278 Qintangible = intern ("intangible");
2279 staticpro (&Qcategory);
2280 Qcategory = intern ("category");
2281 staticpro (&Qlocal_map);
2282 Qlocal_map = intern ("local-map");
2283 staticpro (&Qfront_sticky);
2284 Qfront_sticky = intern ("front-sticky");
2285 staticpro (&Qrear_nonsticky);
2286 Qrear_nonsticky = intern ("rear-nonsticky");
2287 staticpro (&Qmouse_face);
2288 Qmouse_face = intern ("mouse-face");
2290 /* Properties that text might use to specify certain actions */
2292 staticpro (&Qmouse_left);
2293 Qmouse_left = intern ("mouse-left");
2294 staticpro (&Qmouse_entered);
2295 Qmouse_entered = intern ("mouse-entered");
2296 staticpro (&Qpoint_left);
2297 Qpoint_left = intern ("point-left");
2298 staticpro (&Qpoint_entered);
2299 Qpoint_entered = intern ("point-entered");
2301 defsubr (&Stext_properties_at);
2302 defsubr (&Sget_text_property);
2303 defsubr (&Sget_char_property);
2304 defsubr (&Sget_char_property_and_overlay);
2305 defsubr (&Snext_char_property_change);
2306 defsubr (&Sprevious_char_property_change);
2307 defsubr (&Snext_single_char_property_change);
2308 defsubr (&Sprevious_single_char_property_change);
2309 defsubr (&Snext_property_change);
2310 defsubr (&Snext_single_property_change);
2311 defsubr (&Sprevious_property_change);
2312 defsubr (&Sprevious_single_property_change);
2313 defsubr (&Sadd_text_properties);
2314 defsubr (&Sput_text_property);
2315 defsubr (&Sset_text_properties);
2316 defsubr (&Sremove_text_properties);
2317 defsubr (&Sremove_list_of_text_properties);
2318 defsubr (&Stext_property_any);
2319 defsubr (&Stext_property_not_all);
2320 /* defsubr (&Serase_text_properties); */
2321 /* defsubr (&Scopy_text_properties); */
2324 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2325 (do not change this comment) */