* NEWS: Mention --enable-checking is now supported on Windows.
[emacs.git] / src / textprop.c
blob0e398e41e4ac4fd31197df3abac528fde9987359
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, 2010 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 <setjmp.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
61 Lisp_Object Qminibuffer_prompt;
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;
81 static void text_read_only (Lisp_Object) NO_RETURN;
84 /* Signal a `text-read-only' error. This function makes it easier
85 to capture that error in GDB by putting a breakpoint on it. */
87 static void
88 text_read_only (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 (Lisp_Object object, Lisp_Object *begin, Lisp_Object *end, int force)
127 register INTERVAL i;
128 int searchpos;
130 CHECK_STRING_OR_BUFFER (object);
131 CHECK_NUMBER_COERCE_MARKER (*begin);
132 CHECK_NUMBER_COERCE_MARKER (*end);
134 /* If we are asked for a point, but from a subr which operates
135 on a range, then return nothing. */
136 if (EQ (*begin, *end) && begin != end)
137 return NULL_INTERVAL;
139 if (XINT (*begin) > XINT (*end))
141 Lisp_Object n;
142 n = *begin;
143 *begin = *end;
144 *end = n;
147 if (BUFFERP (object))
149 register struct buffer *b = XBUFFER (object);
151 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
152 && XINT (*end) <= BUF_ZV (b)))
153 args_out_of_range (*begin, *end);
154 i = BUF_INTERVALS (b);
156 /* If there's no text, there are no properties. */
157 if (BUF_BEGV (b) == BUF_ZV (b))
158 return NULL_INTERVAL;
160 searchpos = XINT (*begin);
162 else
164 int len = SCHARS (object);
166 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
167 && XINT (*end) <= len))
168 args_out_of_range (*begin, *end);
169 XSETFASTINT (*begin, XFASTINT (*begin));
170 if (begin != end)
171 XSETFASTINT (*end, XFASTINT (*end));
172 i = STRING_INTERVALS (object);
174 if (len == 0)
175 return NULL_INTERVAL;
177 searchpos = XINT (*begin);
180 if (NULL_INTERVAL_P (i))
181 return (force ? create_root_interval (object) : i);
183 return find_interval (i, searchpos);
186 /* Validate LIST as a property list. If LIST is not a list, then
187 make one consisting of (LIST nil). Otherwise, verify that LIST
188 is even numbered and thus suitable as a plist. */
190 static Lisp_Object
191 validate_plist (Lisp_Object list)
193 if (NILP (list))
194 return Qnil;
196 if (CONSP (list))
198 register int i;
199 register Lisp_Object tail;
200 for (i = 0, tail = list; CONSP (tail); i++)
202 tail = XCDR (tail);
203 QUIT;
205 if (i & 1)
206 error ("Odd length text property list");
207 return list;
210 return Fcons (list, Fcons (Qnil, Qnil));
213 /* Return nonzero if interval I has all the properties,
214 with the same values, of list PLIST. */
216 static int
217 interval_has_all_properties (Lisp_Object plist, INTERVAL i)
219 register Lisp_Object tail1, tail2, sym1;
220 register int found;
222 /* Go through each element of PLIST. */
223 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
225 sym1 = XCAR (tail1);
226 found = 0;
228 /* Go through I's plist, looking for sym1 */
229 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
230 if (EQ (sym1, XCAR (tail2)))
232 /* Found the same property on both lists. If the
233 values are unequal, return zero. */
234 if (! EQ (Fcar (XCDR (tail1)), Fcar (XCDR (tail2))))
235 return 0;
237 /* Property has same value on both lists; go to next one. */
238 found = 1;
239 break;
242 if (! found)
243 return 0;
246 return 1;
249 /* Return nonzero if the plist of interval I has any of the
250 properties of PLIST, regardless of their values. */
252 static INLINE int
253 interval_has_some_properties (Lisp_Object plist, INTERVAL i)
255 register Lisp_Object tail1, tail2, sym;
257 /* Go through each element of PLIST. */
258 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
260 sym = XCAR (tail1);
262 /* Go through i's plist, looking for tail1 */
263 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
264 if (EQ (sym, XCAR (tail2)))
265 return 1;
268 return 0;
271 /* Return nonzero if the plist of interval I has any of the
272 property names in LIST, regardless of their values. */
274 static INLINE int
275 interval_has_some_properties_list (Lisp_Object list, INTERVAL i)
277 register Lisp_Object tail1, tail2, sym;
279 /* Go through each element of LIST. */
280 for (tail1 = list; CONSP (tail1); tail1 = XCDR (tail1))
282 sym = Fcar (tail1);
284 /* Go through i's plist, looking for tail1 */
285 for (tail2 = i->plist; CONSP (tail2); tail2 = XCDR (XCDR (tail2)))
286 if (EQ (sym, XCAR (tail2)))
287 return 1;
290 return 0;
293 /* Changing the plists of individual intervals. */
295 /* Return the value of PROP in property-list PLIST, or Qunbound if it
296 has none. */
297 static Lisp_Object
298 property_value (Lisp_Object plist, Lisp_Object prop)
300 Lisp_Object value;
302 while (PLIST_ELT_P (plist, value))
303 if (EQ (XCAR (plist), prop))
304 return XCAR (value);
305 else
306 plist = XCDR (value);
308 return Qunbound;
311 /* Set the properties of INTERVAL to PROPERTIES,
312 and record undo info for the previous values.
313 OBJECT is the string or buffer that INTERVAL belongs to. */
315 static void
316 set_properties (Lisp_Object properties, INTERVAL interval, Lisp_Object object)
318 Lisp_Object sym, value;
320 if (BUFFERP (object))
322 /* For each property in the old plist which is missing from PROPERTIES,
323 or has a different value in PROPERTIES, make an undo record. */
324 for (sym = interval->plist;
325 PLIST_ELT_P (sym, value);
326 sym = XCDR (value))
327 if (! EQ (property_value (properties, XCAR (sym)),
328 XCAR (value)))
330 record_property_change (interval->position, LENGTH (interval),
331 XCAR (sym), XCAR (value),
332 object);
335 /* For each new property that has no value at all in the old plist,
336 make an undo record binding it to nil, so it will be removed. */
337 for (sym = properties;
338 PLIST_ELT_P (sym, value);
339 sym = XCDR (value))
340 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
342 record_property_change (interval->position, LENGTH (interval),
343 XCAR (sym), Qnil,
344 object);
348 /* Store new properties. */
349 interval->plist = Fcopy_sequence (properties);
352 /* Add the properties of PLIST to the interval I, or set
353 the value of I's property to the value of the property on PLIST
354 if they are different.
356 OBJECT should be the string or buffer the interval is in.
358 Return nonzero if this changes I (i.e., if any members of PLIST
359 are actually added to I's plist) */
361 static int
362 add_properties (Lisp_Object plist, INTERVAL i, Lisp_Object object)
364 Lisp_Object tail1, tail2, sym1, val1;
365 register int changed = 0;
366 register int found;
367 struct gcpro gcpro1, gcpro2, gcpro3;
369 tail1 = plist;
370 sym1 = Qnil;
371 val1 = Qnil;
372 /* No need to protect OBJECT, because we can GC only in the case
373 where it is a buffer, and live buffers are always protected.
374 I and its plist are also protected, via OBJECT. */
375 GCPRO3 (tail1, sym1, val1);
377 /* Go through each element of PLIST. */
378 for (tail1 = plist; CONSP (tail1); tail1 = Fcdr (XCDR (tail1)))
380 sym1 = XCAR (tail1);
381 val1 = Fcar (XCDR (tail1));
382 found = 0;
384 /* Go through I's plist, looking for sym1 */
385 for (tail2 = i->plist; CONSP (tail2); tail2 = Fcdr (XCDR (tail2)))
386 if (EQ (sym1, XCAR (tail2)))
388 /* No need to gcpro, because tail2 protects this
389 and it must be a cons cell (we get an error otherwise). */
390 register Lisp_Object this_cdr;
392 this_cdr = XCDR (tail2);
393 /* Found the property. Now check its value. */
394 found = 1;
396 /* The properties have the same value on both lists.
397 Continue to the next property. */
398 if (EQ (val1, Fcar (this_cdr)))
399 break;
401 /* Record this change in the buffer, for undo purposes. */
402 if (BUFFERP (object))
404 record_property_change (i->position, LENGTH (i),
405 sym1, Fcar (this_cdr), object);
408 /* I's property has a different value -- change it */
409 Fsetcar (this_cdr, val1);
410 changed++;
411 break;
414 if (! found)
416 /* Record this change in the buffer, for undo purposes. */
417 if (BUFFERP (object))
419 record_property_change (i->position, LENGTH (i),
420 sym1, Qnil, object);
422 i->plist = Fcons (sym1, Fcons (val1, i->plist));
423 changed++;
427 UNGCPRO;
429 return changed;
432 /* For any members of PLIST, or LIST,
433 which are properties of I, remove them from I's plist.
434 (If PLIST is non-nil, use that, otherwise use LIST.)
435 OBJECT is the string or buffer containing I. */
437 static int
438 remove_properties (Lisp_Object plist, Lisp_Object list, INTERVAL i, Lisp_Object object)
440 register Lisp_Object tail1, tail2, sym, current_plist;
441 register int changed = 0;
443 /* Nonzero means tail1 is a plist, otherwise it is a list. */
444 int use_plist;
446 current_plist = i->plist;
448 if (! NILP (plist))
449 tail1 = plist, use_plist = 1;
450 else
451 tail1 = list, use_plist = 0;
453 /* Go through each element of LIST or PLIST. */
454 while (CONSP (tail1))
456 sym = XCAR (tail1);
458 /* First, remove the symbol if it's at the head of the list */
459 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
461 if (BUFFERP (object))
462 record_property_change (i->position, LENGTH (i),
463 sym, XCAR (XCDR (current_plist)),
464 object);
466 current_plist = XCDR (XCDR (current_plist));
467 changed++;
470 /* Go through I's plist, looking for SYM. */
471 tail2 = current_plist;
472 while (! NILP (tail2))
474 register Lisp_Object this;
475 this = XCDR (XCDR (tail2));
476 if (CONSP (this) && EQ (sym, XCAR (this)))
478 if (BUFFERP (object))
479 record_property_change (i->position, LENGTH (i),
480 sym, XCAR (XCDR (this)), object);
482 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
483 changed++;
485 tail2 = this;
488 /* Advance thru TAIL1 one way or the other. */
489 tail1 = XCDR (tail1);
490 if (use_plist && CONSP (tail1))
491 tail1 = XCDR (tail1);
494 if (changed)
495 i->plist = current_plist;
496 return changed;
499 #if 0
500 /* Remove all properties from interval I. Return non-zero
501 if this changes the interval. */
503 static INLINE int
504 erase_properties (i)
505 INTERVAL i;
507 if (NILP (i->plist))
508 return 0;
510 i->plist = Qnil;
511 return 1;
513 #endif
515 /* Returns the interval of POSITION in OBJECT.
516 POSITION is BEG-based. */
518 INTERVAL
519 interval_of (int position, Lisp_Object object)
521 register INTERVAL i;
522 int beg, end;
524 if (NILP (object))
525 XSETBUFFER (object, current_buffer);
526 else if (EQ (object, Qt))
527 return NULL_INTERVAL;
529 CHECK_STRING_OR_BUFFER (object);
531 if (BUFFERP (object))
533 register struct buffer *b = XBUFFER (object);
535 beg = BUF_BEGV (b);
536 end = BUF_ZV (b);
537 i = BUF_INTERVALS (b);
539 else
541 beg = 0;
542 end = SCHARS (object);
543 i = STRING_INTERVALS (object);
546 if (!(beg <= position && position <= end))
547 args_out_of_range (make_number (position), make_number (position));
548 if (beg == end || NULL_INTERVAL_P (i))
549 return NULL_INTERVAL;
551 return find_interval (i, position);
554 DEFUN ("text-properties-at", Ftext_properties_at,
555 Stext_properties_at, 1, 2, 0,
556 doc: /* Return the list of properties of the character at POSITION in OBJECT.
557 If the optional second argument OBJECT is a buffer (or nil, which means
558 the current buffer), POSITION is a buffer position (integer or marker).
559 If OBJECT is a string, POSITION is a 0-based index into it.
560 If POSITION is at the end of OBJECT, the value is nil. */)
561 (Lisp_Object position, Lisp_Object object)
563 register INTERVAL i;
565 if (NILP (object))
566 XSETBUFFER (object, current_buffer);
568 i = validate_interval_range (object, &position, &position, soft);
569 if (NULL_INTERVAL_P (i))
570 return Qnil;
571 /* If POSITION is at the end of the interval,
572 it means it's the end of OBJECT.
573 There are no properties at the very end,
574 since no character follows. */
575 if (XINT (position) == LENGTH (i) + i->position)
576 return Qnil;
578 return i->plist;
581 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
582 doc: /* Return the value of POSITION's property PROP, in OBJECT.
583 OBJECT is optional and defaults to the current buffer.
584 If POSITION is at the end of OBJECT, the value is nil. */)
585 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
587 return textget (Ftext_properties_at (position, object), prop);
590 /* Return the value of char's property PROP, in OBJECT at POSITION.
591 OBJECT is optional and defaults to the current buffer.
592 If OVERLAY is non-0, then in the case that the returned property is from
593 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
594 returned in *OVERLAY.
595 If POSITION is at the end of OBJECT, the value is nil.
596 If OBJECT is a buffer, then overlay properties are considered as well as
597 text properties.
598 If OBJECT is a window, then that window's buffer is used, but
599 window-specific overlays are considered only if they are associated
600 with OBJECT. */
601 Lisp_Object
602 get_char_property_and_overlay (Lisp_Object position, register Lisp_Object prop, Lisp_Object object, Lisp_Object *overlay)
604 struct window *w = 0;
606 CHECK_NUMBER_COERCE_MARKER (position);
608 if (NILP (object))
609 XSETBUFFER (object, current_buffer);
611 if (WINDOWP (object))
613 w = XWINDOW (object);
614 object = w->buffer;
616 if (BUFFERP (object))
618 int noverlays;
619 Lisp_Object *overlay_vec;
620 struct buffer *obuf = current_buffer;
622 if (XINT (position) < BUF_BEGV (XBUFFER (object))
623 || XINT (position) > BUF_ZV (XBUFFER (object)))
624 xsignal1 (Qargs_out_of_range, position);
626 set_buffer_temp (XBUFFER (object));
628 GET_OVERLAYS_AT (XINT (position), overlay_vec, noverlays, NULL, 0);
629 noverlays = sort_overlays (overlay_vec, noverlays, w);
631 set_buffer_temp (obuf);
633 /* Now check the overlays in order of decreasing priority. */
634 while (--noverlays >= 0)
636 Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
637 if (!NILP (tem))
639 if (overlay)
640 /* Return the overlay we got the property from. */
641 *overlay = overlay_vec[noverlays];
642 return tem;
647 if (overlay)
648 /* Indicate that the return value is not from an overlay. */
649 *overlay = Qnil;
651 /* Not a buffer, or no appropriate overlay, so fall through to the
652 simpler case. */
653 return Fget_text_property (position, prop, object);
656 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
657 doc: /* Return the value of POSITION's property PROP, in OBJECT.
658 Both overlay properties and text properties are checked.
659 OBJECT is optional and defaults to the current buffer.
660 If POSITION is at the end of OBJECT, the value is nil.
661 If OBJECT is a buffer, then overlay properties are considered as well as
662 text properties.
663 If OBJECT is a window, then that window's buffer is used, but window-specific
664 overlays are considered only if they are associated with OBJECT. */)
665 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
667 return get_char_property_and_overlay (position, prop, object, 0);
670 DEFUN ("get-char-property-and-overlay", Fget_char_property_and_overlay,
671 Sget_char_property_and_overlay, 2, 3, 0,
672 doc: /* Like `get-char-property', but with extra overlay information.
673 The value is a cons cell. Its car is the return value of `get-char-property'
674 with the same arguments--that is, the value of POSITION's property
675 PROP in OBJECT. Its cdr is the overlay in which the property was
676 found, or nil, if it was found as a text property or not found at all.
678 OBJECT is optional and defaults to the current buffer. OBJECT may be
679 a string, a buffer or a window. For strings, the cdr of the return
680 value is always nil, since strings do not have overlays. If OBJECT is
681 a window, then that window's buffer is used, but window-specific
682 overlays are considered only if they are associated with OBJECT. If
683 POSITION is at the end of OBJECT, both car and cdr are nil. */)
684 (Lisp_Object position, Lisp_Object prop, Lisp_Object object)
686 Lisp_Object overlay;
687 Lisp_Object val
688 = get_char_property_and_overlay (position, prop, object, &overlay);
689 return Fcons (val, overlay);
693 DEFUN ("next-char-property-change", Fnext_char_property_change,
694 Snext_char_property_change, 1, 2, 0,
695 doc: /* Return the position of next text property or overlay change.
696 This scans characters forward in the current buffer from POSITION till
697 it finds a change in some text property, or the beginning or end of an
698 overlay, and returns the position of that.
699 If none is found up to (point-max), the function returns (point-max).
701 If the optional second argument LIMIT is non-nil, don't search
702 past position LIMIT; return LIMIT if nothing is found before LIMIT.
703 LIMIT is a no-op if it is greater than (point-max). */)
704 (Lisp_Object position, Lisp_Object limit)
706 Lisp_Object temp;
708 temp = Fnext_overlay_change (position);
709 if (! NILP (limit))
711 CHECK_NUMBER_COERCE_MARKER (limit);
712 if (XINT (limit) < XINT (temp))
713 temp = limit;
715 return Fnext_property_change (position, Qnil, temp);
718 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
719 Sprevious_char_property_change, 1, 2, 0,
720 doc: /* Return the position of previous text property or overlay change.
721 Scans characters backward in the current buffer from POSITION till it
722 finds a change in some text property, or the beginning or end of an
723 overlay, and returns the position of that.
724 If none is found since (point-min), the function returns (point-min).
726 If the optional second argument LIMIT is non-nil, don't search
727 past position LIMIT; return LIMIT if nothing is found before LIMIT.
728 LIMIT is a no-op if it is less than (point-min). */)
729 (Lisp_Object position, Lisp_Object limit)
731 Lisp_Object temp;
733 temp = Fprevious_overlay_change (position);
734 if (! NILP (limit))
736 CHECK_NUMBER_COERCE_MARKER (limit);
737 if (XINT (limit) > XINT (temp))
738 temp = limit;
740 return Fprevious_property_change (position, Qnil, temp);
744 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
745 Snext_single_char_property_change, 2, 4, 0,
746 doc: /* Return the position of next text property or overlay change for a specific property.
747 Scans characters forward from POSITION till it finds
748 a change in the PROP property, then returns the position of the change.
749 If the optional third argument OBJECT is a buffer (or nil, which means
750 the current buffer), POSITION is a buffer position (integer or marker).
751 If OBJECT is a string, POSITION is a 0-based index into it.
753 In a string, scan runs to the end of the string.
754 In a buffer, it runs to (point-max), and the value cannot exceed that.
756 The property values are compared with `eq'.
757 If the property is constant all the way to the end of OBJECT, return the
758 last valid position in OBJECT.
759 If the optional fourth argument LIMIT is non-nil, don't search
760 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
761 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
763 if (STRINGP (object))
765 position = Fnext_single_property_change (position, prop, object, limit);
766 if (NILP (position))
768 if (NILP (limit))
769 position = make_number (SCHARS (object));
770 else
772 CHECK_NUMBER (limit);
773 position = limit;
777 else
779 Lisp_Object initial_value, value;
780 int count = SPECPDL_INDEX ();
782 if (! NILP (object))
783 CHECK_BUFFER (object);
785 if (BUFFERP (object) && current_buffer != XBUFFER (object))
787 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
788 Fset_buffer (object);
791 CHECK_NUMBER_COERCE_MARKER (position);
793 initial_value = Fget_char_property (position, prop, object);
795 if (NILP (limit))
796 XSETFASTINT (limit, ZV);
797 else
798 CHECK_NUMBER_COERCE_MARKER (limit);
800 if (XFASTINT (position) >= XFASTINT (limit))
802 position = limit;
803 if (XFASTINT (position) > ZV)
804 XSETFASTINT (position, ZV);
806 else
807 while (1)
809 position = Fnext_char_property_change (position, limit);
810 if (XFASTINT (position) >= XFASTINT (limit))
812 position = limit;
813 break;
816 value = Fget_char_property (position, prop, object);
817 if (!EQ (value, initial_value))
818 break;
821 unbind_to (count, Qnil);
824 return position;
827 DEFUN ("previous-single-char-property-change",
828 Fprevious_single_char_property_change,
829 Sprevious_single_char_property_change, 2, 4, 0,
830 doc: /* Return the position of previous text property or overlay change for a specific property.
831 Scans characters backward from POSITION till it finds
832 a change in the PROP property, then returns the position of the change.
833 If the optional third argument OBJECT is a buffer (or nil, which means
834 the current buffer), POSITION is a buffer position (integer or marker).
835 If OBJECT is a string, POSITION is a 0-based index into it.
837 In a string, scan runs to the start of the string.
838 In a buffer, it runs to (point-min), and the value cannot be less than that.
840 The property values are compared with `eq'.
841 If the property is constant all the way to the start of OBJECT, return the
842 first valid position in OBJECT.
843 If the optional fourth argument LIMIT is non-nil, don't search
844 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
845 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
847 if (STRINGP (object))
849 position = Fprevious_single_property_change (position, prop, object, limit);
850 if (NILP (position))
852 if (NILP (limit))
853 position = make_number (0);
854 else
856 CHECK_NUMBER (limit);
857 position = limit;
861 else
863 int count = SPECPDL_INDEX ();
865 if (! NILP (object))
866 CHECK_BUFFER (object);
868 if (BUFFERP (object) && current_buffer != XBUFFER (object))
870 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
871 Fset_buffer (object);
874 CHECK_NUMBER_COERCE_MARKER (position);
876 if (NILP (limit))
877 XSETFASTINT (limit, BEGV);
878 else
879 CHECK_NUMBER_COERCE_MARKER (limit);
881 if (XFASTINT (position) <= XFASTINT (limit))
883 position = limit;
884 if (XFASTINT (position) < BEGV)
885 XSETFASTINT (position, BEGV);
887 else
889 Lisp_Object initial_value
890 = Fget_char_property (make_number (XFASTINT (position) - 1),
891 prop, object);
893 while (1)
895 position = Fprevious_char_property_change (position, limit);
897 if (XFASTINT (position) <= XFASTINT (limit))
899 position = limit;
900 break;
902 else
904 Lisp_Object value
905 = Fget_char_property (make_number (XFASTINT (position) - 1),
906 prop, object);
908 if (!EQ (value, initial_value))
909 break;
914 unbind_to (count, Qnil);
917 return position;
920 DEFUN ("next-property-change", Fnext_property_change,
921 Snext_property_change, 1, 3, 0,
922 doc: /* Return the position of next property change.
923 Scans characters forward from POSITION in OBJECT till it finds
924 a change in some text property, then returns the position of the change.
925 If the optional second argument OBJECT is a buffer (or nil, which means
926 the current buffer), POSITION is a buffer position (integer or marker).
927 If OBJECT is a string, POSITION is a 0-based index into it.
928 Return nil if the property is constant all the way to the end of OBJECT.
929 If the value is non-nil, it is a position greater than POSITION, never equal.
931 If the optional third argument LIMIT is non-nil, don't search
932 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
933 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
935 register INTERVAL i, next;
937 if (NILP (object))
938 XSETBUFFER (object, current_buffer);
940 if (!NILP (limit) && !EQ (limit, Qt))
941 CHECK_NUMBER_COERCE_MARKER (limit);
943 i = validate_interval_range (object, &position, &position, soft);
945 /* If LIMIT is t, return start of next interval--don't
946 bother checking further intervals. */
947 if (EQ (limit, Qt))
949 if (NULL_INTERVAL_P (i))
950 next = i;
951 else
952 next = next_interval (i);
954 if (NULL_INTERVAL_P (next))
955 XSETFASTINT (position, (STRINGP (object)
956 ? SCHARS (object)
957 : BUF_ZV (XBUFFER (object))));
958 else
959 XSETFASTINT (position, next->position);
960 return position;
963 if (NULL_INTERVAL_P (i))
964 return limit;
966 next = next_interval (i);
968 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
969 && (NILP (limit) || next->position < XFASTINT (limit)))
970 next = next_interval (next);
972 if (NULL_INTERVAL_P (next)
973 || (next->position
974 >= (INTEGERP (limit)
975 ? XFASTINT (limit)
976 : (STRINGP (object)
977 ? SCHARS (object)
978 : BUF_ZV (XBUFFER (object))))))
979 return limit;
980 else
981 return make_number (next->position);
984 /* Return 1 if there's a change in some property between BEG and END. */
987 property_change_between_p (int beg, int end)
989 register INTERVAL i, next;
990 Lisp_Object object, pos;
992 XSETBUFFER (object, current_buffer);
993 XSETFASTINT (pos, beg);
995 i = validate_interval_range (object, &pos, &pos, soft);
996 if (NULL_INTERVAL_P (i))
997 return 0;
999 next = next_interval (i);
1000 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
1002 next = next_interval (next);
1003 if (NULL_INTERVAL_P (next))
1004 return 0;
1005 if (next->position >= end)
1006 return 0;
1009 if (NULL_INTERVAL_P (next))
1010 return 0;
1012 return 1;
1015 DEFUN ("next-single-property-change", Fnext_single_property_change,
1016 Snext_single_property_change, 2, 4, 0,
1017 doc: /* Return the position of next property change for a specific property.
1018 Scans characters forward from POSITION till it finds
1019 a change in the PROP property, then returns the position of the change.
1020 If the optional third argument OBJECT is a buffer (or nil, which means
1021 the current buffer), POSITION is a buffer position (integer or marker).
1022 If OBJECT is a string, POSITION is a 0-based index into it.
1023 The property values are compared with `eq'.
1024 Return nil if the property is constant all the way to the end of OBJECT.
1025 If the value is non-nil, it is a position greater than POSITION, never equal.
1027 If the optional fourth argument LIMIT is non-nil, don't search
1028 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1029 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1031 register INTERVAL i, next;
1032 register Lisp_Object here_val;
1034 if (NILP (object))
1035 XSETBUFFER (object, current_buffer);
1037 if (!NILP (limit))
1038 CHECK_NUMBER_COERCE_MARKER (limit);
1040 i = validate_interval_range (object, &position, &position, soft);
1041 if (NULL_INTERVAL_P (i))
1042 return limit;
1044 here_val = textget (i->plist, prop);
1045 next = next_interval (i);
1046 while (! NULL_INTERVAL_P (next)
1047 && EQ (here_val, textget (next->plist, prop))
1048 && (NILP (limit) || next->position < XFASTINT (limit)))
1049 next = next_interval (next);
1051 if (NULL_INTERVAL_P (next)
1052 || (next->position
1053 >= (INTEGERP (limit)
1054 ? XFASTINT (limit)
1055 : (STRINGP (object)
1056 ? SCHARS (object)
1057 : BUF_ZV (XBUFFER (object))))))
1058 return limit;
1059 else
1060 return make_number (next->position);
1063 DEFUN ("previous-property-change", Fprevious_property_change,
1064 Sprevious_property_change, 1, 3, 0,
1065 doc: /* Return the position of previous property change.
1066 Scans characters backwards from POSITION in OBJECT till it finds
1067 a change in some text property, then returns the position of the change.
1068 If the optional second argument OBJECT is a buffer (or nil, which means
1069 the current buffer), POSITION is a buffer position (integer or marker).
1070 If OBJECT is a string, POSITION is a 0-based index into it.
1071 Return nil if the property is constant all the way to the start of OBJECT.
1072 If the value is non-nil, it is a position less than POSITION, never equal.
1074 If the optional third argument LIMIT is non-nil, don't search
1075 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1076 (Lisp_Object position, Lisp_Object object, Lisp_Object limit)
1078 register INTERVAL i, previous;
1080 if (NILP (object))
1081 XSETBUFFER (object, current_buffer);
1083 if (!NILP (limit))
1084 CHECK_NUMBER_COERCE_MARKER (limit);
1086 i = validate_interval_range (object, &position, &position, soft);
1087 if (NULL_INTERVAL_P (i))
1088 return limit;
1090 /* Start with the interval containing the char before point. */
1091 if (i->position == XFASTINT (position))
1092 i = previous_interval (i);
1094 previous = previous_interval (i);
1095 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1096 && (NILP (limit)
1097 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1098 previous = previous_interval (previous);
1100 if (NULL_INTERVAL_P (previous)
1101 || (previous->position + LENGTH (previous)
1102 <= (INTEGERP (limit)
1103 ? XFASTINT (limit)
1104 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1105 return limit;
1106 else
1107 return make_number (previous->position + LENGTH (previous));
1110 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1111 Sprevious_single_property_change, 2, 4, 0,
1112 doc: /* Return the position of previous property change for a specific property.
1113 Scans characters backward from POSITION till it finds
1114 a change in the PROP property, then returns the position of the change.
1115 If the optional third argument OBJECT is a buffer (or nil, which means
1116 the current buffer), POSITION is a buffer position (integer or marker).
1117 If OBJECT is a string, POSITION is a 0-based index into it.
1118 The property values are compared with `eq'.
1119 Return nil if the property is constant all the way to the start of OBJECT.
1120 If the value is non-nil, it is a position less than POSITION, never equal.
1122 If the optional fourth argument LIMIT is non-nil, don't search
1123 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1124 (Lisp_Object position, Lisp_Object prop, Lisp_Object object, Lisp_Object limit)
1126 register INTERVAL i, previous;
1127 register Lisp_Object here_val;
1129 if (NILP (object))
1130 XSETBUFFER (object, current_buffer);
1132 if (!NILP (limit))
1133 CHECK_NUMBER_COERCE_MARKER (limit);
1135 i = validate_interval_range (object, &position, &position, soft);
1137 /* Start with the interval containing the char before point. */
1138 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1139 i = previous_interval (i);
1141 if (NULL_INTERVAL_P (i))
1142 return limit;
1144 here_val = textget (i->plist, prop);
1145 previous = previous_interval (i);
1146 while (!NULL_INTERVAL_P (previous)
1147 && EQ (here_val, textget (previous->plist, prop))
1148 && (NILP (limit)
1149 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1150 previous = previous_interval (previous);
1152 if (NULL_INTERVAL_P (previous)
1153 || (previous->position + LENGTH (previous)
1154 <= (INTEGERP (limit)
1155 ? XFASTINT (limit)
1156 : (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))))))
1157 return limit;
1158 else
1159 return make_number (previous->position + LENGTH (previous));
1162 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1164 DEFUN ("add-text-properties", Fadd_text_properties,
1165 Sadd_text_properties, 3, 4, 0,
1166 doc: /* Add properties to the text from START to END.
1167 The third argument PROPERTIES is a property list
1168 specifying the property values to add. If the optional fourth argument
1169 OBJECT is a buffer (or nil, which means the current buffer),
1170 START and END are buffer positions (integers or markers).
1171 If OBJECT is a string, START and END are 0-based indices into it.
1172 Return t if any property value actually changed, nil otherwise. */)
1173 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1175 register INTERVAL i, unchanged;
1176 register int s, len, modified = 0;
1177 struct gcpro gcpro1;
1179 properties = validate_plist (properties);
1180 if (NILP (properties))
1181 return Qnil;
1183 if (NILP (object))
1184 XSETBUFFER (object, current_buffer);
1186 i = validate_interval_range (object, &start, &end, hard);
1187 if (NULL_INTERVAL_P (i))
1188 return Qnil;
1190 s = XINT (start);
1191 len = XINT (end) - s;
1193 /* No need to protect OBJECT, because we GC only if it's a buffer,
1194 and live buffers are always protected. */
1195 GCPRO1 (properties);
1197 /* If we're not starting on an interval boundary, we have to
1198 split this interval. */
1199 if (i->position != s)
1201 /* If this interval already has the properties, we can
1202 skip it. */
1203 if (interval_has_all_properties (properties, i))
1205 int got = (LENGTH (i) - (s - i->position));
1206 if (got >= len)
1207 RETURN_UNGCPRO (Qnil);
1208 len -= got;
1209 i = next_interval (i);
1211 else
1213 unchanged = i;
1214 i = split_interval_right (unchanged, s - unchanged->position);
1215 copy_properties (unchanged, i);
1219 if (BUFFERP (object))
1220 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1222 /* We are at the beginning of interval I, with LEN chars to scan. */
1223 for (;;)
1225 if (i == 0)
1226 abort ();
1228 if (LENGTH (i) >= len)
1230 /* We can UNGCPRO safely here, because there will be just
1231 one more chance to gc, in the next call to add_properties,
1232 and after that we will not need PROPERTIES or OBJECT again. */
1233 UNGCPRO;
1235 if (interval_has_all_properties (properties, i))
1237 if (BUFFERP (object))
1238 signal_after_change (XINT (start), XINT (end) - XINT (start),
1239 XINT (end) - XINT (start));
1241 return modified ? Qt : Qnil;
1244 if (LENGTH (i) == len)
1246 add_properties (properties, i, object);
1247 if (BUFFERP (object))
1248 signal_after_change (XINT (start), XINT (end) - XINT (start),
1249 XINT (end) - XINT (start));
1250 return Qt;
1253 /* i doesn't have the properties, and goes past the change limit */
1254 unchanged = i;
1255 i = split_interval_left (unchanged, len);
1256 copy_properties (unchanged, i);
1257 add_properties (properties, i, object);
1258 if (BUFFERP (object))
1259 signal_after_change (XINT (start), XINT (end) - XINT (start),
1260 XINT (end) - XINT (start));
1261 return Qt;
1264 len -= LENGTH (i);
1265 modified += add_properties (properties, i, object);
1266 i = next_interval (i);
1270 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1272 DEFUN ("put-text-property", Fput_text_property,
1273 Sput_text_property, 4, 5, 0,
1274 doc: /* Set one property of the text from START to END.
1275 The third and fourth arguments PROPERTY and VALUE
1276 specify the property to add.
1277 If the optional fifth argument OBJECT is a buffer (or nil, which means
1278 the current buffer), START and END are buffer positions (integers or
1279 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1280 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1282 Fadd_text_properties (start, end,
1283 Fcons (property, Fcons (value, Qnil)),
1284 object);
1285 return Qnil;
1288 DEFUN ("set-text-properties", Fset_text_properties,
1289 Sset_text_properties, 3, 4, 0,
1290 doc: /* Completely replace properties of text from START to END.
1291 The third argument PROPERTIES is the new property list.
1292 If the optional fourth argument OBJECT is a buffer (or nil, which means
1293 the current buffer), START and END are buffer positions (integers or
1294 markers). If OBJECT is a string, START and END are 0-based indices into it.
1295 If PROPERTIES is nil, the effect is to remove all properties from
1296 the designated part of OBJECT. */)
1297 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1299 return set_text_properties (start, end, properties, object, Qt);
1303 /* Replace properties of text from START to END with new list of
1304 properties PROPERTIES. OBJECT is the buffer or string containing
1305 the text. OBJECT nil means use the current buffer.
1306 COHERENT_CHANGE_P nil means this is being called as an internal
1307 subroutine, rather than as a change primitive with checking of
1308 read-only, invoking change hooks, etc.. Value is nil if the
1309 function _detected_ that it did not replace any properties, non-nil
1310 otherwise. */
1312 Lisp_Object
1313 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1315 register INTERVAL i;
1316 Lisp_Object ostart, oend;
1318 ostart = start;
1319 oend = end;
1321 properties = validate_plist (properties);
1323 if (NILP (object))
1324 XSETBUFFER (object, current_buffer);
1326 /* If we want no properties for a whole string,
1327 get rid of its intervals. */
1328 if (NILP (properties) && STRINGP (object)
1329 && XFASTINT (start) == 0
1330 && XFASTINT (end) == SCHARS (object))
1332 if (! STRING_INTERVALS (object))
1333 return Qnil;
1335 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1336 return Qt;
1339 i = validate_interval_range (object, &start, &end, soft);
1341 if (NULL_INTERVAL_P (i))
1343 /* If buffer has no properties, and we want none, return now. */
1344 if (NILP (properties))
1345 return Qnil;
1347 /* Restore the original START and END values
1348 because validate_interval_range increments them for strings. */
1349 start = ostart;
1350 end = oend;
1352 i = validate_interval_range (object, &start, &end, hard);
1353 /* This can return if start == end. */
1354 if (NULL_INTERVAL_P (i))
1355 return Qnil;
1358 if (BUFFERP (object) && !NILP (coherent_change_p))
1359 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1361 set_text_properties_1 (start, end, properties, object, i);
1363 if (BUFFERP (object) && !NILP (coherent_change_p))
1364 signal_after_change (XINT (start), XINT (end) - XINT (start),
1365 XINT (end) - XINT (start));
1366 return Qt;
1369 /* Replace properties of text from START to END with new list of
1370 properties PROPERTIES. BUFFER is the buffer containing
1371 the text. This does not obey any hooks.
1372 You can provide the interval that START is located in as I,
1373 or pass NULL for I and this function will find it.
1374 START and END can be in any order. */
1376 void
1377 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object buffer, INTERVAL i)
1379 register INTERVAL prev_changed = NULL_INTERVAL;
1380 register int s, len;
1381 INTERVAL unchanged;
1383 s = XINT (start);
1384 len = XINT (end) - s;
1385 if (len == 0)
1386 return;
1387 if (len < 0)
1389 s = s + len;
1390 len = - len;
1393 if (i == 0)
1394 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1396 if (i->position != s)
1398 unchanged = i;
1399 i = split_interval_right (unchanged, s - unchanged->position);
1401 if (LENGTH (i) > len)
1403 copy_properties (unchanged, i);
1404 i = split_interval_left (i, len);
1405 set_properties (properties, i, buffer);
1406 return;
1409 set_properties (properties, i, buffer);
1411 if (LENGTH (i) == len)
1412 return;
1414 prev_changed = i;
1415 len -= LENGTH (i);
1416 i = next_interval (i);
1419 /* We are starting at the beginning of an interval, I */
1420 while (len > 0)
1422 if (i == 0)
1423 abort ();
1425 if (LENGTH (i) >= len)
1427 if (LENGTH (i) > len)
1428 i = split_interval_left (i, len);
1430 /* We have to call set_properties even if we are going to
1431 merge the intervals, so as to make the undo records
1432 and cause redisplay to happen. */
1433 set_properties (properties, i, buffer);
1434 if (!NULL_INTERVAL_P (prev_changed))
1435 merge_interval_left (i);
1436 return;
1439 len -= LENGTH (i);
1441 /* We have to call set_properties even if we are going to
1442 merge the intervals, so as to make the undo records
1443 and cause redisplay to happen. */
1444 set_properties (properties, i, buffer);
1445 if (NULL_INTERVAL_P (prev_changed))
1446 prev_changed = i;
1447 else
1448 prev_changed = i = merge_interval_left (i);
1450 i = next_interval (i);
1454 DEFUN ("remove-text-properties", Fremove_text_properties,
1455 Sremove_text_properties, 3, 4, 0,
1456 doc: /* Remove some properties from text from START to END.
1457 The third argument PROPERTIES is a property list
1458 whose property names specify the properties to remove.
1459 \(The values stored in PROPERTIES are ignored.)
1460 If the optional fourth argument OBJECT is a buffer (or nil, which means
1461 the current buffer), START and END are buffer positions (integers or
1462 markers). If OBJECT is a string, START and END are 0-based indices into it.
1463 Return t if any property was actually removed, nil otherwise.
1465 Use `set-text-properties' if you want to remove all text properties. */)
1466 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1468 register INTERVAL i, unchanged;
1469 register int s, len, modified = 0;
1471 if (NILP (object))
1472 XSETBUFFER (object, current_buffer);
1474 i = validate_interval_range (object, &start, &end, soft);
1475 if (NULL_INTERVAL_P (i))
1476 return Qnil;
1478 s = XINT (start);
1479 len = XINT (end) - s;
1481 if (i->position != s)
1483 /* No properties on this first interval -- return if
1484 it covers the entire region. */
1485 if (! interval_has_some_properties (properties, i))
1487 int got = (LENGTH (i) - (s - i->position));
1488 if (got >= len)
1489 return Qnil;
1490 len -= got;
1491 i = next_interval (i);
1493 /* Split away the beginning of this interval; what we don't
1494 want to modify. */
1495 else
1497 unchanged = i;
1498 i = split_interval_right (unchanged, s - unchanged->position);
1499 copy_properties (unchanged, i);
1503 if (BUFFERP (object))
1504 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1506 /* We are at the beginning of an interval, with len to scan */
1507 for (;;)
1509 if (i == 0)
1510 abort ();
1512 if (LENGTH (i) >= len)
1514 if (! interval_has_some_properties (properties, i))
1515 return modified ? Qt : Qnil;
1517 if (LENGTH (i) == len)
1519 remove_properties (properties, Qnil, i, object);
1520 if (BUFFERP (object))
1521 signal_after_change (XINT (start), XINT (end) - XINT (start),
1522 XINT (end) - XINT (start));
1523 return Qt;
1526 /* i has the properties, and goes past the change limit */
1527 unchanged = i;
1528 i = split_interval_left (i, len);
1529 copy_properties (unchanged, i);
1530 remove_properties (properties, Qnil, i, object);
1531 if (BUFFERP (object))
1532 signal_after_change (XINT (start), XINT (end) - XINT (start),
1533 XINT (end) - XINT (start));
1534 return Qt;
1537 len -= LENGTH (i);
1538 modified += remove_properties (properties, Qnil, i, object);
1539 i = next_interval (i);
1543 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1544 Sremove_list_of_text_properties, 3, 4, 0,
1545 doc: /* Remove some properties from text from START to END.
1546 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1547 If the optional fourth argument OBJECT is a buffer (or nil, which means
1548 the current buffer), START and END are buffer positions (integers or
1549 markers). If OBJECT is a string, START and END are 0-based indices into it.
1550 Return t if any property was actually removed, nil otherwise. */)
1551 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1553 register INTERVAL i, unchanged;
1554 register int s, len, modified = 0;
1555 Lisp_Object properties;
1556 properties = list_of_properties;
1558 if (NILP (object))
1559 XSETBUFFER (object, current_buffer);
1561 i = validate_interval_range (object, &start, &end, soft);
1562 if (NULL_INTERVAL_P (i))
1563 return Qnil;
1565 s = XINT (start);
1566 len = XINT (end) - s;
1568 if (i->position != s)
1570 /* No properties on this first interval -- return if
1571 it covers the entire region. */
1572 if (! interval_has_some_properties_list (properties, i))
1574 int got = (LENGTH (i) - (s - i->position));
1575 if (got >= len)
1576 return Qnil;
1577 len -= got;
1578 i = next_interval (i);
1580 /* Split away the beginning of this interval; what we don't
1581 want to modify. */
1582 else
1584 unchanged = i;
1585 i = split_interval_right (unchanged, s - unchanged->position);
1586 copy_properties (unchanged, i);
1590 /* We are at the beginning of an interval, with len to scan.
1591 The flag `modified' records if changes have been made.
1592 When object is a buffer, we must call modify_region before changes are
1593 made and signal_after_change when we are done.
1594 We call modify_region before calling remove_properties if modified == 0,
1595 and we call signal_after_change before returning if modified != 0. */
1596 for (;;)
1598 if (i == 0)
1599 abort ();
1601 if (LENGTH (i) >= len)
1603 if (! interval_has_some_properties_list (properties, i))
1604 if (modified)
1606 if (BUFFERP (object))
1607 signal_after_change (XINT (start), XINT (end) - XINT (start),
1608 XINT (end) - XINT (start));
1609 return Qt;
1611 else
1612 return Qnil;
1614 if (LENGTH (i) == len)
1616 if (!modified && BUFFERP (object))
1617 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1618 remove_properties (Qnil, properties, i, object);
1619 if (BUFFERP (object))
1620 signal_after_change (XINT (start), XINT (end) - XINT (start),
1621 XINT (end) - XINT (start));
1622 return Qt;
1625 /* i has the properties, and goes past the change limit */
1626 unchanged = i;
1627 i = split_interval_left (i, len);
1628 copy_properties (unchanged, i);
1629 if (!modified && BUFFERP (object))
1630 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1631 remove_properties (Qnil, properties, i, object);
1632 if (BUFFERP (object))
1633 signal_after_change (XINT (start), XINT (end) - XINT (start),
1634 XINT (end) - XINT (start));
1635 return Qt;
1638 if (interval_has_some_properties_list (properties, i))
1640 if (!modified && BUFFERP (object))
1641 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1642 remove_properties (Qnil, properties, i, object);
1643 modified = 1;
1645 len -= LENGTH (i);
1646 i = next_interval (i);
1650 DEFUN ("text-property-any", Ftext_property_any,
1651 Stext_property_any, 4, 5, 0,
1652 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1653 If so, return the position of the first character whose property PROPERTY
1654 is `eq' to VALUE. Otherwise return nil.
1655 If the optional fifth argument OBJECT is a buffer (or nil, which means
1656 the current buffer), START and END are buffer positions (integers or
1657 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1658 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1660 register INTERVAL i;
1661 register int e, pos;
1663 if (NILP (object))
1664 XSETBUFFER (object, current_buffer);
1665 i = validate_interval_range (object, &start, &end, soft);
1666 if (NULL_INTERVAL_P (i))
1667 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1668 e = XINT (end);
1670 while (! NULL_INTERVAL_P (i))
1672 if (i->position >= e)
1673 break;
1674 if (EQ (textget (i->plist, property), value))
1676 pos = i->position;
1677 if (pos < XINT (start))
1678 pos = XINT (start);
1679 return make_number (pos);
1681 i = next_interval (i);
1683 return Qnil;
1686 DEFUN ("text-property-not-all", Ftext_property_not_all,
1687 Stext_property_not_all, 4, 5, 0,
1688 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1689 If so, return the position of the first character whose property PROPERTY
1690 is not `eq' to VALUE. Otherwise, return nil.
1691 If the optional fifth argument OBJECT is a buffer (or nil, which means
1692 the current buffer), START and END are buffer positions (integers or
1693 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1694 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1696 register INTERVAL i;
1697 register int s, e;
1699 if (NILP (object))
1700 XSETBUFFER (object, current_buffer);
1701 i = validate_interval_range (object, &start, &end, soft);
1702 if (NULL_INTERVAL_P (i))
1703 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1704 s = XINT (start);
1705 e = XINT (end);
1707 while (! NULL_INTERVAL_P (i))
1709 if (i->position >= e)
1710 break;
1711 if (! EQ (textget (i->plist, property), value))
1713 if (i->position > s)
1714 s = i->position;
1715 return make_number (s);
1717 i = next_interval (i);
1719 return Qnil;
1723 /* Return the direction from which the text-property PROP would be
1724 inherited by any new text inserted at POS: 1 if it would be
1725 inherited from the char after POS, -1 if it would be inherited from
1726 the char before POS, and 0 if from neither.
1727 BUFFER can be either a buffer or nil (meaning current buffer). */
1730 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1732 Lisp_Object prev_pos, front_sticky;
1733 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1735 if (NILP (buffer))
1736 XSETBUFFER (buffer, current_buffer);
1738 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1739 /* Consider previous character. */
1741 Lisp_Object rear_non_sticky;
1743 prev_pos = make_number (XINT (pos) - 1);
1744 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1746 if (!NILP (CONSP (rear_non_sticky)
1747 ? Fmemq (prop, rear_non_sticky)
1748 : rear_non_sticky))
1749 /* PROP is rear-non-sticky. */
1750 is_rear_sticky = 0;
1752 else
1753 return 0;
1755 /* Consider following character. */
1756 /* This signals an arg-out-of-range error if pos is outside the
1757 buffer's accessible range. */
1758 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1760 if (EQ (front_sticky, Qt)
1761 || (CONSP (front_sticky)
1762 && !NILP (Fmemq (prop, front_sticky))))
1763 /* PROP is inherited from after. */
1764 is_front_sticky = 1;
1766 /* Simple cases, where the properties are consistent. */
1767 if (is_rear_sticky && !is_front_sticky)
1768 return -1;
1769 else if (!is_rear_sticky && is_front_sticky)
1770 return 1;
1771 else if (!is_rear_sticky && !is_front_sticky)
1772 return 0;
1774 /* The stickiness properties are inconsistent, so we have to
1775 disambiguate. Basically, rear-sticky wins, _except_ if the
1776 property that would be inherited has a value of nil, in which case
1777 front-sticky wins. */
1778 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1779 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1780 return 1;
1781 else
1782 return -1;
1786 /* I don't think this is the right interface to export; how often do you
1787 want to do something like this, other than when you're copying objects
1788 around?
1790 I think it would be better to have a pair of functions, one which
1791 returns the text properties of a region as a list of ranges and
1792 plists, and another which applies such a list to another object. */
1794 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1795 SRC and DEST may each refer to strings or buffers.
1796 Optional sixth argument PROP causes only that property to be copied.
1797 Properties are copied to DEST as if by `add-text-properties'.
1798 Return t if any property value actually changed, nil otherwise. */
1800 /* Note this can GC when DEST is a buffer. */
1802 Lisp_Object
1803 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1805 INTERVAL i;
1806 Lisp_Object res;
1807 Lisp_Object stuff;
1808 Lisp_Object plist;
1809 int s, e, e2, p, len, modified = 0;
1810 struct gcpro gcpro1, gcpro2;
1812 i = validate_interval_range (src, &start, &end, soft);
1813 if (NULL_INTERVAL_P (i))
1814 return Qnil;
1816 CHECK_NUMBER_COERCE_MARKER (pos);
1818 Lisp_Object dest_start, dest_end;
1820 dest_start = pos;
1821 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1822 /* Apply this to a copy of pos; it will try to increment its arguments,
1823 which we don't want. */
1824 validate_interval_range (dest, &dest_start, &dest_end, soft);
1827 s = XINT (start);
1828 e = XINT (end);
1829 p = XINT (pos);
1831 stuff = Qnil;
1833 while (s < e)
1835 e2 = i->position + LENGTH (i);
1836 if (e2 > e)
1837 e2 = e;
1838 len = e2 - s;
1840 plist = i->plist;
1841 if (! NILP (prop))
1842 while (! NILP (plist))
1844 if (EQ (Fcar (plist), prop))
1846 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1847 break;
1849 plist = Fcdr (Fcdr (plist));
1851 if (! NILP (plist))
1853 /* Must defer modifications to the interval tree in case src
1854 and dest refer to the same string or buffer. */
1855 stuff = Fcons (Fcons (make_number (p),
1856 Fcons (make_number (p + len),
1857 Fcons (plist, Qnil))),
1858 stuff);
1861 i = next_interval (i);
1862 if (NULL_INTERVAL_P (i))
1863 break;
1865 p += len;
1866 s = i->position;
1869 GCPRO2 (stuff, dest);
1871 while (! NILP (stuff))
1873 res = Fcar (stuff);
1874 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1875 Fcar (Fcdr (Fcdr (res))), dest);
1876 if (! NILP (res))
1877 modified++;
1878 stuff = Fcdr (stuff);
1881 UNGCPRO;
1883 return modified ? Qt : Qnil;
1887 /* Return a list representing the text properties of OBJECT between
1888 START and END. if PROP is non-nil, report only on that property.
1889 Each result list element has the form (S E PLIST), where S and E
1890 are positions in OBJECT and PLIST is a property list containing the
1891 text properties of OBJECT between S and E. Value is nil if OBJECT
1892 doesn't contain text properties between START and END. */
1894 Lisp_Object
1895 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1897 struct interval *i;
1898 Lisp_Object result;
1900 result = Qnil;
1902 i = validate_interval_range (object, &start, &end, soft);
1903 if (!NULL_INTERVAL_P (i))
1905 int s = XINT (start);
1906 int e = XINT (end);
1908 while (s < e)
1910 int interval_end, len;
1911 Lisp_Object plist;
1913 interval_end = i->position + LENGTH (i);
1914 if (interval_end > e)
1915 interval_end = e;
1916 len = interval_end - s;
1918 plist = i->plist;
1920 if (!NILP (prop))
1921 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1922 if (EQ (XCAR (plist), prop))
1924 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1925 break;
1928 if (!NILP (plist))
1929 result = Fcons (Fcons (make_number (s),
1930 Fcons (make_number (s + len),
1931 Fcons (plist, Qnil))),
1932 result);
1934 i = next_interval (i);
1935 if (NULL_INTERVAL_P (i))
1936 break;
1937 s = i->position;
1941 return result;
1945 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1946 (START END PLIST), where START and END are positions and PLIST is a
1947 property list containing the text properties to add. Adjust START
1948 and END positions by DELTA before adding properties. Value is
1949 non-zero if OBJECT was modified. */
1952 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1954 struct gcpro gcpro1, gcpro2;
1955 int modified_p = 0;
1957 GCPRO2 (list, object);
1959 for (; CONSP (list); list = XCDR (list))
1961 Lisp_Object item, start, end, plist, tem;
1963 item = XCAR (list);
1964 start = make_number (XINT (XCAR (item)) + XINT (delta));
1965 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1966 plist = XCAR (XCDR (XCDR (item)));
1968 tem = Fadd_text_properties (start, end, plist, object);
1969 if (!NILP (tem))
1970 modified_p = 1;
1973 UNGCPRO;
1974 return modified_p;
1979 /* Modify end-points of ranges in LIST destructively, and return the
1980 new list. LIST is a list as returned from text_property_list.
1981 Discard properties that begin at or after NEW_END, and limit
1982 end-points to NEW_END. */
1984 Lisp_Object
1985 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1987 Lisp_Object prev = Qnil, head = list;
1988 int max = XINT (new_end);
1990 for (; CONSP (list); prev = list, list = XCDR (list))
1992 Lisp_Object item, beg, end;
1994 item = XCAR (list);
1995 beg = XCAR (item);
1996 end = XCAR (XCDR (item));
1998 if (XINT (beg) >= max)
2000 /* The start-point is past the end of the new string.
2001 Discard this property. */
2002 if (EQ (head, list))
2003 head = XCDR (list);
2004 else
2005 XSETCDR (prev, XCDR (list));
2007 else if (XINT (end) > max)
2008 /* The end-point is past the end of the new string. */
2009 XSETCAR (XCDR (item), new_end);
2012 return head;
2017 /* Call the modification hook functions in LIST, each with START and END. */
2019 static void
2020 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2022 struct gcpro gcpro1;
2023 GCPRO1 (list);
2024 while (!NILP (list))
2026 call2 (Fcar (list), start, end);
2027 list = Fcdr (list);
2029 UNGCPRO;
2032 /* Check for read-only intervals between character positions START ... END,
2033 in BUF, and signal an error if we find one.
2035 Then check for any modification hooks in the range.
2036 Create a list of all these hooks in lexicographic order,
2037 eliminating consecutive extra copies of the same hook. Then call
2038 those hooks in order, with START and END - 1 as arguments. */
2040 void
2041 verify_interval_modification (struct buffer *buf, int start, int end)
2043 register INTERVAL intervals = BUF_INTERVALS (buf);
2044 register INTERVAL i;
2045 Lisp_Object hooks;
2046 register Lisp_Object prev_mod_hooks;
2047 Lisp_Object mod_hooks;
2048 struct gcpro gcpro1;
2050 hooks = Qnil;
2051 prev_mod_hooks = Qnil;
2052 mod_hooks = Qnil;
2054 interval_insert_behind_hooks = Qnil;
2055 interval_insert_in_front_hooks = Qnil;
2057 if (NULL_INTERVAL_P (intervals))
2058 return;
2060 if (start > end)
2062 int temp = start;
2063 start = end;
2064 end = temp;
2067 /* For an insert operation, check the two chars around the position. */
2068 if (start == end)
2070 INTERVAL prev = NULL;
2071 Lisp_Object before, after;
2073 /* Set I to the interval containing the char after START,
2074 and PREV to the interval containing the char before START.
2075 Either one may be null. They may be equal. */
2076 i = find_interval (intervals, start);
2078 if (start == BUF_BEGV (buf))
2079 prev = 0;
2080 else if (i->position == start)
2081 prev = previous_interval (i);
2082 else if (i->position < start)
2083 prev = i;
2084 if (start == BUF_ZV (buf))
2085 i = 0;
2087 /* If Vinhibit_read_only is set and is not a list, we can
2088 skip the read_only checks. */
2089 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2091 /* If I and PREV differ we need to check for the read-only
2092 property together with its stickiness. If either I or
2093 PREV are 0, this check is all we need.
2094 We have to take special care, since read-only may be
2095 indirectly defined via the category property. */
2096 if (i != prev)
2098 if (! NULL_INTERVAL_P (i))
2100 after = textget (i->plist, Qread_only);
2102 /* If interval I is read-only and read-only is
2103 front-sticky, inhibit insertion.
2104 Check for read-only as well as category. */
2105 if (! NILP (after)
2106 && NILP (Fmemq (after, Vinhibit_read_only)))
2108 Lisp_Object tem;
2110 tem = textget (i->plist, Qfront_sticky);
2111 if (TMEM (Qread_only, tem)
2112 || (NILP (Fplist_get (i->plist, Qread_only))
2113 && TMEM (Qcategory, tem)))
2114 text_read_only (after);
2118 if (! NULL_INTERVAL_P (prev))
2120 before = textget (prev->plist, Qread_only);
2122 /* If interval PREV is read-only and read-only isn't
2123 rear-nonsticky, inhibit insertion.
2124 Check for read-only as well as category. */
2125 if (! NILP (before)
2126 && NILP (Fmemq (before, Vinhibit_read_only)))
2128 Lisp_Object tem;
2130 tem = textget (prev->plist, Qrear_nonsticky);
2131 if (! TMEM (Qread_only, tem)
2132 && (! NILP (Fplist_get (prev->plist,Qread_only))
2133 || ! TMEM (Qcategory, tem)))
2134 text_read_only (before);
2138 else if (! NULL_INTERVAL_P (i))
2140 after = textget (i->plist, Qread_only);
2142 /* If interval I is read-only and read-only is
2143 front-sticky, inhibit insertion.
2144 Check for read-only as well as category. */
2145 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2147 Lisp_Object tem;
2149 tem = textget (i->plist, Qfront_sticky);
2150 if (TMEM (Qread_only, tem)
2151 || (NILP (Fplist_get (i->plist, Qread_only))
2152 && TMEM (Qcategory, tem)))
2153 text_read_only (after);
2155 tem = textget (prev->plist, Qrear_nonsticky);
2156 if (! TMEM (Qread_only, tem)
2157 && (! NILP (Fplist_get (prev->plist, Qread_only))
2158 || ! TMEM (Qcategory, tem)))
2159 text_read_only (after);
2164 /* Run both insert hooks (just once if they're the same). */
2165 if (!NULL_INTERVAL_P (prev))
2166 interval_insert_behind_hooks
2167 = textget (prev->plist, Qinsert_behind_hooks);
2168 if (!NULL_INTERVAL_P (i))
2169 interval_insert_in_front_hooks
2170 = textget (i->plist, Qinsert_in_front_hooks);
2172 else
2174 /* Loop over intervals on or next to START...END,
2175 collecting their hooks. */
2177 i = find_interval (intervals, start);
2180 if (! INTERVAL_WRITABLE_P (i))
2181 text_read_only (textget (i->plist, Qread_only));
2183 if (!inhibit_modification_hooks)
2185 mod_hooks = textget (i->plist, Qmodification_hooks);
2186 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2188 hooks = Fcons (mod_hooks, hooks);
2189 prev_mod_hooks = mod_hooks;
2193 i = next_interval (i);
2195 /* Keep going thru the interval containing the char before END. */
2196 while (! NULL_INTERVAL_P (i) && i->position < end);
2198 if (!inhibit_modification_hooks)
2200 GCPRO1 (hooks);
2201 hooks = Fnreverse (hooks);
2202 while (! EQ (hooks, Qnil))
2204 call_mod_hooks (Fcar (hooks), make_number (start),
2205 make_number (end));
2206 hooks = Fcdr (hooks);
2208 UNGCPRO;
2213 /* Run the interval hooks for an insertion on character range START ... END.
2214 verify_interval_modification chose which hooks to run;
2215 this function is called after the insertion happens
2216 so it can indicate the range of inserted text. */
2218 void
2219 report_interval_modification (Lisp_Object start, Lisp_Object end)
2221 if (! NILP (interval_insert_behind_hooks))
2222 call_mod_hooks (interval_insert_behind_hooks, start, end);
2223 if (! NILP (interval_insert_in_front_hooks)
2224 && ! EQ (interval_insert_in_front_hooks,
2225 interval_insert_behind_hooks))
2226 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2229 void
2230 syms_of_textprop (void)
2232 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2233 doc: /* Property-list used as default values.
2234 The value of a property in this list is seen as the value for every
2235 character that does not have its own value for that property. */);
2236 Vdefault_text_properties = Qnil;
2238 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2239 doc: /* Alist of alternative properties for properties without a value.
2240 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2241 If a piece of text has no direct value for a particular property, then
2242 this alist is consulted. If that property appears in the alist, then
2243 the first non-nil value from the associated alternative properties is
2244 returned. */);
2245 Vchar_property_alias_alist = Qnil;
2247 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2248 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2249 This also inhibits the use of the `intangible' text property. */);
2250 Vinhibit_point_motion_hooks = Qnil;
2252 DEFVAR_LISP ("text-property-default-nonsticky",
2253 &Vtext_property_default_nonsticky,
2254 doc: /* Alist of properties vs the corresponding non-stickinesses.
2255 Each element has the form (PROPERTY . NONSTICKINESS).
2257 If a character in a buffer has PROPERTY, new text inserted adjacent to
2258 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2259 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2260 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2261 /* Text property `syntax-table' should be nonsticky by default. */
2262 Vtext_property_default_nonsticky
2263 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt), Qnil);
2265 staticpro (&interval_insert_behind_hooks);
2266 staticpro (&interval_insert_in_front_hooks);
2267 interval_insert_behind_hooks = Qnil;
2268 interval_insert_in_front_hooks = Qnil;
2271 /* Common attributes one might give text */
2273 staticpro (&Qforeground);
2274 Qforeground = intern_c_string ("foreground");
2275 staticpro (&Qbackground);
2276 Qbackground = intern_c_string ("background");
2277 staticpro (&Qfont);
2278 Qfont = intern_c_string ("font");
2279 staticpro (&Qstipple);
2280 Qstipple = intern_c_string ("stipple");
2281 staticpro (&Qunderline);
2282 Qunderline = intern_c_string ("underline");
2283 staticpro (&Qread_only);
2284 Qread_only = intern_c_string ("read-only");
2285 staticpro (&Qinvisible);
2286 Qinvisible = intern_c_string ("invisible");
2287 staticpro (&Qintangible);
2288 Qintangible = intern_c_string ("intangible");
2289 staticpro (&Qcategory);
2290 Qcategory = intern_c_string ("category");
2291 staticpro (&Qlocal_map);
2292 Qlocal_map = intern_c_string ("local-map");
2293 staticpro (&Qfront_sticky);
2294 Qfront_sticky = intern_c_string ("front-sticky");
2295 staticpro (&Qrear_nonsticky);
2296 Qrear_nonsticky = intern_c_string ("rear-nonsticky");
2297 staticpro (&Qmouse_face);
2298 Qmouse_face = intern_c_string ("mouse-face");
2299 staticpro (&Qminibuffer_prompt);
2300 Qminibuffer_prompt = intern_c_string ("minibuffer-prompt");
2302 /* Properties that text might use to specify certain actions */
2304 staticpro (&Qmouse_left);
2305 Qmouse_left = intern_c_string ("mouse-left");
2306 staticpro (&Qmouse_entered);
2307 Qmouse_entered = intern_c_string ("mouse-entered");
2308 staticpro (&Qpoint_left);
2309 Qpoint_left = intern_c_string ("point-left");
2310 staticpro (&Qpoint_entered);
2311 Qpoint_entered = intern_c_string ("point-entered");
2313 defsubr (&Stext_properties_at);
2314 defsubr (&Sget_text_property);
2315 defsubr (&Sget_char_property);
2316 defsubr (&Sget_char_property_and_overlay);
2317 defsubr (&Snext_char_property_change);
2318 defsubr (&Sprevious_char_property_change);
2319 defsubr (&Snext_single_char_property_change);
2320 defsubr (&Sprevious_single_char_property_change);
2321 defsubr (&Snext_property_change);
2322 defsubr (&Snext_single_property_change);
2323 defsubr (&Sprevious_property_change);
2324 defsubr (&Sprevious_single_property_change);
2325 defsubr (&Sadd_text_properties);
2326 defsubr (&Sput_text_property);
2327 defsubr (&Sset_text_properties);
2328 defsubr (&Sremove_text_properties);
2329 defsubr (&Sremove_list_of_text_properties);
2330 defsubr (&Stext_property_any);
2331 defsubr (&Stext_property_not_all);
2332 /* defsubr (&Serase_text_properties); */
2333 /* defsubr (&Scopy_text_properties); */
2336 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2337 (do not change this comment) */