etc/NEWS: Document change to mode-line-format.
[emacs.git] / src / textprop.c
blob88e869b13e90c800903ea4e2c10b9457119eae5f
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 EMACS_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 EMACS_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 EMACS_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 (EMACS_INT beg, EMACS_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 EMACS_INT s, len;
1177 register int modified = 0;
1178 struct gcpro gcpro1;
1180 properties = validate_plist (properties);
1181 if (NILP (properties))
1182 return Qnil;
1184 if (NILP (object))
1185 XSETBUFFER (object, current_buffer);
1187 i = validate_interval_range (object, &start, &end, hard);
1188 if (NULL_INTERVAL_P (i))
1189 return Qnil;
1191 s = XINT (start);
1192 len = XINT (end) - s;
1194 /* No need to protect OBJECT, because we GC only if it's a buffer,
1195 and live buffers are always protected. */
1196 GCPRO1 (properties);
1198 /* If we're not starting on an interval boundary, we have to
1199 split this interval. */
1200 if (i->position != s)
1202 /* If this interval already has the properties, we can
1203 skip it. */
1204 if (interval_has_all_properties (properties, i))
1206 EMACS_INT got = (LENGTH (i) - (s - i->position));
1207 if (got >= len)
1208 RETURN_UNGCPRO (Qnil);
1209 len -= got;
1210 i = next_interval (i);
1212 else
1214 unchanged = i;
1215 i = split_interval_right (unchanged, s - unchanged->position);
1216 copy_properties (unchanged, i);
1220 if (BUFFERP (object))
1221 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1223 /* We are at the beginning of interval I, with LEN chars to scan. */
1224 for (;;)
1226 if (i == 0)
1227 abort ();
1229 if (LENGTH (i) >= len)
1231 /* We can UNGCPRO safely here, because there will be just
1232 one more chance to gc, in the next call to add_properties,
1233 and after that we will not need PROPERTIES or OBJECT again. */
1234 UNGCPRO;
1236 if (interval_has_all_properties (properties, i))
1238 if (BUFFERP (object))
1239 signal_after_change (XINT (start), XINT (end) - XINT (start),
1240 XINT (end) - XINT (start));
1242 return modified ? Qt : Qnil;
1245 if (LENGTH (i) == len)
1247 add_properties (properties, i, object);
1248 if (BUFFERP (object))
1249 signal_after_change (XINT (start), XINT (end) - XINT (start),
1250 XINT (end) - XINT (start));
1251 return Qt;
1254 /* i doesn't have the properties, and goes past the change limit */
1255 unchanged = i;
1256 i = split_interval_left (unchanged, len);
1257 copy_properties (unchanged, i);
1258 add_properties (properties, i, object);
1259 if (BUFFERP (object))
1260 signal_after_change (XINT (start), XINT (end) - XINT (start),
1261 XINT (end) - XINT (start));
1262 return Qt;
1265 len -= LENGTH (i);
1266 modified += add_properties (properties, i, object);
1267 i = next_interval (i);
1271 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1273 DEFUN ("put-text-property", Fput_text_property,
1274 Sput_text_property, 4, 5, 0,
1275 doc: /* Set one property of the text from START to END.
1276 The third and fourth arguments PROPERTY and VALUE
1277 specify the property to add.
1278 If the optional fifth argument OBJECT is a buffer (or nil, which means
1279 the current buffer), START and END are buffer positions (integers or
1280 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1281 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1283 Fadd_text_properties (start, end,
1284 Fcons (property, Fcons (value, Qnil)),
1285 object);
1286 return Qnil;
1289 DEFUN ("set-text-properties", Fset_text_properties,
1290 Sset_text_properties, 3, 4, 0,
1291 doc: /* Completely replace properties of text from START to END.
1292 The third argument PROPERTIES is the new property list.
1293 If the optional fourth argument OBJECT is a buffer (or nil, which means
1294 the current buffer), START and END are buffer positions (integers or
1295 markers). If OBJECT is a string, START and END are 0-based indices into it.
1296 If PROPERTIES is nil, the effect is to remove all properties from
1297 the designated part of OBJECT. */)
1298 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1300 return set_text_properties (start, end, properties, object, Qt);
1304 /* Replace properties of text from START to END with new list of
1305 properties PROPERTIES. OBJECT is the buffer or string containing
1306 the text. OBJECT nil means use the current buffer.
1307 COHERENT_CHANGE_P nil means this is being called as an internal
1308 subroutine, rather than as a change primitive with checking of
1309 read-only, invoking change hooks, etc.. Value is nil if the
1310 function _detected_ that it did not replace any properties, non-nil
1311 otherwise. */
1313 Lisp_Object
1314 set_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object, Lisp_Object coherent_change_p)
1316 register INTERVAL i;
1317 Lisp_Object ostart, oend;
1319 ostart = start;
1320 oend = end;
1322 properties = validate_plist (properties);
1324 if (NILP (object))
1325 XSETBUFFER (object, current_buffer);
1327 /* If we want no properties for a whole string,
1328 get rid of its intervals. */
1329 if (NILP (properties) && STRINGP (object)
1330 && XFASTINT (start) == 0
1331 && XFASTINT (end) == SCHARS (object))
1333 if (! STRING_INTERVALS (object))
1334 return Qnil;
1336 STRING_SET_INTERVALS (object, NULL_INTERVAL);
1337 return Qt;
1340 i = validate_interval_range (object, &start, &end, soft);
1342 if (NULL_INTERVAL_P (i))
1344 /* If buffer has no properties, and we want none, return now. */
1345 if (NILP (properties))
1346 return Qnil;
1348 /* Restore the original START and END values
1349 because validate_interval_range increments them for strings. */
1350 start = ostart;
1351 end = oend;
1353 i = validate_interval_range (object, &start, &end, hard);
1354 /* This can return if start == end. */
1355 if (NULL_INTERVAL_P (i))
1356 return Qnil;
1359 if (BUFFERP (object) && !NILP (coherent_change_p))
1360 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1362 set_text_properties_1 (start, end, properties, object, i);
1364 if (BUFFERP (object) && !NILP (coherent_change_p))
1365 signal_after_change (XINT (start), XINT (end) - XINT (start),
1366 XINT (end) - XINT (start));
1367 return Qt;
1370 /* Replace properties of text from START to END with new list of
1371 properties PROPERTIES. BUFFER is the buffer containing
1372 the text. This does not obey any hooks.
1373 You can provide the interval that START is located in as I,
1374 or pass NULL for I and this function will find it.
1375 START and END can be in any order. */
1377 void
1378 set_text_properties_1 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object buffer, INTERVAL i)
1380 register INTERVAL prev_changed = NULL_INTERVAL;
1381 register EMACS_INT s, len;
1382 INTERVAL unchanged;
1384 s = XINT (start);
1385 len = XINT (end) - s;
1386 if (len == 0)
1387 return;
1388 if (len < 0)
1390 s = s + len;
1391 len = - len;
1394 if (i == 0)
1395 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1397 if (i->position != s)
1399 unchanged = i;
1400 i = split_interval_right (unchanged, s - unchanged->position);
1402 if (LENGTH (i) > len)
1404 copy_properties (unchanged, i);
1405 i = split_interval_left (i, len);
1406 set_properties (properties, i, buffer);
1407 return;
1410 set_properties (properties, i, buffer);
1412 if (LENGTH (i) == len)
1413 return;
1415 prev_changed = i;
1416 len -= LENGTH (i);
1417 i = next_interval (i);
1420 /* We are starting at the beginning of an interval, I */
1421 while (len > 0)
1423 if (i == 0)
1424 abort ();
1426 if (LENGTH (i) >= len)
1428 if (LENGTH (i) > len)
1429 i = split_interval_left (i, len);
1431 /* We have to call set_properties even if we are going to
1432 merge the intervals, so as to make the undo records
1433 and cause redisplay to happen. */
1434 set_properties (properties, i, buffer);
1435 if (!NULL_INTERVAL_P (prev_changed))
1436 merge_interval_left (i);
1437 return;
1440 len -= LENGTH (i);
1442 /* We have to call set_properties even if we are going to
1443 merge the intervals, so as to make the undo records
1444 and cause redisplay to happen. */
1445 set_properties (properties, i, buffer);
1446 if (NULL_INTERVAL_P (prev_changed))
1447 prev_changed = i;
1448 else
1449 prev_changed = i = merge_interval_left (i);
1451 i = next_interval (i);
1455 DEFUN ("remove-text-properties", Fremove_text_properties,
1456 Sremove_text_properties, 3, 4, 0,
1457 doc: /* Remove some properties from text from START to END.
1458 The third argument PROPERTIES is a property list
1459 whose property names specify the properties to remove.
1460 \(The values stored in PROPERTIES are ignored.)
1461 If the optional fourth argument OBJECT is a buffer (or nil, which means
1462 the current buffer), START and END are buffer positions (integers or
1463 markers). If OBJECT is a string, START and END are 0-based indices into it.
1464 Return t if any property was actually removed, nil otherwise.
1466 Use `set-text-properties' if you want to remove all text properties. */)
1467 (Lisp_Object start, Lisp_Object end, Lisp_Object properties, Lisp_Object object)
1469 register INTERVAL i, unchanged;
1470 register EMACS_INT s, len;
1471 register int modified = 0;
1473 if (NILP (object))
1474 XSETBUFFER (object, current_buffer);
1476 i = validate_interval_range (object, &start, &end, soft);
1477 if (NULL_INTERVAL_P (i))
1478 return Qnil;
1480 s = XINT (start);
1481 len = XINT (end) - s;
1483 if (i->position != s)
1485 /* No properties on this first interval -- return if
1486 it covers the entire region. */
1487 if (! interval_has_some_properties (properties, i))
1489 EMACS_INT got = (LENGTH (i) - (s - i->position));
1490 if (got >= len)
1491 return Qnil;
1492 len -= got;
1493 i = next_interval (i);
1495 /* Split away the beginning of this interval; what we don't
1496 want to modify. */
1497 else
1499 unchanged = i;
1500 i = split_interval_right (unchanged, s - unchanged->position);
1501 copy_properties (unchanged, i);
1505 if (BUFFERP (object))
1506 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1508 /* We are at the beginning of an interval, with len to scan */
1509 for (;;)
1511 if (i == 0)
1512 abort ();
1514 if (LENGTH (i) >= len)
1516 if (! interval_has_some_properties (properties, i))
1517 return modified ? Qt : Qnil;
1519 if (LENGTH (i) == len)
1521 remove_properties (properties, Qnil, i, object);
1522 if (BUFFERP (object))
1523 signal_after_change (XINT (start), XINT (end) - XINT (start),
1524 XINT (end) - XINT (start));
1525 return Qt;
1528 /* i has the properties, and goes past the change limit */
1529 unchanged = i;
1530 i = split_interval_left (i, len);
1531 copy_properties (unchanged, i);
1532 remove_properties (properties, Qnil, i, object);
1533 if (BUFFERP (object))
1534 signal_after_change (XINT (start), XINT (end) - XINT (start),
1535 XINT (end) - XINT (start));
1536 return Qt;
1539 len -= LENGTH (i);
1540 modified += remove_properties (properties, Qnil, i, object);
1541 i = next_interval (i);
1545 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1546 Sremove_list_of_text_properties, 3, 4, 0,
1547 doc: /* Remove some properties from text from START to END.
1548 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1549 If the optional fourth argument OBJECT is a buffer (or nil, which means
1550 the current buffer), START and END are buffer positions (integers or
1551 markers). If OBJECT is a string, START and END are 0-based indices into it.
1552 Return t if any property was actually removed, nil otherwise. */)
1553 (Lisp_Object start, Lisp_Object end, Lisp_Object list_of_properties, Lisp_Object object)
1555 register INTERVAL i, unchanged;
1556 register EMACS_INT s, len;
1557 register int modified = 0;
1558 Lisp_Object properties;
1559 properties = list_of_properties;
1561 if (NILP (object))
1562 XSETBUFFER (object, current_buffer);
1564 i = validate_interval_range (object, &start, &end, soft);
1565 if (NULL_INTERVAL_P (i))
1566 return Qnil;
1568 s = XINT (start);
1569 len = XINT (end) - s;
1571 if (i->position != s)
1573 /* No properties on this first interval -- return if
1574 it covers the entire region. */
1575 if (! interval_has_some_properties_list (properties, i))
1577 EMACS_INT got = (LENGTH (i) - (s - i->position));
1578 if (got >= len)
1579 return Qnil;
1580 len -= got;
1581 i = next_interval (i);
1583 /* Split away the beginning of this interval; what we don't
1584 want to modify. */
1585 else
1587 unchanged = i;
1588 i = split_interval_right (unchanged, s - unchanged->position);
1589 copy_properties (unchanged, i);
1593 /* We are at the beginning of an interval, with len to scan.
1594 The flag `modified' records if changes have been made.
1595 When object is a buffer, we must call modify_region before changes are
1596 made and signal_after_change when we are done.
1597 We call modify_region before calling remove_properties if modified == 0,
1598 and we call signal_after_change before returning if modified != 0. */
1599 for (;;)
1601 if (i == 0)
1602 abort ();
1604 if (LENGTH (i) >= len)
1606 if (! interval_has_some_properties_list (properties, i))
1607 if (modified)
1609 if (BUFFERP (object))
1610 signal_after_change (XINT (start), XINT (end) - XINT (start),
1611 XINT (end) - XINT (start));
1612 return Qt;
1614 else
1615 return Qnil;
1617 if (LENGTH (i) == len)
1619 if (!modified && BUFFERP (object))
1620 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1621 remove_properties (Qnil, properties, i, object);
1622 if (BUFFERP (object))
1623 signal_after_change (XINT (start), XINT (end) - XINT (start),
1624 XINT (end) - XINT (start));
1625 return Qt;
1628 /* i has the properties, and goes past the change limit */
1629 unchanged = i;
1630 i = split_interval_left (i, len);
1631 copy_properties (unchanged, i);
1632 if (!modified && BUFFERP (object))
1633 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1634 remove_properties (Qnil, properties, i, object);
1635 if (BUFFERP (object))
1636 signal_after_change (XINT (start), XINT (end) - XINT (start),
1637 XINT (end) - XINT (start));
1638 return Qt;
1641 if (interval_has_some_properties_list (properties, i))
1643 if (!modified && BUFFERP (object))
1644 modify_region (XBUFFER (object), XINT (start), XINT (end), 1);
1645 remove_properties (Qnil, properties, i, object);
1646 modified = 1;
1648 len -= LENGTH (i);
1649 i = next_interval (i);
1653 DEFUN ("text-property-any", Ftext_property_any,
1654 Stext_property_any, 4, 5, 0,
1655 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1656 If so, return the position of the first character whose property PROPERTY
1657 is `eq' to VALUE. Otherwise return nil.
1658 If the optional fifth argument OBJECT is a buffer (or nil, which means
1659 the current buffer), START and END are buffer positions (integers or
1660 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1661 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1663 register INTERVAL i;
1664 register EMACS_INT e, pos;
1666 if (NILP (object))
1667 XSETBUFFER (object, current_buffer);
1668 i = validate_interval_range (object, &start, &end, soft);
1669 if (NULL_INTERVAL_P (i))
1670 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1671 e = XINT (end);
1673 while (! NULL_INTERVAL_P (i))
1675 if (i->position >= e)
1676 break;
1677 if (EQ (textget (i->plist, property), value))
1679 pos = i->position;
1680 if (pos < XINT (start))
1681 pos = XINT (start);
1682 return make_number (pos);
1684 i = next_interval (i);
1686 return Qnil;
1689 DEFUN ("text-property-not-all", Ftext_property_not_all,
1690 Stext_property_not_all, 4, 5, 0,
1691 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1692 If so, return the position of the first character whose property PROPERTY
1693 is not `eq' to VALUE. Otherwise, return nil.
1694 If the optional fifth argument OBJECT is a buffer (or nil, which means
1695 the current buffer), START and END are buffer positions (integers or
1696 markers). If OBJECT is a string, START and END are 0-based indices into it. */)
1697 (Lisp_Object start, Lisp_Object end, Lisp_Object property, Lisp_Object value, Lisp_Object object)
1699 register INTERVAL i;
1700 register EMACS_INT s, e;
1702 if (NILP (object))
1703 XSETBUFFER (object, current_buffer);
1704 i = validate_interval_range (object, &start, &end, soft);
1705 if (NULL_INTERVAL_P (i))
1706 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1707 s = XINT (start);
1708 e = XINT (end);
1710 while (! NULL_INTERVAL_P (i))
1712 if (i->position >= e)
1713 break;
1714 if (! EQ (textget (i->plist, property), value))
1716 if (i->position > s)
1717 s = i->position;
1718 return make_number (s);
1720 i = next_interval (i);
1722 return Qnil;
1726 /* Return the direction from which the text-property PROP would be
1727 inherited by any new text inserted at POS: 1 if it would be
1728 inherited from the char after POS, -1 if it would be inherited from
1729 the char before POS, and 0 if from neither.
1730 BUFFER can be either a buffer or nil (meaning current buffer). */
1733 text_property_stickiness (Lisp_Object prop, Lisp_Object pos, Lisp_Object buffer)
1735 Lisp_Object prev_pos, front_sticky;
1736 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1738 if (NILP (buffer))
1739 XSETBUFFER (buffer, current_buffer);
1741 if (XINT (pos) > BUF_BEGV (XBUFFER (buffer)))
1742 /* Consider previous character. */
1744 Lisp_Object rear_non_sticky;
1746 prev_pos = make_number (XINT (pos) - 1);
1747 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, buffer);
1749 if (!NILP (CONSP (rear_non_sticky)
1750 ? Fmemq (prop, rear_non_sticky)
1751 : rear_non_sticky))
1752 /* PROP is rear-non-sticky. */
1753 is_rear_sticky = 0;
1755 else
1756 return 0;
1758 /* Consider following character. */
1759 /* This signals an arg-out-of-range error if pos is outside the
1760 buffer's accessible range. */
1761 front_sticky = Fget_text_property (pos, Qfront_sticky, buffer);
1763 if (EQ (front_sticky, Qt)
1764 || (CONSP (front_sticky)
1765 && !NILP (Fmemq (prop, front_sticky))))
1766 /* PROP is inherited from after. */
1767 is_front_sticky = 1;
1769 /* Simple cases, where the properties are consistent. */
1770 if (is_rear_sticky && !is_front_sticky)
1771 return -1;
1772 else if (!is_rear_sticky && is_front_sticky)
1773 return 1;
1774 else if (!is_rear_sticky && !is_front_sticky)
1775 return 0;
1777 /* The stickiness properties are inconsistent, so we have to
1778 disambiguate. Basically, rear-sticky wins, _except_ if the
1779 property that would be inherited has a value of nil, in which case
1780 front-sticky wins. */
1781 if (XINT (pos) == BUF_BEGV (XBUFFER (buffer))
1782 || NILP (Fget_text_property (prev_pos, prop, buffer)))
1783 return 1;
1784 else
1785 return -1;
1789 /* I don't think this is the right interface to export; how often do you
1790 want to do something like this, other than when you're copying objects
1791 around?
1793 I think it would be better to have a pair of functions, one which
1794 returns the text properties of a region as a list of ranges and
1795 plists, and another which applies such a list to another object. */
1797 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1798 SRC and DEST may each refer to strings or buffers.
1799 Optional sixth argument PROP causes only that property to be copied.
1800 Properties are copied to DEST as if by `add-text-properties'.
1801 Return t if any property value actually changed, nil otherwise. */
1803 /* Note this can GC when DEST is a buffer. */
1805 Lisp_Object
1806 copy_text_properties (Lisp_Object start, Lisp_Object end, Lisp_Object src, Lisp_Object pos, Lisp_Object dest, Lisp_Object prop)
1808 INTERVAL i;
1809 Lisp_Object res;
1810 Lisp_Object stuff;
1811 Lisp_Object plist;
1812 EMACS_INT s, e, e2, p, len;
1813 int modified = 0;
1814 struct gcpro gcpro1, gcpro2;
1816 i = validate_interval_range (src, &start, &end, soft);
1817 if (NULL_INTERVAL_P (i))
1818 return Qnil;
1820 CHECK_NUMBER_COERCE_MARKER (pos);
1822 Lisp_Object dest_start, dest_end;
1824 dest_start = pos;
1825 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1826 /* Apply this to a copy of pos; it will try to increment its arguments,
1827 which we don't want. */
1828 validate_interval_range (dest, &dest_start, &dest_end, soft);
1831 s = XINT (start);
1832 e = XINT (end);
1833 p = XINT (pos);
1835 stuff = Qnil;
1837 while (s < e)
1839 e2 = i->position + LENGTH (i);
1840 if (e2 > e)
1841 e2 = e;
1842 len = e2 - s;
1844 plist = i->plist;
1845 if (! NILP (prop))
1846 while (! NILP (plist))
1848 if (EQ (Fcar (plist), prop))
1850 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1851 break;
1853 plist = Fcdr (Fcdr (plist));
1855 if (! NILP (plist))
1857 /* Must defer modifications to the interval tree in case src
1858 and dest refer to the same string or buffer. */
1859 stuff = Fcons (Fcons (make_number (p),
1860 Fcons (make_number (p + len),
1861 Fcons (plist, Qnil))),
1862 stuff);
1865 i = next_interval (i);
1866 if (NULL_INTERVAL_P (i))
1867 break;
1869 p += len;
1870 s = i->position;
1873 GCPRO2 (stuff, dest);
1875 while (! NILP (stuff))
1877 res = Fcar (stuff);
1878 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1879 Fcar (Fcdr (Fcdr (res))), dest);
1880 if (! NILP (res))
1881 modified++;
1882 stuff = Fcdr (stuff);
1885 UNGCPRO;
1887 return modified ? Qt : Qnil;
1891 /* Return a list representing the text properties of OBJECT between
1892 START and END. if PROP is non-nil, report only on that property.
1893 Each result list element has the form (S E PLIST), where S and E
1894 are positions in OBJECT and PLIST is a property list containing the
1895 text properties of OBJECT between S and E. Value is nil if OBJECT
1896 doesn't contain text properties between START and END. */
1898 Lisp_Object
1899 text_property_list (Lisp_Object object, Lisp_Object start, Lisp_Object end, Lisp_Object prop)
1901 struct interval *i;
1902 Lisp_Object result;
1904 result = Qnil;
1906 i = validate_interval_range (object, &start, &end, soft);
1907 if (!NULL_INTERVAL_P (i))
1909 EMACS_INT s = XINT (start);
1910 EMACS_INT e = XINT (end);
1912 while (s < e)
1914 EMACS_INT interval_end, len;
1915 Lisp_Object plist;
1917 interval_end = i->position + LENGTH (i);
1918 if (interval_end > e)
1919 interval_end = e;
1920 len = interval_end - s;
1922 plist = i->plist;
1924 if (!NILP (prop))
1925 for (; CONSP (plist); plist = Fcdr (XCDR (plist)))
1926 if (EQ (XCAR (plist), prop))
1928 plist = Fcons (prop, Fcons (Fcar (XCDR (plist)), Qnil));
1929 break;
1932 if (!NILP (plist))
1933 result = Fcons (Fcons (make_number (s),
1934 Fcons (make_number (s + len),
1935 Fcons (plist, Qnil))),
1936 result);
1938 i = next_interval (i);
1939 if (NULL_INTERVAL_P (i))
1940 break;
1941 s = i->position;
1945 return result;
1949 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1950 (START END PLIST), where START and END are positions and PLIST is a
1951 property list containing the text properties to add. Adjust START
1952 and END positions by DELTA before adding properties. Value is
1953 non-zero if OBJECT was modified. */
1956 add_text_properties_from_list (Lisp_Object object, Lisp_Object list, Lisp_Object delta)
1958 struct gcpro gcpro1, gcpro2;
1959 int modified_p = 0;
1961 GCPRO2 (list, object);
1963 for (; CONSP (list); list = XCDR (list))
1965 Lisp_Object item, start, end, plist, tem;
1967 item = XCAR (list);
1968 start = make_number (XINT (XCAR (item)) + XINT (delta));
1969 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1970 plist = XCAR (XCDR (XCDR (item)));
1972 tem = Fadd_text_properties (start, end, plist, object);
1973 if (!NILP (tem))
1974 modified_p = 1;
1977 UNGCPRO;
1978 return modified_p;
1983 /* Modify end-points of ranges in LIST destructively, and return the
1984 new list. LIST is a list as returned from text_property_list.
1985 Discard properties that begin at or after NEW_END, and limit
1986 end-points to NEW_END. */
1988 Lisp_Object
1989 extend_property_ranges (Lisp_Object list, Lisp_Object new_end)
1991 Lisp_Object prev = Qnil, head = list;
1992 EMACS_INT max = XINT (new_end);
1994 for (; CONSP (list); prev = list, list = XCDR (list))
1996 Lisp_Object item, beg, end;
1998 item = XCAR (list);
1999 beg = XCAR (item);
2000 end = XCAR (XCDR (item));
2002 if (XINT (beg) >= max)
2004 /* The start-point is past the end of the new string.
2005 Discard this property. */
2006 if (EQ (head, list))
2007 head = XCDR (list);
2008 else
2009 XSETCDR (prev, XCDR (list));
2011 else if (XINT (end) > max)
2012 /* The end-point is past the end of the new string. */
2013 XSETCAR (XCDR (item), new_end);
2016 return head;
2021 /* Call the modification hook functions in LIST, each with START and END. */
2023 static void
2024 call_mod_hooks (Lisp_Object list, Lisp_Object start, Lisp_Object end)
2026 struct gcpro gcpro1;
2027 GCPRO1 (list);
2028 while (!NILP (list))
2030 call2 (Fcar (list), start, end);
2031 list = Fcdr (list);
2033 UNGCPRO;
2036 /* Check for read-only intervals between character positions START ... END,
2037 in BUF, and signal an error if we find one.
2039 Then check for any modification hooks in the range.
2040 Create a list of all these hooks in lexicographic order,
2041 eliminating consecutive extra copies of the same hook. Then call
2042 those hooks in order, with START and END - 1 as arguments. */
2044 void
2045 verify_interval_modification (struct buffer *buf, int start, int end)
2047 register INTERVAL intervals = BUF_INTERVALS (buf);
2048 register INTERVAL i;
2049 Lisp_Object hooks;
2050 register Lisp_Object prev_mod_hooks;
2051 Lisp_Object mod_hooks;
2052 struct gcpro gcpro1;
2054 hooks = Qnil;
2055 prev_mod_hooks = Qnil;
2056 mod_hooks = Qnil;
2058 interval_insert_behind_hooks = Qnil;
2059 interval_insert_in_front_hooks = Qnil;
2061 if (NULL_INTERVAL_P (intervals))
2062 return;
2064 if (start > end)
2066 EMACS_INT temp = start;
2067 start = end;
2068 end = temp;
2071 /* For an insert operation, check the two chars around the position. */
2072 if (start == end)
2074 INTERVAL prev = NULL;
2075 Lisp_Object before, after;
2077 /* Set I to the interval containing the char after START,
2078 and PREV to the interval containing the char before START.
2079 Either one may be null. They may be equal. */
2080 i = find_interval (intervals, start);
2082 if (start == BUF_BEGV (buf))
2083 prev = 0;
2084 else if (i->position == start)
2085 prev = previous_interval (i);
2086 else if (i->position < start)
2087 prev = i;
2088 if (start == BUF_ZV (buf))
2089 i = 0;
2091 /* If Vinhibit_read_only is set and is not a list, we can
2092 skip the read_only checks. */
2093 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2095 /* If I and PREV differ we need to check for the read-only
2096 property together with its stickiness. If either I or
2097 PREV are 0, this check is all we need.
2098 We have to take special care, since read-only may be
2099 indirectly defined via the category property. */
2100 if (i != prev)
2102 if (! NULL_INTERVAL_P (i))
2104 after = textget (i->plist, Qread_only);
2106 /* If interval I is read-only and read-only is
2107 front-sticky, inhibit insertion.
2108 Check for read-only as well as category. */
2109 if (! NILP (after)
2110 && NILP (Fmemq (after, Vinhibit_read_only)))
2112 Lisp_Object tem;
2114 tem = textget (i->plist, Qfront_sticky);
2115 if (TMEM (Qread_only, tem)
2116 || (NILP (Fplist_get (i->plist, Qread_only))
2117 && TMEM (Qcategory, tem)))
2118 text_read_only (after);
2122 if (! NULL_INTERVAL_P (prev))
2124 before = textget (prev->plist, Qread_only);
2126 /* If interval PREV is read-only and read-only isn't
2127 rear-nonsticky, inhibit insertion.
2128 Check for read-only as well as category. */
2129 if (! NILP (before)
2130 && NILP (Fmemq (before, Vinhibit_read_only)))
2132 Lisp_Object tem;
2134 tem = textget (prev->plist, Qrear_nonsticky);
2135 if (! TMEM (Qread_only, tem)
2136 && (! NILP (Fplist_get (prev->plist,Qread_only))
2137 || ! TMEM (Qcategory, tem)))
2138 text_read_only (before);
2142 else if (! NULL_INTERVAL_P (i))
2144 after = textget (i->plist, Qread_only);
2146 /* If interval I is read-only and read-only is
2147 front-sticky, inhibit insertion.
2148 Check for read-only as well as category. */
2149 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2151 Lisp_Object tem;
2153 tem = textget (i->plist, Qfront_sticky);
2154 if (TMEM (Qread_only, tem)
2155 || (NILP (Fplist_get (i->plist, Qread_only))
2156 && TMEM (Qcategory, tem)))
2157 text_read_only (after);
2159 tem = textget (prev->plist, Qrear_nonsticky);
2160 if (! TMEM (Qread_only, tem)
2161 && (! NILP (Fplist_get (prev->plist, Qread_only))
2162 || ! TMEM (Qcategory, tem)))
2163 text_read_only (after);
2168 /* Run both insert hooks (just once if they're the same). */
2169 if (!NULL_INTERVAL_P (prev))
2170 interval_insert_behind_hooks
2171 = textget (prev->plist, Qinsert_behind_hooks);
2172 if (!NULL_INTERVAL_P (i))
2173 interval_insert_in_front_hooks
2174 = textget (i->plist, Qinsert_in_front_hooks);
2176 else
2178 /* Loop over intervals on or next to START...END,
2179 collecting their hooks. */
2181 i = find_interval (intervals, start);
2184 if (! INTERVAL_WRITABLE_P (i))
2185 text_read_only (textget (i->plist, Qread_only));
2187 if (!inhibit_modification_hooks)
2189 mod_hooks = textget (i->plist, Qmodification_hooks);
2190 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2192 hooks = Fcons (mod_hooks, hooks);
2193 prev_mod_hooks = mod_hooks;
2197 i = next_interval (i);
2199 /* Keep going thru the interval containing the char before END. */
2200 while (! NULL_INTERVAL_P (i) && i->position < end);
2202 if (!inhibit_modification_hooks)
2204 GCPRO1 (hooks);
2205 hooks = Fnreverse (hooks);
2206 while (! EQ (hooks, Qnil))
2208 call_mod_hooks (Fcar (hooks), make_number (start),
2209 make_number (end));
2210 hooks = Fcdr (hooks);
2212 UNGCPRO;
2217 /* Run the interval hooks for an insertion on character range START ... END.
2218 verify_interval_modification chose which hooks to run;
2219 this function is called after the insertion happens
2220 so it can indicate the range of inserted text. */
2222 void
2223 report_interval_modification (Lisp_Object start, Lisp_Object end)
2225 if (! NILP (interval_insert_behind_hooks))
2226 call_mod_hooks (interval_insert_behind_hooks, start, end);
2227 if (! NILP (interval_insert_in_front_hooks)
2228 && ! EQ (interval_insert_in_front_hooks,
2229 interval_insert_behind_hooks))
2230 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2233 void
2234 syms_of_textprop (void)
2236 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2237 doc: /* Property-list used as default values.
2238 The value of a property in this list is seen as the value for every
2239 character that does not have its own value for that property. */);
2240 Vdefault_text_properties = Qnil;
2242 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2243 doc: /* Alist of alternative properties for properties without a value.
2244 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2245 If a piece of text has no direct value for a particular property, then
2246 this alist is consulted. If that property appears in the alist, then
2247 the first non-nil value from the associated alternative properties is
2248 returned. */);
2249 Vchar_property_alias_alist = Qnil;
2251 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2252 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2253 This also inhibits the use of the `intangible' text property. */);
2254 Vinhibit_point_motion_hooks = Qnil;
2256 DEFVAR_LISP ("text-property-default-nonsticky",
2257 &Vtext_property_default_nonsticky,
2258 doc: /* Alist of properties vs the corresponding non-stickinesses.
2259 Each element has the form (PROPERTY . NONSTICKINESS).
2261 If a character in a buffer has PROPERTY, new text inserted adjacent to
2262 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2263 inherits it if NONSTICKINESS is nil. The `front-sticky' and
2264 `rear-nonsticky' properties of the character override NONSTICKINESS. */);
2265 /* Text property `syntax-table' should be nonsticky by default. */
2266 Vtext_property_default_nonsticky
2267 = Fcons (Fcons (intern_c_string ("syntax-table"), Qt), Qnil);
2269 staticpro (&interval_insert_behind_hooks);
2270 staticpro (&interval_insert_in_front_hooks);
2271 interval_insert_behind_hooks = Qnil;
2272 interval_insert_in_front_hooks = Qnil;
2275 /* Common attributes one might give text */
2277 staticpro (&Qforeground);
2278 Qforeground = intern_c_string ("foreground");
2279 staticpro (&Qbackground);
2280 Qbackground = intern_c_string ("background");
2281 staticpro (&Qfont);
2282 Qfont = intern_c_string ("font");
2283 staticpro (&Qstipple);
2284 Qstipple = intern_c_string ("stipple");
2285 staticpro (&Qunderline);
2286 Qunderline = intern_c_string ("underline");
2287 staticpro (&Qread_only);
2288 Qread_only = intern_c_string ("read-only");
2289 staticpro (&Qinvisible);
2290 Qinvisible = intern_c_string ("invisible");
2291 staticpro (&Qintangible);
2292 Qintangible = intern_c_string ("intangible");
2293 staticpro (&Qcategory);
2294 Qcategory = intern_c_string ("category");
2295 staticpro (&Qlocal_map);
2296 Qlocal_map = intern_c_string ("local-map");
2297 staticpro (&Qfront_sticky);
2298 Qfront_sticky = intern_c_string ("front-sticky");
2299 staticpro (&Qrear_nonsticky);
2300 Qrear_nonsticky = intern_c_string ("rear-nonsticky");
2301 staticpro (&Qmouse_face);
2302 Qmouse_face = intern_c_string ("mouse-face");
2303 staticpro (&Qminibuffer_prompt);
2304 Qminibuffer_prompt = intern_c_string ("minibuffer-prompt");
2306 /* Properties that text might use to specify certain actions */
2308 staticpro (&Qmouse_left);
2309 Qmouse_left = intern_c_string ("mouse-left");
2310 staticpro (&Qmouse_entered);
2311 Qmouse_entered = intern_c_string ("mouse-entered");
2312 staticpro (&Qpoint_left);
2313 Qpoint_left = intern_c_string ("point-left");
2314 staticpro (&Qpoint_entered);
2315 Qpoint_entered = intern_c_string ("point-entered");
2317 defsubr (&Stext_properties_at);
2318 defsubr (&Sget_text_property);
2319 defsubr (&Sget_char_property);
2320 defsubr (&Sget_char_property_and_overlay);
2321 defsubr (&Snext_char_property_change);
2322 defsubr (&Sprevious_char_property_change);
2323 defsubr (&Snext_single_char_property_change);
2324 defsubr (&Sprevious_single_char_property_change);
2325 defsubr (&Snext_property_change);
2326 defsubr (&Snext_single_property_change);
2327 defsubr (&Sprevious_property_change);
2328 defsubr (&Sprevious_single_property_change);
2329 defsubr (&Sadd_text_properties);
2330 defsubr (&Sput_text_property);
2331 defsubr (&Sset_text_properties);
2332 defsubr (&Sremove_text_properties);
2333 defsubr (&Sremove_list_of_text_properties);
2334 defsubr (&Stext_property_any);
2335 defsubr (&Stext_property_not_all);
2336 /* defsubr (&Serase_text_properties); */
2337 /* defsubr (&Scopy_text_properties); */
2340 /* arch-tag: 454cdde8-5f86-4faa-a078-101e3625d479
2341 (do not change this comment) */