(erc-button-add-button): Only call `widget-convert-button' in XEmacs.
[emacs.git] / src / textprop.c
blob0018088b63eb153e2857caa9678003f3763c0450
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003,
3 2004, 2005, 2006, 2007, 2008, 2009 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 3 of the License, or
10 (at your option) 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. If not, see <http://www.gnu.org/licenses/>. */
20 #include <config.h>
21 #include "lisp.h"
22 #include "intervals.h"
23 #include "buffer.h"
24 #include "window.h"
26 #ifndef NULL
27 #define NULL (void *)0
28 #endif
30 /* Test for membership, allowing for t (actually any non-cons) to mean the
31 universal set. */
33 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
36 /* NOTES: previous- and next- property change will have to skip
37 zero-length intervals if they are implemented. This could be done
38 inside next_interval and previous_interval.
40 set_properties needs to deal with the interval property cache.
42 It is assumed that for any interval plist, a property appears
43 only once on the list. Although some code i.e., remove_properties,
44 handles the more general case, the uniqueness of properties is
45 necessary for the system to remain consistent. This requirement
46 is enforced by the subrs installing properties onto the intervals. */
49 /* Types of hooks. */
50 Lisp_Object Qmouse_left;
51 Lisp_Object Qmouse_entered;
52 Lisp_Object Qpoint_left;
53 Lisp_Object Qpoint_entered;
54 Lisp_Object Qcategory;
55 Lisp_Object Qlocal_map;
57 /* Visual properties text (including strings) may have. */
58 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
59 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
60 Lisp_Object Qminibuffer_prompt;
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
67 traversing plists. */
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70 Lisp_Object Vinhibit_point_motion_hooks;
71 Lisp_Object Vdefault_text_properties;
72 Lisp_Object Vchar_property_alias_alist;
73 Lisp_Object Vtext_property_default_nonsticky;
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 static void text_read_only P_ ((Lisp_Object)) NO_RETURN;
83 /* Signal a `text-read-only' error. This function makes it easier
84 to capture that error in GDB by putting a breakpoint on it. */
86 static void
87 text_read_only (propval)
88 Lisp_Object propval;
90 if (STRINGP (propval))
91 xsignal1 (Qtext_read_only, propval);
93 xsignal0 (Qtext_read_only);
98 /* Extract the interval at the position pointed to by BEGIN from
99 OBJECT, a string or buffer. Additionally, check that the positions
100 pointed to by BEGIN and END are within the bounds of OBJECT, and
101 reverse them if *BEGIN is greater than *END. The objects pointed
102 to by BEGIN and END may be integers or markers; if the latter, they
103 are coerced to integers.
105 When OBJECT is a string, we increment *BEGIN and *END
106 to make them origin-one.
108 Note that buffer points don't correspond to interval indices.
109 For example, point-max is 1 greater than the index of the last
110 character. This difference is handled in the caller, which uses
111 the validated points to determine a length, and operates on that.
112 Exceptions are Ftext_properties_at, Fnext_property_change, and
113 Fprevious_property_change which call this function with BEGIN == END.
114 Handle this case specially.
116 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
117 create an interval tree for OBJECT if one doesn't exist, provided
118 the object actually contains text. In the current design, if there
119 is no text, there can be no text properties. */
121 #define soft 0
122 #define hard 1
124 INTERVAL
125 validate_interval_range (object, begin, end, force)
126 Lisp_Object object, *begin, *end;
127 int force;
129 register INTERVAL i;
130 int searchpos;
132 CHECK_STRING_OR_BUFFER (object);
133 CHECK_NUMBER_COERCE_MARKER (*begin);
134 CHECK_NUMBER_COERCE_MARKER (*end);
136 /* If we are asked for a point, but from a subr which operates
137 on a range, then return nothing. */
138 if (EQ (*begin, *end) && begin != end)
139 return NULL_INTERVAL;
141 if (XINT (*begin) > XINT (*end))
143 Lisp_Object n;
144 n = *begin;
145 *begin = *end;
146 *end = n;
149 if (BUFFERP (object))
151 register struct buffer *b = XBUFFER (object);
153 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
154 && XINT (*end) <= BUF_ZV (b)))
155 args_out_of_range (*begin, *end);
156 i = BUF_INTERVALS (b);
158 /* If there's no text, there are no properties. */
159 if (BUF_BEGV (b) == BUF_ZV (b))
160 return NULL_INTERVAL;
162 searchpos = XINT (*begin);
164 else
166 int len = SCHARS (object);
168 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
169 && XINT (*end) <= len))
170 args_out_of_range (*begin, *end);
171 XSETFASTINT (*begin, XFASTINT (*begin));
172 if (begin != end)
173 XSETFASTINT (*end, XFASTINT (*end));
174 i = STRING_INTERVALS (object);
176 if (len == 0)
177 return NULL_INTERVAL;
179 searchpos = XINT (*begin);
182 if (NULL_INTERVAL_P (i))
183 return (force ? create_root_interval (object) : i);
185 return find_interval (i, searchpos);
188 /* Validate LIST as a property list. If LIST is not a list, then
189 make one consisting of (LIST nil). Otherwise, verify that LIST
190 is even numbered and thus suitable as a plist. */
192 static Lisp_Object
193 validate_plist (list)
194 Lisp_Object list;
196 if (NILP (list))
197 return Qnil;
199 if (CONSP (list))
201 register int i;
202 register Lisp_Object tail;
203 for (i = 0, tail = list; CONSP (tail); i++)
205 tail = XCDR (tail);
206 QUIT;
208 if (i & 1)
209 error ("Odd length text property list");
210 return list;
213 return Fcons (list, Fcons (Qnil, Qnil));
216 /* Return nonzero if interval I has all the properties,
217 with the same values, of list PLIST. */
219 static int
220 interval_has_all_properties (plist, i)
221 Lisp_Object plist;
222 INTERVAL i;
224 register Lisp_Object tail1, tail2, sym1;
225 register int found;
227 /* Go through each element of PLIST. */
228 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
230 sym1 = XCAR (tail1);
231 found = 0;
233 /* Go through I's plist, looking for sym1 */
234 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
235 if (EQ (sym1, XCAR (tail2)))
237 /* Found the same property on both lists. If the
238 values are unequal, return zero. */
239 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
240 return 0;
242 /* Property has same value on both lists; go to next one. */
243 found = 1;
244 break;
247 if (! found)
248 return 0;
251 return 1;
254 /* Return nonzero if the plist of interval I has any of the
255 properties of PLIST, regardless of their values. */
257 static INLINE int
258 interval_has_some_properties (plist, i)
259 Lisp_Object plist;
260 INTERVAL i;
262 register Lisp_Object tail1, tail2, sym;
264 /* Go through each element of PLIST. */
265 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
267 sym = XCAR (tail1);
269 /* Go through i's plist, looking for tail1 */
270 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
271 if (EQ (sym, XCAR (tail2)))
272 return 1;
275 return 0;
278 /* Return nonzero if the plist of interval I has any of the
279 property names in LIST, regardless of their values. */
281 static INLINE int
282 interval_has_some_properties_list (list, i)
283 Lisp_Object list;
284 INTERVAL i;
286 register Lisp_Object tail1, tail2, sym;
288 /* Go through each element of LIST. */
289 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
291 sym = Fcar (tail1);
293 /* Go through i's plist, looking for tail1 */
294 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
295 if (EQ (sym, XCAR (tail2)))
296 return 1;
299 return 0;
302 /* Changing the plists of individual intervals. */
304 /* Return the value of PROP in property-list PLIST, or Qunbound if it
305 has none. */
306 static Lisp_Object
307 property_value (plist, prop)
308 Lisp_Object plist, prop;
310 Lisp_Object value;
312 while (PLIST_ELT_P (plist, value))
313 if (EQ (XCAR (plist), prop))
314 return XCAR (value);
315 else
316 plist = XCDR (value);
318 return Qunbound;
321 /* Set the properties of INTERVAL to PROPERTIES,
322 and record undo info for the previous values.
323 OBJECT is the string or buffer that INTERVAL belongs to. */
325 static void
326 set_properties (properties, interval, object)
327 Lisp_Object properties, object;
328 INTERVAL interval;
330 Lisp_Object sym, value;
332 if (BUFFERP (object))
334 /* For each property in the old plist which is missing from PROPERTIES,
335 or has a different value in PROPERTIES, make an undo record. */
336 for (sym = interval->plist;
337 PLIST_ELT_P (sym, value);
338 sym = XCDR (value))
339 if (! EQ (property_value (properties, XCAR (sym)),
340 XCAR (value)))
342 record_property_change (interval->position, LENGTH (interval),
343 XCAR (sym), XCAR (value),
344 object);
347 /* For each new property that has no value at all in the old plist,
348 make an undo record binding it to nil, so it will be removed. */
349 for (sym = properties;
350 PLIST_ELT_P (sym, value);
351 sym = XCDR (value))
352 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
354 record_property_change (interval->position, LENGTH (interval),
355 XCAR (sym), Qnil,
356 object);
360 /* Store new properties. */
361 interval->plist = Fcopy_sequence (properties);
364 /* Add the properties of PLIST to the interval I, or set
365 the value of I's property to the value of the property on PLIST
366 if they are different.
368 OBJECT should be the string or buffer the interval is in.
370 Return nonzero if this changes I (i.e., if any members of PLIST
371 are actually added to I's plist) */
373 static int
374 add_properties (plist, i, object)
375 Lisp_Object plist;
376 INTERVAL i;
377 Lisp_Object object;
379 Lisp_Object tail1, tail2, sym1, val1;
380 register int changed = 0;
381 register int found;
382 struct gcpro gcpro1, gcpro2, gcpro3;
384 tail1 = plist;
385 sym1 = Qnil;
386 val1 = Qnil;
387 /* No need to protect OBJECT, because we can GC only in the case
388 where it is a buffer, and live buffers are always protected.
389 I and its plist are also protected, via OBJECT. */
390 GCPRO3 (tail1, sym1, val1);
392 /* Go through each element of PLIST. */
393 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
395 sym1 = XCAR (tail1);
396 val1 = Fcar (XCDR (tail1));
397 found = 0;
399 /* Go through I's plist, looking for sym1 */
400 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
401 if (EQ (sym1, XCAR (tail2)))
403 /* No need to gcpro, because tail2 protects this
404 and it must be a cons cell (we get an error otherwise). */
405 register Lisp_Object this_cdr;
407 this_cdr = XCDR (tail2);
408 /* Found the property. Now check its value. */
409 found = 1;
411 /* The properties have the same value on both lists.
412 Continue to the next property. */
413 if (EQ (val1, Fcar (this_cdr)))
414 break;
416 /* Record this change in the buffer, for undo purposes. */
417 if (BUFFERP (object))
419 record_property_change (i->position, LENGTH (i),
420 sym1, Fcar (this_cdr), object);
423 /* I's property has a different value -- change it */
424 Fsetcar (this_cdr, val1);
425 changed++;
426 break;
429 if (! found)
431 /* Record this change in the buffer, for undo purposes. */
432 if (BUFFERP (object))
434 record_property_change (i->position, LENGTH (i),
435 sym1, Qnil, object);
437 i->plist = Fcons (sym1, Fcons (val1, i->plist));
438 changed++;
442 UNGCPRO;
444 return changed;
447 /* For any members of PLIST, or LIST,
448 which are properties of I, remove them from I's plist.
449 (If PLIST is non-nil, use that, otherwise use LIST.)
450 OBJECT is the string or buffer containing I. */
452 static int
453 remove_properties (plist, list, i, object)
454 Lisp_Object plist, list;
455 INTERVAL i;
456 Lisp_Object object;
458 register Lisp_Object tail1, tail2, sym, current_plist;
459 register int changed = 0;
461 /* Nonzero means tail1 is a plist, otherwise it is a list. */
462 int use_plist;
464 current_plist = i->plist;
466 if (! NILP (plist))
467 tail1 = plist, use_plist = 1;
468 else
469 tail1 = list, use_plist = 0;
471 /* Go through each element of LIST or PLIST. */
472 while (CONSP (tail1))
474 sym = XCAR (tail1);
476 /* First, remove the symbol if it's at the head of the list */
477 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
479 if (BUFFERP (object))
480 record_property_change (i->position, LENGTH (i),
481 sym, XCAR (XCDR (current_plist)),
482 object);
484 current_plist = XCDR (XCDR (current_plist));
485 changed++;
488 /* Go through I's plist, looking for SYM. */
489 tail2 = current_plist;
490 while (! NILP (tail2))
492 register Lisp_Object this;
493 this = XCDR (XCDR (tail2));
494 if (CONSP (this) && EQ (sym, XCAR (this)))
496 if (BUFFERP (object))
497 record_property_change (i->position, LENGTH (i),
498 sym, XCAR (XCDR (this)), object);
500 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
501 changed++;
503 tail2 = this;
506 /* Advance thru TAIL1 one way or the other. */
507 tail1 = XCDR (tail1);
508 if (use_plist && CONSP (tail1))
509 tail1 = XCDR (tail1);
512 if (changed)
513 i->plist = current_plist;
514 return changed;
517 #if 0
518 /* Remove all properties from interval I. Return non-zero
519 if this changes the interval. */
521 static INLINE int
522 erase_properties (i)
523 INTERVAL i;
525 if (NILP (i->plist))
526 return 0;
528 i->plist = Qnil;
529 return 1;
531 #endif
533 /* Returns the interval of POSITION in OBJECT.
534 POSITION is BEG-based. */
536 INTERVAL
537 interval_of (position, object)
538 int position;
539 Lisp_Object object;
541 register INTERVAL i;
542 int beg, end;
544 if (NILP (object))
545 XSETBUFFER (object, current_buffer);
546 else if (EQ (object, Qt))
547 return NULL_INTERVAL;
549 CHECK_STRING_OR_BUFFER (object);
551 if (BUFFERP (object))
553 register struct buffer *b = XBUFFER (object);
555 beg = BUF_BEGV (b);
556 end = BUF_ZV (b);
557 i = BUF_INTERVALS (b);
559 else
561 beg = 0;
562 end = SCHARS (object);
563 i = STRING_INTERVALS (object);
566 if (!(beg <= position && position <= end))
567 args_out_of_range (make_number (position), make_number (position));
568 if (beg == end || NULL_INTERVAL_P (i))
569 return NULL_INTERVAL;
571 return find_interval (i, position);
574 DEFUN ("text-properties-at", Ftext_properties_at,
575 Stext_properties_at, 1, 2, 0,
576 doc: /* Return the list of properties of the character at POSITION in OBJECT.
577 If the optional second argument OBJECT is a buffer (or nil, which means
578 the current buffer), POSITION is a buffer position (integer or marker).
579 If OBJECT is a string, POSITION is a 0-based index into it.
580 If POSITION is at the end of OBJECT, the value is nil. */)
581 (position, object)
582 Lisp_Object position, object;
584 register INTERVAL i;
586 if (NILP (object))
587 XSETBUFFER (object, current_buffer);
589 i = validate_interval_range (object, &position, &position, soft);
590 if (NULL_INTERVAL_P (i))
591 return Qnil;
592 /* If POSITION is at the end of the interval,
593 it means it's the end of OBJECT.
594 There are no properties at the very end,
595 since no character follows. */
596 if (XINT (position) == LENGTH (i) + i->position)
597 return Qnil;
599 return i->plist;
602 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
603 doc: /* Return the value of POSITION's property PROP, in OBJECT.
604 OBJECT is optional and defaults to the current buffer.
605 If POSITION is at the end of OBJECT, the value is nil. */)
606 (position, prop, object)
607 Lisp_Object position, object;
608 Lisp_Object prop;
610 return textget (Ftext_properties_at (position, object), prop);
613 /* Return the value of char's property PROP, in OBJECT at POSITION.
614 OBJECT is optional and defaults to the current buffer.
615 If OVERLAY is non-0, then in the case that the returned property is from
616 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
617 returned in *OVERLAY.
618 If POSITION is at the end of OBJECT, the value is nil.
619 If OBJECT is a buffer, then overlay properties are considered as well as
620 text properties.
621 If OBJECT is a window, then that window's buffer is used, but
622 window-specific overlays are considered only if they are associated
623 with OBJECT. */
624 Lisp_Object
625 get_char_property_and_overlay (position, prop, object, overlay)
626 Lisp_Object position, object;
627 register Lisp_Object prop;
628 Lisp_Object *overlay;
630 struct window *w = 0;
632 CHECK_NUMBER_COERCE_MARKER (position);
634 if (NILP (object))
635 XSETBUFFER (object, current_buffer);
637 if (WINDOWP (object))
639 w = XWINDOW (object);
640 object = w->buffer;
642 if (BUFFERP (object))
644 int noverlays;
645 Lisp_Object *overlay_vec;
646 struct buffer *obuf = current_buffer;
648 if (XINT (position) < BUF_BEGV (XBUFFER (object))
649 || XINT (position) > BUF_ZV (XBUFFER (object)))
650 xsignal1 (Qargs_out_of_range, position);
652 set_buffer_temp (XBUFFER (object));
654 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
655 noverlays = sort_overlays (overlay_vec, noverlays, w);
657 set_buffer_temp (obuf);
659 /* Now check the overlays in order of decreasing priority. */
660 while (--noverlays >= 0)
662 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
663 if (!NILP (tem))
665 if (overlay)
666 /* Return the overlay we got the property from. */
667 *overlay = overlay_vec[noverlays];
668 return tem;
673 if (overlay)
674 /* Indicate that the return value is not from an overlay. */
675 *overlay = Qnil;
677 /* Not a buffer, or no appropriate overlay, so fall through to the
678 simpler case. */
679 return Fget_text_property (position, prop, object);
682 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
683 doc: /* Return the value of POSITION's property PROP, in OBJECT.
684 Both overlay properties and text properties are checked.
685 OBJECT is optional and defaults to the current buffer.
686 If POSITION is at the end of OBJECT, the value is nil.
687 If OBJECT is a buffer, then overlay properties are considered as well as
688 text properties.
689 If OBJECT is a window, then that window's buffer is used, but window-specific
690 overlays are considered only if they are associated with OBJECT. */)
691 (position, prop, object)
692 Lisp_Object position, object;
693 register Lisp_Object prop;
695 return get_char_property_and_overlay (position, prop, object, 0);
698 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
699 Sget_char_property_and_overlay, 2, 3, 0,
700 doc: /* Like `get-char-property', but with extra overlay information.
701 The value is a cons cell. Its car is the return value of `get-char-property'
702 with the same arguments--that is, the value of POSITION's property
703 PROP in OBJECT. Its cdr is the overlay in which the property was
704 found, or nil, if it was found as a text property or not found at all.
706 OBJECT is optional and defaults to the current buffer. OBJECT may be
707 a string, a buffer or a window. For strings, the cdr of the return
708 value is always nil, since strings do not have overlays. If OBJECT is
709 a window, then that window's buffer is used, but window-specific
710 overlays are considered only if they are associated with OBJECT. If
711 POSITION is at the end of OBJECT, both car and cdr are nil. */)
712 (position, prop, object)
713 Lisp_Object position, object;
714 register Lisp_Object prop;
716 Lisp_Object overlay;
717 Lisp_Object val
718 = get_char_property_and_overlay (position, prop, object, &overlay);
719 return Fcons (val, overlay);
723 DEFUN ("next-char-property-change", Fnext_char_property_change,
724 Snext_char_property_change, 1, 2, 0,
725 doc: /* Return the position of next text property or overlay change.
726 This scans characters forward in the current buffer from POSITION till
727 it finds a change in some text property, or the beginning or end of an
728 overlay, and returns the position of that.
729 If none is found up to (point-max), the function returns (point-max).
731 If the optional second argument LIMIT is non-nil, don't search
732 past position LIMIT; return LIMIT if nothing is found before LIMIT.
733 LIMIT is a no-op if it is greater than (point-max). */)
734 (position, limit)
735 Lisp_Object position, limit;
737 Lisp_Object temp;
739 temp = Fnext_overlay_change (position);
740 if (! NILP (limit))
742 CHECK_NUMBER_COERCE_MARKER (limit);
743 if (XINT (limit) < XINT (temp))
744 temp = limit;
746 return Fnext_property_change (position, Qnil, temp);
749 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
750 Sprevious_char_property_change, 1, 2, 0,
751 doc: /* Return the position of previous text property or overlay change.
752 Scans characters backward in the current buffer from POSITION till it
753 finds a change in some text property, or the beginning or end of an
754 overlay, and returns the position of that.
755 If none is found since (point-min), the function returns (point-min).
757 If the optional second argument LIMIT is non-nil, don't search
758 past position LIMIT; return LIMIT if nothing is found before LIMIT.
759 LIMIT is a no-op if it is less than (point-min). */)
760 (position, limit)
761 Lisp_Object position, limit;
763 Lisp_Object temp;
765 temp = Fprevious_overlay_change (position);
766 if (! NILP (limit))
768 CHECK_NUMBER_COERCE_MARKER (limit);
769 if (XINT (limit) > XINT (temp))
770 temp = limit;
772 return Fprevious_property_change (position, Qnil, temp);
776 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
777 Snext_single_char_property_change, 2, 4, 0,
778 doc: /* Return the position of next text property or overlay change for a specific property.
779 Scans characters forward from POSITION till it finds
780 a change in the PROP property, then returns the position of the change.
781 If the optional third argument OBJECT is a buffer (or nil, which means
782 the current buffer), POSITION is a buffer position (integer or marker).
783 If OBJECT is a string, POSITION is a 0-based index into it.
785 In a string, scan runs to the end of the string.
786 In a buffer, it runs to (point-max), and the value cannot exceed that.
788 The property values are compared with `eq'.
789 If the property is constant all the way to the end of OBJECT, return the
790 last valid position in OBJECT.
791 If the optional fourth argument LIMIT is non-nil, don't search
792 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
793 (position, prop, object, limit)
794 Lisp_Object prop, position, object, limit;
796 if (STRINGP (object))
798 position = Fnext_single_property_change (position, prop, object, limit);
799 if (NILP (position))
801 if (NILP (limit))
802 position = make_number (SCHARS (object));
803 else
805 CHECK_NUMBER (limit);
806 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 CHECK_NUMBER_COERCE_MARKER (position);
826 initial_value = Fget_char_property (position, prop, object);
828 if (NILP (limit))
829 XSETFASTINT (limit, ZV);
830 else
831 CHECK_NUMBER_COERCE_MARKER (limit);
833 if (XFASTINT (position) >= XFASTINT (limit))
835 position = limit;
836 if (XFASTINT (position) > ZV)
837 XSETFASTINT (position, ZV);
839 else
840 while (1)
842 position = Fnext_char_property_change (position, limit);
843 if (XFASTINT (position) >= XFASTINT (limit))
845 position = limit;
846 break;
849 value = Fget_char_property (position, prop, object);
850 if (!EQ (value, initial_value))
851 break;
854 unbind_to (count, Qnil);
857 return position;
860 DEFUN ("previous-single-char-property-change",
861 Fprevious_single_char_property_change,
862 Sprevious_single_char_property_change, 2, 4, 0,
863 doc: /* Return the position of previous text property or overlay change for a specific property.
864 Scans characters backward from POSITION till it finds
865 a change in the PROP property, then returns the position of the change.
866 If the optional third argument OBJECT is a buffer (or nil, which means
867 the current buffer), POSITION is a buffer position (integer or marker).
868 If OBJECT is a string, POSITION is a 0-based index into it.
870 In a string, scan runs to the start of the string.
871 In a buffer, it runs to (point-min), and the value cannot be less than that.
873 The property values are compared with `eq'.
874 If the property is constant all the way to the start of OBJECT, return the
875 first valid position in OBJECT.
876 If the optional fourth argument LIMIT is non-nil, don't search
877 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
878 (position, prop, object, limit)
879 Lisp_Object prop, position, object, limit;
881 if (STRINGP (object))
883 position = Fprevious_single_property_change (position, prop, object, limit);
884 if (NILP (position))
886 if (NILP (limit))
887 position = make_number (0);
888 else
890 CHECK_NUMBER (limit);
891 position = limit;
895 else
897 int count = SPECPDL_INDEX ();
899 if (! NILP (object))
900 CHECK_BUFFER (object);
902 if (BUFFERP (object) && current_buffer != XBUFFER (object))
904 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
905 Fset_buffer (object);
908 CHECK_NUMBER_COERCE_MARKER (position);
910 if (NILP (limit))
911 XSETFASTINT (limit, BEGV);
912 else
913 CHECK_NUMBER_COERCE_MARKER (limit);
915 if (XFASTINT (position) <= XFASTINT (limit))
917 position = limit;
918 if (XFASTINT (position) < BEGV)
919 XSETFASTINT (position, BEGV);
921 else
923 Lisp_Object initial_value
924 = Fget_char_property (make_number (XFASTINT (position) - 1),
925 prop, object);
927 while (1)
929 position = Fprevious_char_property_change (position, limit);
931 if (XFASTINT (position) <= XFASTINT (limit))
933 position = limit;
934 break;
936 else
938 Lisp_Object value
939 = Fget_char_property (make_number (XFASTINT (position) - 1),
940 prop, object);
942 if (!EQ (value, initial_value))
943 break;
948 unbind_to (count, Qnil);
951 return position;
954 DEFUN ("next-property-change", Fnext_property_change,
955 Snext_property_change, 1, 3, 0,
956 doc: /* Return the position of next property change.
957 Scans characters forward from POSITION in OBJECT till it finds
958 a change in some text property, then returns the position of the change.
959 If the optional second argument OBJECT is a buffer (or nil, which means
960 the current buffer), POSITION is a buffer position (integer or marker).
961 If OBJECT is a string, POSITION is a 0-based index into it.
962 Return nil if the property is constant all the way to the end of OBJECT.
963 If the value is non-nil, it is a position greater than POSITION, never equal.
965 If the optional third argument LIMIT is non-nil, don't search
966 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
967 (position, object, limit)
968 Lisp_Object position, object, limit;
970 register INTERVAL i, next;
972 if (NILP (object))
973 XSETBUFFER (object, current_buffer);
975 if (!NILP (limit) && !EQ (limit, Qt))
976 CHECK_NUMBER_COERCE_MARKER (limit);
978 i = validate_interval_range (object, &position, &position, soft);
980 /* If LIMIT is t, return start of next interval--don't
981 bother checking further intervals. */
982 if (EQ (limit, Qt))
984 if (NULL_INTERVAL_P (i))
985 next = i;
986 else
987 next = next_interval (i);
989 if (NULL_INTERVAL_P (next))
990 XSETFASTINT (position, (STRINGP (object)
991 ? SCHARS (object)
992 : BUF_ZV (XBUFFER (object))));
993 else
994 XSETFASTINT (position, next->position);
995 return position;
998 if (NULL_INTERVAL_P (i))
999 return limit;
1001 next = next_interval (i);
1003 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
1004 && (NILP (limit) || next->position < XFASTINT (limit)))
1005 next = next_interval (next);
1007 if (NULL_INTERVAL_P (next)
1008 || (next->position
1009 >= (INTEGERP (limit)
1010 ? XFASTINT (limit)
1011 : (STRINGP (object)
1012 ? SCHARS (object)
1013 : BUF_ZV (XBUFFER (object))))))
1014 return limit;
1015 else
1016 return make_number (next->position);
1019 /* Return 1 if there's a change in some property between BEG and END. */
1022 property_change_between_p (beg, end)
1023 int beg, end;
1025 register INTERVAL i, next;
1026 Lisp_Object object, pos;
1028 XSETBUFFER (object, current_buffer);
1029 XSETFASTINT (pos, beg);
1031 i = validate_interval_range (object, &pos, &pos, soft);
1032 if (NULL_INTERVAL_P (i))
1033 return 0;
1035 next = next_interval (i);
1036 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1038 next = next_interval (next);
1039 if (NULL_INTERVAL_P (next))
1040 return 0;
1041 if (next->position >= end)
1042 return 0;
1045 if (NULL_INTERVAL_P (next))
1046 return 0;
1048 return 1;
1051 DEFUN ("next-single-property-change", Fnext_single_property_change,
1052 Snext_single_property_change, 2, 4, 0,
1053 doc: /* Return the position of next property change for a specific property.
1054 Scans characters forward from POSITION till it finds
1055 a change in the PROP property, then returns the position of the change.
1056 If the optional third argument OBJECT is a buffer (or nil, which means
1057 the current buffer), POSITION is a buffer position (integer or marker).
1058 If OBJECT is a string, POSITION is a 0-based index into it.
1059 The property values are compared with `eq'.
1060 Return nil if the property is constant all the way to the end of OBJECT.
1061 If the value is non-nil, it is a position greater than POSITION, never equal.
1063 If the optional fourth argument LIMIT is non-nil, don't search
1064 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1065 (position, prop, object, limit)
1066 Lisp_Object position, prop, object, limit;
1068 register INTERVAL i, next;
1069 register Lisp_Object here_val;
1071 if (NILP (object))
1072 XSETBUFFER (object, current_buffer);
1074 if (!NILP (limit))
1075 CHECK_NUMBER_COERCE_MARKER (limit);
1077 i = validate_interval_range (object, &position, &position, soft);
1078 if (NULL_INTERVAL_P (i))
1079 return limit;
1081 here_val = textget (i->plist, prop);
1082 next = next_interval (i);
1083 while (! NULL_INTERVAL_P (next)
1084 && EQ (here_val, textget (next->plist, prop))
1085 && (NILP (limit) || next->position < XFASTINT (limit)))
1086 next = next_interval (next);
1088 if (NULL_INTERVAL_P (next)
1089 || (next->position
1090 >= (INTEGERP (limit)
1091 ? XFASTINT (limit)
1092 : (STRINGP (object)
1093 ? SCHARS (object)
1094 : BUF_ZV (XBUFFER (object))))))
1095 return limit;
1096 else
1097 return make_number (next->position);
1100 DEFUN ("previous-property-change", Fprevious_property_change,
1101 Sprevious_property_change, 1, 3, 0,
1102 doc: /* Return the position of previous property change.
1103 Scans characters backwards from POSITION in OBJECT till it finds
1104 a change in some text property, then returns the position of the change.
1105 If the optional second argument OBJECT is a buffer (or nil, which means
1106 the current buffer), POSITION is a buffer position (integer or marker).
1107 If OBJECT is a string, POSITION is a 0-based index into it.
1108 Return nil if the property is constant all the way to the start of OBJECT.
1109 If the value is non-nil, it is a position less than POSITION, never equal.
1111 If the optional third argument LIMIT is non-nil, don't search
1112 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1113 (position, object, limit)
1114 Lisp_Object position, object, limit;
1116 register INTERVAL i, previous;
1118 if (NILP (object))
1119 XSETBUFFER (object, current_buffer);
1121 if (!NILP (limit))
1122 CHECK_NUMBER_COERCE_MARKER (limit);
1124 i = validate_interval_range (object, &position, &position, soft);
1125 if (NULL_INTERVAL_P (i))
1126 return limit;
1128 /* Start with the interval containing the char before point. */
1129 if (i->position == XFASTINT (position))
1130 i = previous_interval (i);
1132 previous = previous_interval (i);
1133 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1134 && (NILP (limit)
1135 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1136 previous = previous_interval (previous);
1138 if (NULL_INTERVAL_P (previous)
1139 || (previous->position + LENGTH (previous)
1140 <= (INTEGERP (limit)
1141 ? XFASTINT (limit)
1142 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1143 return limit;
1144 else
1145 return make_number (previous->position + LENGTH (previous));
1148 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1149 Sprevious_single_property_change, 2, 4, 0,
1150 doc: /* Return the position of previous property change for a specific property.
1151 Scans characters backward from POSITION till it finds
1152 a change in the PROP property, then returns the position of the change.
1153 If the optional third argument OBJECT is a buffer (or nil, which means
1154 the current buffer), POSITION is a buffer position (integer or marker).
1155 If OBJECT is a string, POSITION is a 0-based index into it.
1156 The property values are compared with `eq'.
1157 Return nil if the property is constant all the way to the start of OBJECT.
1158 If the value is non-nil, it is a position less than POSITION, never equal.
1160 If the optional fourth argument LIMIT is non-nil, don't search
1161 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1162 (position, prop, object, limit)
1163 Lisp_Object position, prop, object, limit;
1165 register INTERVAL i, previous;
1166 register Lisp_Object here_val;
1168 if (NILP (object))
1169 XSETBUFFER (object, current_buffer);
1171 if (!NILP (limit))
1172 CHECK_NUMBER_COERCE_MARKER (limit);
1174 i = validate_interval_range (object, &position, &position, soft);
1176 /* Start with the interval containing the char before point. */
1177 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1178 i = previous_interval (i);
1180 if (NULL_INTERVAL_P (i))
1181 return limit;
1183 here_val = textget (i->plist, prop);
1184 previous = previous_interval (i);
1185 while (!NULL_INTERVAL_P (previous)
1186 && EQ (here_val, textget (previous->plist, prop))
1187 && (NILP (limit)
1188 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1189 previous = previous_interval (previous);
1191 if (NULL_INTERVAL_P (previous)
1192 || (previous->position + LENGTH (previous)
1193 <= (INTEGERP (limit)
1194 ? XFASTINT (limit)
1195 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1196 return limit;
1197 else
1198 return make_number (previous->position + LENGTH (previous));
1201 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1203 DEFUN ("add-text-properties", Fadd_text_properties,
1204 Sadd_text_properties, 3, 4, 0,
1205 doc: /* Add properties to the text from START to END.
1206 The third argument PROPERTIES is a property list
1207 specifying the property values to add. If the optional fourth argument
1208 OBJECT is a buffer (or nil, which means the current buffer),
1209 START and END are buffer positions (integers or markers).
1210 If OBJECT is a string, START and END are 0-based indices into it.
1211 Return t if any property value actually changed, nil otherwise. */)
1212 (start, end, properties, object)
1213 Lisp_Object start, end, properties, object;
1215 register INTERVAL i, unchanged;
1216 register int s, len, modified = 0;
1217 struct gcpro gcpro1;
1219 properties = validate_plist (properties);
1220 if (NILP (properties))
1221 return Qnil;
1223 if (NILP (object))
1224 XSETBUFFER (object, current_buffer);
1226 i = validate_interval_range (object, &start, &end, hard);
1227 if (NULL_INTERVAL_P (i))
1228 return Qnil;
1230 s = XINT (start);
1231 len = XINT (end) - s;
1233 /* No need to protect OBJECT, because we GC only if it's a buffer,
1234 and live buffers are always protected. */
1235 GCPRO1 (properties);
1237 /* If we're not starting on an interval boundary, we have to
1238 split this interval. */
1239 if (i->position != s)
1241 /* If this interval already has the properties, we can
1242 skip it. */
1243 if (interval_has_all_properties (properties, i))
1245 int got = (LENGTH (i) - (s - i->position));
1246 if (got >= len)
1247 RETURN_UNGCPRO (Qnil);
1248 len -= got;
1249 i = next_interval (i);
1251 else
1253 unchanged = i;
1254 i = split_interval_right (unchanged, s - unchanged->position);
1255 copy_properties (unchanged, i);
1259 if (BUFFERP (object))
1260 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1262 /* We are at the beginning of interval I, with LEN chars to scan. */
1263 for (;;)
1265 if (i == 0)
1266 abort ();
1268 if (LENGTH (i) >= len)
1270 /* We can UNGCPRO safely here, because there will be just
1271 one more chance to gc, in the next call to add_properties,
1272 and after that we will not need PROPERTIES or OBJECT again. */
1273 UNGCPRO;
1275 if (interval_has_all_properties (properties, i))
1277 if (BUFFERP (object))
1278 signal_after_change (XINT (start), XINT (end) - XINT (start),
1279 XINT (end) - XINT (start));
1281 return modified ? Qt : Qnil;
1284 if (LENGTH (i) == len)
1286 add_properties (properties, i, object);
1287 if (BUFFERP (object))
1288 signal_after_change (XINT (start), XINT (end) - XINT (start),
1289 XINT (end) - XINT (start));
1290 return Qt;
1293 /* i doesn't have the properties, and goes past the change limit */
1294 unchanged = i;
1295 i = split_interval_left (unchanged, len);
1296 copy_properties (unchanged, i);
1297 add_properties (properties, i, object);
1298 if (BUFFERP (object))
1299 signal_after_change (XINT (start), XINT (end) - XINT (start),
1300 XINT (end) - XINT (start));
1301 return Qt;
1304 len -= LENGTH (i);
1305 modified += add_properties (properties, i, object);
1306 i = next_interval (i);
1310 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1312 DEFUN ("put-text-property", Fput_text_property,
1313 Sput_text_property, 4, 5, 0,
1314 doc: /* Set one property of the text from START to END.
1315 The third and fourth arguments PROPERTY and VALUE
1316 specify the property to add.
1317 If the optional fifth argument OBJECT is a buffer (or nil, which means
1318 the current buffer), START and END are buffer positions (integers or
1319 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1320 (start, end, property, value, object)
1321 Lisp_Object start, end, property, value, object;
1323 Fadd_text_properties (start, end,
1324 Fcons (property, Fcons (value, Qnil)),
1325 object);
1326 return Qnil;
1329 DEFUN ("set-text-properties", Fset_text_properties,
1330 Sset_text_properties, 3, 4, 0,
1331 doc: /* Completely replace properties of text from START to END.
1332 The third argument PROPERTIES is the new property list.
1333 If the optional fourth argument OBJECT is a buffer (or nil, which means
1334 the current buffer), START and END are buffer positions (integers or
1335 markers). If OBJECT is a string, START and END are 0-based indices into it.
1336 If PROPERTIES is nil, the effect is to remove all properties from
1337 the designated part of OBJECT. */)
1338 (start, end, properties, object)
1339 Lisp_Object start, end, properties, object;
1341 return set_text_properties (start, end, properties, object, Qt);
1345 /* Replace properties of text from START to END with new list of
1346 properties PROPERTIES. OBJECT is the buffer or string containing
1347 the text. OBJECT nil means use the current buffer.
1348 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1349 is nil if the function _detected_ that it did not replace any
1350 properties, non-nil otherwise. */
1352 Lisp_Object
1353 set_text_properties (start, end, properties, object, signal_after_change_p)
1354 Lisp_Object start, end, properties, object, signal_after_change_p;
1356 register INTERVAL i;
1357 Lisp_Object ostart, oend;
1359 ostart = start;
1360 oend = end;
1362 properties = validate_plist (properties);
1364 if (NILP (object))
1365 XSETBUFFER (object, current_buffer);
1367 /* If we want no properties for a whole string,
1368 get rid of its intervals. */
1369 if (NILP (properties) && STRINGP (object)
1370 && XFASTINT (start) == 0
1371 && XFASTINT (end) == SCHARS (object))
1373 if (! STRING_INTERVALS (object))
1374 return Qnil;
1376 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1377 return Qt;
1380 i = validate_interval_range (object, &start, &end, soft);
1382 if (NULL_INTERVAL_P (i))
1384 /* If buffer has no properties, and we want none, return now. */
1385 if (NILP (properties))
1386 return Qnil;
1388 /* Restore the original START and END values
1389 because validate_interval_range increments them for strings. */
1390 start = ostart;
1391 end = oend;
1393 i = validate_interval_range (object, &start, &end, hard);
1394 /* This can return if start == end. */
1395 if (NULL_INTERVAL_P (i))
1396 return Qnil;
1399 if (BUFFERP (object))
1400 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1402 set_text_properties_1 (start, end, properties, object, i);
1404 if (BUFFERP (object) && !NILP (signal_after_change_p))
1405 signal_after_change (XINT (start), XINT (end) - XINT (start),
1406 XINT (end) - XINT (start));
1407 return Qt;
1410 /* Replace properties of text from START to END with new list of
1411 properties PROPERTIES. BUFFER is the buffer containing
1412 the text. This does not obey any hooks.
1413 You can provide the interval that START is located in as I,
1414 or pass NULL for I and this function will find it.
1415 START and END can be in any order. */
1417 void
1418 set_text_properties_1 (start, end, properties, buffer, i)
1419 Lisp_Object start, end, properties, buffer;
1420 INTERVAL i;
1422 register INTERVAL prev_changed = NULL_INTERVAL;
1423 register int s, len;
1424 INTERVAL unchanged;
1426 s = XINT (start);
1427 len = XINT (end) - s;
1428 if (len == 0)
1429 return;
1430 if (len < 0)
1432 s = s + len;
1433 len = - len;
1436 if (i == 0)
1437 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1439 if (i->position != s)
1441 unchanged = i;
1442 i = split_interval_right (unchanged, s - unchanged->position);
1444 if (LENGTH (i) > len)
1446 copy_properties (unchanged, i);
1447 i = split_interval_left (i, len);
1448 set_properties (properties, i, buffer);
1449 return;
1452 set_properties (properties, i, buffer);
1454 if (LENGTH (i) == len)
1455 return;
1457 prev_changed = i;
1458 len -= LENGTH (i);
1459 i = next_interval (i);
1462 /* We are starting at the beginning of an interval, I */
1463 while (len > 0)
1465 if (i == 0)
1466 abort ();
1468 if (LENGTH (i) >= len)
1470 if (LENGTH (i) > len)
1471 i = split_interval_left (i, len);
1473 /* We have to call set_properties even if we are going to
1474 merge the intervals, so as to make the undo records
1475 and cause redisplay to happen. */
1476 set_properties (properties, i, buffer);
1477 if (!NULL_INTERVAL_P (prev_changed))
1478 merge_interval_left (i);
1479 return;
1482 len -= LENGTH (i);
1484 /* We have to call set_properties even if we are going to
1485 merge the intervals, so as to make the undo records
1486 and cause redisplay to happen. */
1487 set_properties (properties, i, buffer);
1488 if (NULL_INTERVAL_P (prev_changed))
1489 prev_changed = i;
1490 else
1491 prev_changed = i = merge_interval_left (i);
1493 i = next_interval (i);
1497 DEFUN ("remove-text-properties", Fremove_text_properties,
1498 Sremove_text_properties, 3, 4, 0,
1499 doc: /* Remove some properties from text from START to END.
1500 The third argument PROPERTIES is a property list
1501 whose property names specify the properties to remove.
1502 \(The values stored in PROPERTIES are ignored.)
1503 If the optional fourth argument OBJECT is a buffer (or nil, which means
1504 the current buffer), START and END are buffer positions (integers or
1505 markers). If OBJECT is a string, START and END are 0-based indices into it.
1506 Return t if any property was actually removed, nil otherwise.
1508 Use `set-text-properties' if you want to remove all text properties. */)
1509 (start, end, properties, object)
1510 Lisp_Object start, end, properties, object;
1512 register INTERVAL i, unchanged;
1513 register int s, len, modified = 0;
1515 if (NILP (object))
1516 XSETBUFFER (object, current_buffer);
1518 i = validate_interval_range (object, &start, &end, soft);
1519 if (NULL_INTERVAL_P (i))
1520 return Qnil;
1522 s = XINT (start);
1523 len = XINT (end) - s;
1525 if (i->position != s)
1527 /* No properties on this first interval -- return if
1528 it covers the entire region. */
1529 if (! interval_has_some_properties (properties, i))
1531 int got = (LENGTH (i) - (s - i->position));
1532 if (got >= len)
1533 return Qnil;
1534 len -= got;
1535 i = next_interval (i);
1537 /* Split away the beginning of this interval; what we don't
1538 want to modify. */
1539 else
1541 unchanged = i;
1542 i = split_interval_right (unchanged, s - unchanged->position);
1543 copy_properties (unchanged, i);
1547 if (BUFFERP (object))
1548 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1550 /* We are at the beginning of an interval, with len to scan */
1551 for (;;)
1553 if (i == 0)
1554 abort ();
1556 if (LENGTH (i) >= len)
1558 if (! interval_has_some_properties (properties, i))
1559 return modified ? Qt : Qnil;
1561 if (LENGTH (i) == len)
1563 remove_properties (properties, Qnil, i, object);
1564 if (BUFFERP (object))
1565 signal_after_change (XINT (start), XINT (end) - XINT (start),
1566 XINT (end) - XINT (start));
1567 return Qt;
1570 /* i has the properties, and goes past the change limit */
1571 unchanged = i;
1572 i = split_interval_left (i, len);
1573 copy_properties (unchanged, i);
1574 remove_properties (properties, Qnil, i, object);
1575 if (BUFFERP (object))
1576 signal_after_change (XINT (start), XINT (end) - XINT (start),
1577 XINT (end) - XINT (start));
1578 return Qt;
1581 len -= LENGTH (i);
1582 modified += remove_properties (properties, Qnil, i, object);
1583 i = next_interval (i);
1587 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1588 Sremove_list_of_text_properties, 3, 4, 0,
1589 doc: /* Remove some properties from text from START to END.
1590 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1591 If the optional fourth argument OBJECT is a buffer (or nil, which means
1592 the current buffer), START and END are buffer positions (integers or
1593 markers). If OBJECT is a string, START and END are 0-based indices into it.
1594 Return t if any property was actually removed, nil otherwise. */)
1595 (start, end, list_of_properties, object)
1596 Lisp_Object start, end, list_of_properties, object;
1598 register INTERVAL i, unchanged;
1599 register int s, len, modified = 0;
1600 Lisp_Object properties;
1601 properties = list_of_properties;
1603 if (NILP (object))
1604 XSETBUFFER (object, current_buffer);
1606 i = validate_interval_range (object, &start, &end, soft);
1607 if (NULL_INTERVAL_P (i))
1608 return Qnil;
1610 s = XINT (start);
1611 len = XINT (end) - s;
1613 if (i->position != s)
1615 /* No properties on this first interval -- return if
1616 it covers the entire region. */
1617 if (! interval_has_some_properties_list (properties, i))
1619 int got = (LENGTH (i) - (s - i->position));
1620 if (got >= len)
1621 return Qnil;
1622 len -= got;
1623 i = next_interval (i);
1625 /* Split away the beginning of this interval; what we don't
1626 want to modify. */
1627 else
1629 unchanged = i;
1630 i = split_interval_right (unchanged, s - unchanged->position);
1631 copy_properties (unchanged, i);
1635 /* We are at the beginning of an interval, with len to scan.
1636 The flag `modified' records if changes have been made.
1637 When object is a buffer, we must call modify_region before changes are
1638 made and signal_after_change when we are done.
1639 We call modify_region before calling remove_properties if modified == 0,
1640 and we call signal_after_change before returning if modified != 0. */
1641 for (;;)
1643 if (i == 0)
1644 abort ();
1646 if (LENGTH (i) >= len)
1648 if (! interval_has_some_properties_list (properties, i))
1649 if (modified)
1651 if (BUFFERP (object))
1652 signal_after_change (XINT (start), XINT (end) - XINT (start),
1653 XINT (end) - XINT (start));
1654 return Qt;
1656 else
1657 return Qnil;
1659 if (LENGTH (i) == len)
1661 if (!modified && BUFFERP (object))
1662 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1663 remove_properties (Qnil, properties, i, object);
1664 if (BUFFERP (object))
1665 signal_after_change (XINT (start), XINT (end) - XINT (start),
1666 XINT (end) - XINT (start));
1667 return Qt;
1670 /* i has the properties, and goes past the change limit */
1671 unchanged = i;
1672 i = split_interval_left (i, len);
1673 copy_properties (unchanged, i);
1674 if (!modified && BUFFERP (object))
1675 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1676 remove_properties (Qnil, properties, i, object);
1677 if (BUFFERP (object))
1678 signal_after_change (XINT (start), XINT (end) - XINT (start),
1679 XINT (end) - XINT (start));
1680 return Qt;
1683 if (interval_has_some_properties_list (properties, i))
1685 if (!modified && BUFFERP (object))
1686 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1687 remove_properties (Qnil, properties, i, object);
1688 modified = 1;
1690 len -= LENGTH (i);
1691 i = next_interval (i);
1695 DEFUN ("text-property-any", Ftext_property_any,
1696 Stext_property_any, 4, 5, 0,
1697 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1698 If so, return the position of the first character whose property PROPERTY
1699 is `eq' to VALUE. Otherwise return nil.
1700 If the optional fifth argument OBJECT is a buffer (or nil, which means
1701 the current buffer), START and END are buffer positions (integers or
1702 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1703 (start, end, property, value, object)
1704 Lisp_Object start, end, property, value, object;
1706 register INTERVAL i;
1707 register int e, pos;
1709 if (NILP (object))
1710 XSETBUFFER (object, current_buffer);
1711 i = validate_interval_range (object, &start, &end, soft);
1712 if (NULL_INTERVAL_P (i))
1713 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1714 e = XINT (end);
1716 while (! NULL_INTERVAL_P (i))
1718 if (i->position >= e)
1719 break;
1720 if (EQ (textget (i->plist, property), value))
1722 pos = i->position;
1723 if (pos < XINT (start))
1724 pos = XINT (start);
1725 return make_number (pos);
1727 i = next_interval (i);
1729 return Qnil;
1732 DEFUN ("text-property-not-all", Ftext_property_not_all,
1733 Stext_property_not_all, 4, 5, 0,
1734 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1735 If so, return the position of the first character whose property PROPERTY
1736 is not `eq' to VALUE. Otherwise, return nil.
1737 If the optional fifth argument OBJECT is a buffer (or nil, which means
1738 the current buffer), START and END are buffer positions (integers or
1739 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1740 (start, end, property, value, object)
1741 Lisp_Object start, end, property, value, object;
1743 register INTERVAL i;
1744 register int s, e;
1746 if (NILP (object))
1747 XSETBUFFER (object, current_buffer);
1748 i = validate_interval_range (object, &start, &end, soft);
1749 if (NULL_INTERVAL_P (i))
1750 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1751 s = XINT (start);
1752 e = XINT (end);
1754 while (! NULL_INTERVAL_P (i))
1756 if (i->position >= e)
1757 break;
1758 if (! EQ (textget (i->plist, property), value))
1760 if (i->position > s)
1761 s = i->position;
1762 return make_number (s);
1764 i = next_interval (i);
1766 return Qnil;
1770 /* Return the direction from which the text-property PROP would be
1771 inherited by any new text inserted at POS: 1 if it would be
1772 inherited from the char after POS, -1 if it would be inherited from
1773 the char before POS, and 0 if from neither.
1774 BUFFER can be either a buffer or nil (meaning current buffer). */
1777 text_property_stickiness (prop, pos, buffer)
1778 Lisp_Object prop, pos, buffer;
1780 Lisp_Object prev_pos, front_sticky;
1781 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1783 if (NILP (buffer))
1784 XSETBUFFER (buffer, current_buffer);
1786 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1787 /* Consider previous character. */
1789 Lisp_Object rear_non_sticky;
1791 prev_pos = make_number (XINT (pos) - 1);
1792 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1794 if (!NILP (CONSP (rear_non_sticky)
1795 ? Fmemq (prop, rear_non_sticky)
1796 : rear_non_sticky))
1797 /* PROP is rear-non-sticky. */
1798 is_rear_sticky = 0;
1800 else
1801 return 0;
1803 /* Consider following character. */
1804 /* This signals an arg-out-of-range error if pos is outside the
1805 buffer's accessible range. */
1806 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1808 if (EQ (front_sticky, Qt)
1809 || (CONSP (front_sticky)
1810 && !NILP (Fmemq (prop, front_sticky))))
1811 /* PROP is inherited from after. */
1812 is_front_sticky = 1;
1814 /* Simple cases, where the properties are consistent. */
1815 if (is_rear_sticky && !is_front_sticky)
1816 return -1;
1817 else if (!is_rear_sticky && is_front_sticky)
1818 return 1;
1819 else if (!is_rear_sticky && !is_front_sticky)
1820 return 0;
1822 /* The stickiness properties are inconsistent, so we have to
1823 disambiguate. Basically, rear-sticky wins, _except_ if the
1824 property that would be inherited has a value of nil, in which case
1825 front-sticky wins. */
1826 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1827 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1828 return 1;
1829 else
1830 return -1;
1834 /* I don't think this is the right interface to export; how often do you
1835 want to do something like this, other than when you're copying objects
1836 around?
1838 I think it would be better to have a pair of functions, one which
1839 returns the text properties of a region as a list of ranges and
1840 plists, and another which applies such a list to another object. */
1842 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1843 SRC and DEST may each refer to strings or buffers.
1844 Optional sixth argument PROP causes only that property to be copied.
1845 Properties are copied to DEST as if by `add-text-properties'.
1846 Return t if any property value actually changed, nil otherwise. */
1848 /* Note this can GC when DEST is a buffer. */
1850 Lisp_Object
1851 copy_text_properties (start, end, src, pos, dest, prop)
1852 Lisp_Object start, end, src, pos, dest, prop;
1854 INTERVAL i;
1855 Lisp_Object res;
1856 Lisp_Object stuff;
1857 Lisp_Object plist;
1858 int s, e, e2, p, len, modified = 0;
1859 struct gcpro gcpro1, gcpro2;
1861 i = validate_interval_range (src, &start, &end, soft);
1862 if (NULL_INTERVAL_P (i))
1863 return Qnil;
1865 CHECK_NUMBER_COERCE_MARKER (pos);
1867 Lisp_Object dest_start, dest_end;
1869 dest_start = pos;
1870 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1871 /* Apply this to a copy of pos; it will try to increment its arguments,
1872 which we don't want. */
1873 validate_interval_range (dest, &dest_start, &dest_end, soft);
1876 s = XINT (start);
1877 e = XINT (end);
1878 p = XINT (pos);
1880 stuff = Qnil;
1882 while (s < e)
1884 e2 = i->position + LENGTH (i);
1885 if (e2 > e)
1886 e2 = e;
1887 len = e2 - s;
1889 plist = i->plist;
1890 if (! NILP (prop))
1891 while (! NILP (plist))
1893 if (EQ (Fcar (plist), prop))
1895 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1896 break;
1898 plist = Fcdr (Fcdr (plist));
1900 if (! NILP (plist))
1902 /* Must defer modifications to the interval tree in case src
1903 and dest refer to the same string or buffer. */
1904 stuff = Fcons (Fcons (make_number (p),
1905 Fcons (make_number (p + len),
1906 Fcons (plist, Qnil))),
1907 stuff);
1910 i = next_interval (i);
1911 if (NULL_INTERVAL_P (i))
1912 break;
1914 p += len;
1915 s = i->position;
1918 GCPRO2 (stuff, dest);
1920 while (! NILP (stuff))
1922 res = Fcar (stuff);
1923 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1924 Fcar (Fcdr (Fcdr (res))), dest);
1925 if (! NILP (res))
1926 modified++;
1927 stuff = Fcdr (stuff);
1930 UNGCPRO;
1932 return modified ? Qt : Qnil;
1936 /* Return a list representing the text properties of OBJECT between
1937 START and END. if PROP is non-nil, report only on that property.
1938 Each result list element has the form (S E PLIST), where S and E
1939 are positions in OBJECT and PLIST is a property list containing the
1940 text properties of OBJECT between S and E. Value is nil if OBJECT
1941 doesn't contain text properties between START and END. */
1943 Lisp_Object
1944 text_property_list (object, start, end, prop)
1945 Lisp_Object object, start, end, prop;
1947 struct interval *i;
1948 Lisp_Object result;
1950 result = Qnil;
1952 i = validate_interval_range (object, &start, &end, soft);
1953 if (!NULL_INTERVAL_P (i))
1955 int s = XINT (start);
1956 int e = XINT (end);
1958 while (s < e)
1960 int interval_end, len;
1961 Lisp_Object plist;
1963 interval_end = i->position + LENGTH (i);
1964 if (interval_end > e)
1965 interval_end = e;
1966 len = interval_end - s;
1968 plist = i->plist;
1970 if (!NILP (prop))
1971 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1972 if (EQ (XCAR (plist), prop))
1974 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1975 break;
1978 if (!NILP (plist))
1979 result = Fcons (Fcons (make_number (s),
1980 Fcons (make_number (s + len),
1981 Fcons (plist, Qnil))),
1982 result);
1984 i = next_interval (i);
1985 if (NULL_INTERVAL_P (i))
1986 break;
1987 s = i->position;
1991 return result;
1995 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1996 (START END PLIST), where START and END are positions and PLIST is a
1997 property list containing the text properties to add. Adjust START
1998 and END positions by DELTA before adding properties. Value is
1999 non-zero if OBJECT was modified. */
2002 add_text_properties_from_list (object, list, delta)
2003 Lisp_Object object, list, delta;
2005 struct gcpro gcpro1, gcpro2;
2006 int modified_p = 0;
2008 GCPRO2 (list, object);
2010 for (; CONSP (list); list = XCDR (list))
2012 Lisp_Object item, start, end, plist, tem;
2014 item = XCAR (list);
2015 start = make_number (XINT (XCAR (item)) + XINT (delta));
2016 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
2017 plist = XCAR (XCDR (XCDR (item)));
2019 tem = Fadd_text_properties (start, end, plist, object);
2020 if (!NILP (tem))
2021 modified_p = 1;
2024 UNGCPRO;
2025 return modified_p;
2030 /* Modify end-points of ranges in LIST destructively. LIST is a list
2031 as returned from text_property_list. Change end-points equal to
2032 OLD_END to NEW_END. */
2034 void
2035 extend_property_ranges (list, old_end, new_end)
2036 Lisp_Object list, old_end, new_end;
2038 for (; CONSP (list); list = XCDR (list))
2040 Lisp_Object item, end;
2042 item = XCAR (list);
2043 end = XCAR (XCDR (item));
2045 if (EQ (end, old_end))
2046 XSETCAR (XCDR (item), new_end);
2052 /* Call the modification hook functions in LIST, each with START and END. */
2054 static void
2055 call_mod_hooks (list, start, end)
2056 Lisp_Object list, start, end;
2058 struct gcpro gcpro1;
2059 GCPRO1 (list);
2060 while (!NILP (list))
2062 call2 (Fcar (list), start, end);
2063 list = Fcdr (list);
2065 UNGCPRO;
2068 /* Check for read-only intervals between character positions START ... END,
2069 in BUF, and signal an error if we find one.
2071 Then check for any modification hooks in the range.
2072 Create a list of all these hooks in lexicographic order,
2073 eliminating consecutive extra copies of the same hook. Then call
2074 those hooks in order, with START and END - 1 as arguments. */
2076 void
2077 verify_interval_modification (buf, start, end)
2078 struct buffer *buf;
2079 int start, end;
2081 register INTERVAL intervals = BUF_INTERVALS (buf);
2082 register INTERVAL i;
2083 Lisp_Object hooks;
2084 register Lisp_Object prev_mod_hooks;
2085 Lisp_Object mod_hooks;
2086 struct gcpro gcpro1;
2088 hooks = Qnil;
2089 prev_mod_hooks = Qnil;
2090 mod_hooks = Qnil;
2092 interval_insert_behind_hooks = Qnil;
2093 interval_insert_in_front_hooks = Qnil;
2095 if (NULL_INTERVAL_P (intervals))
2096 return;
2098 if (start > end)
2100 int temp = start;
2101 start = end;
2102 end = temp;
2105 /* For an insert operation, check the two chars around the position. */
2106 if (start == end)
2108 INTERVAL prev = NULL;
2109 Lisp_Object before, after;
2111 /* Set I to the interval containing the char after START,
2112 and PREV to the interval containing the char before START.
2113 Either one may be null. They may be equal. */
2114 i = find_interval (intervals, start);
2116 if (start == BUF_BEGV (buf))
2117 prev = 0;
2118 else if (i->position == start)
2119 prev = previous_interval (i);
2120 else if (i->position < start)
2121 prev = i;
2122 if (start == BUF_ZV (buf))
2123 i = 0;
2125 /* If Vinhibit_read_only is set and is not a list, we can
2126 skip the read_only checks. */
2127 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2129 /* If I and PREV differ we need to check for the read-only
2130 property together with its stickiness. If either I or
2131 PREV are 0, this check is all we need.
2132 We have to take special care, since read-only may be
2133 indirectly defined via the category property. */
2134 if (i != prev)
2136 if (! NULL_INTERVAL_P (i))
2138 after = textget (i->plist, Qread_only);
2140 /* If interval I is read-only and read-only is
2141 front-sticky, inhibit insertion.
2142 Check for read-only as well as category. */
2143 if (! NILP (after)
2144 && NILP (Fmemq (after, Vinhibit_read_only)))
2146 Lisp_Object tem;
2148 tem = textget (i->plist, Qfront_sticky);
2149 if (TMEM (Qread_only, tem)
2150 || (NILP (Fplist_get (i->plist, Qread_only))
2151 && TMEM (Qcategory, tem)))
2152 text_read_only (after);
2156 if (! NULL_INTERVAL_P (prev))
2158 before = textget (prev->plist, Qread_only);
2160 /* If interval PREV is read-only and read-only isn't
2161 rear-nonsticky, inhibit insertion.
2162 Check for read-only as well as category. */
2163 if (! NILP (before)
2164 && NILP (Fmemq (before, Vinhibit_read_only)))
2166 Lisp_Object tem;
2168 tem = textget (prev->plist, Qrear_nonsticky);
2169 if (! TMEM (Qread_only, tem)
2170 && (! NILP (Fplist_get (prev->plist,Qread_only))
2171 || ! TMEM (Qcategory, tem)))
2172 text_read_only (before);
2176 else if (! NULL_INTERVAL_P (i))
2178 after = textget (i->plist, Qread_only);
2180 /* If interval I is read-only and read-only is
2181 front-sticky, inhibit insertion.
2182 Check for read-only as well as category. */
2183 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2185 Lisp_Object tem;
2187 tem = textget (i->plist, Qfront_sticky);
2188 if (TMEM (Qread_only, tem)
2189 || (NILP (Fplist_get (i->plist, Qread_only))
2190 && TMEM (Qcategory, tem)))
2191 text_read_only (after);
2193 tem = textget (prev->plist, Qrear_nonsticky);
2194 if (! TMEM (Qread_only, tem)
2195 && (! NILP (Fplist_get (prev->plist, Qread_only))
2196 || ! TMEM (Qcategory, tem)))
2197 text_read_only (after);
2202 /* Run both insert hooks (just once if they're the same). */
2203 if (!NULL_INTERVAL_P (prev))
2204 interval_insert_behind_hooks
2205 = textget (prev->plist, Qinsert_behind_hooks);
2206 if (!NULL_INTERVAL_P (i))
2207 interval_insert_in_front_hooks
2208 = textget (i->plist, Qinsert_in_front_hooks);
2210 else
2212 /* Loop over intervals on or next to START...END,
2213 collecting their hooks. */
2215 i = find_interval (intervals, start);
2218 if (! INTERVAL_WRITABLE_P (i))
2219 text_read_only (textget (i->plist, Qread_only));
2221 if (!inhibit_modification_hooks)
2223 mod_hooks = textget (i->plist, Qmodification_hooks);
2224 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2226 hooks = Fcons (mod_hooks, hooks);
2227 prev_mod_hooks = mod_hooks;
2231 i = next_interval (i);
2233 /* Keep going thru the interval containing the char before END. */
2234 while (! NULL_INTERVAL_P (i) && i->position < end);
2236 if (!inhibit_modification_hooks)
2238 GCPRO1 (hooks);
2239 hooks = Fnreverse (hooks);
2240 while (! EQ (hooks, Qnil))
2242 call_mod_hooks (Fcar (hooks), make_number (start),
2243 make_number (end));
2244 hooks = Fcdr (hooks);
2246 UNGCPRO;
2251 /* Run the interval hooks for an insertion on character range START ... END.
2252 verify_interval_modification chose which hooks to run;
2253 this function is called after the insertion happens
2254 so it can indicate the range of inserted text. */
2256 void
2257 report_interval_modification (start, end)
2258 Lisp_Object start, end;
2260 if (! NILP (interval_insert_behind_hooks))
2261 call_mod_hooks (interval_insert_behind_hooks, start, end);
2262 if (! NILP (interval_insert_in_front_hooks)
2263 && ! EQ (interval_insert_in_front_hooks,
2264 interval_insert_behind_hooks))
2265 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2268 void
2269 syms_of_textprop ()
2271 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2272 doc: /* Property-list used as default values.
2273 The value of a property in this list is seen as the value for every
2274 character that does not have its own value for that property. */);
2275 Vdefault_text_properties = Qnil;
2277 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2278 doc: /* Alist of alternative properties for properties without a value.
2279 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2280 If a piece of text has no direct value for a particular property, then
2281 this alist is consulted. If that property appears in the alist, then
2282 the first non-nil value from the associated alternative properties is
2283 returned. */);
2284 Vchar_property_alias_alist = Qnil;
2286 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2287 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2288 This also inhibits the use of the `intangible' text property. */);
2289 Vinhibit_point_motion_hooks = Qnil;
2291 DEFVAR_LISP ("text-property-default-nonsticky",
2292 &Vtext_property_default_nonsticky,
2293 doc: /* Alist of properties vs the corresponding non-stickinesses.
2294 Each element has the form (PROPERTY . NONSTICKINESS).
2296 If a character in a buffer has PROPERTY, new text inserted adjacent to
2297 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2298 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2299 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2300 /* Text property `syntax-table' should be nonsticky by default. */
2301 Vtext_property_default_nonsticky
2302 = Fcons (Fcons (intern ("syntax-table"), Qt), Qnil);
2304 staticpro (&interval_insert_behind_hooks);
2305 staticpro (&interval_insert_in_front_hooks);
2306 interval_insert_behind_hooks = Qnil;
2307 interval_insert_in_front_hooks = Qnil;
2310 /* Common attributes one might give text */
2312 staticpro (&Qforeground);
2313 Qforeground = intern ("foreground");
2314 staticpro (&Qbackground);
2315 Qbackground = intern ("background");
2316 staticpro (&Qfont);
2317 Qfont = intern ("font");
2318 staticpro (&Qstipple);
2319 Qstipple = intern ("stipple");
2320 staticpro (&Qunderline);
2321 Qunderline = intern ("underline");
2322 staticpro (&Qread_only);
2323 Qread_only = intern ("read-only");
2324 staticpro (&Qinvisible);
2325 Qinvisible = intern ("invisible");
2326 staticpro (&Qintangible);
2327 Qintangible = intern ("intangible");
2328 staticpro (&Qcategory);
2329 Qcategory = intern ("category");
2330 staticpro (&Qlocal_map);
2331 Qlocal_map = intern ("local-map");
2332 staticpro (&Qfront_sticky);
2333 Qfront_sticky = intern ("front-sticky");
2334 staticpro (&Qrear_nonsticky);
2335 Qrear_nonsticky = intern ("rear-nonsticky");
2336 staticpro (&Qmouse_face);
2337 Qmouse_face = intern ("mouse-face");
2338 staticpro (&Qminibuffer_prompt);
2339 Qminibuffer_prompt = intern ("minibuffer-prompt");
2341 /* Properties that text might use to specify certain actions */
2343 staticpro (&Qmouse_left);
2344 Qmouse_left = intern ("mouse-left");
2345 staticpro (&Qmouse_entered);
2346 Qmouse_entered = intern ("mouse-entered");
2347 staticpro (&Qpoint_left);
2348 Qpoint_left = intern ("point-left");
2349 staticpro (&Qpoint_entered);
2350 Qpoint_entered = intern ("point-entered");
2352 defsubr (&Stext_properties_at);
2353 defsubr (&Sget_text_property);
2354 defsubr (&Sget_char_property);
2355 defsubr (&Sget_char_property_and_overlay);
2356 defsubr (&Snext_char_property_change);
2357 defsubr (&Sprevious_char_property_change);
2358 defsubr (&Snext_single_char_property_change);
2359 defsubr (&Sprevious_single_char_property_change);
2360 defsubr (&Snext_property_change);
2361 defsubr (&Snext_single_property_change);
2362 defsubr (&Sprevious_property_change);
2363 defsubr (&Sprevious_single_property_change);
2364 defsubr (&Sadd_text_properties);
2365 defsubr (&Sput_text_property);
2366 defsubr (&Sset_text_properties);
2367 defsubr (&Sremove_text_properties);
2368 defsubr (&Sremove_list_of_text_properties);
2369 defsubr (&Stext_property_any);
2370 defsubr (&Stext_property_not_all);
2371 /* defsubr (&Serase_text_properties); */
2372 /* defsubr (&Scopy_text_properties); */
2375 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2376 (do not change this comment) */