* progmodes/ada-mode.el: Update copyright notice.
[emacs.git] / src / textprop.c
blobf6b7dab421e05ec0df3d9c00541a19e92a4715a1
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 #include <config.h>
23 #include "lisp.h"
24 #include "intervals.h"
25 #include "buffer.h"
26 #include "window.h"
28 #ifndef NULL
29 #define NULL (void *)0
30 #endif
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
42 set_properties needs to deal with the interval property cache.
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
51 /* Types of hooks. */
52 Lisp_Object Qmouse_left;
53 Lisp_Object Qmouse_entered;
54 Lisp_Object Qpoint_left;
55 Lisp_Object Qpoint_entered;
56 Lisp_Object Qcategory;
57 Lisp_Object Qlocal_map;
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
61 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
71 Lisp_Object Vinhibit_point_motion_hooks;
72 Lisp_Object Vdefault_text_properties;
73 Lisp_Object Vchar_property_alias_alist;
74 Lisp_Object Vtext_property_default_nonsticky;
76 /* verify_interval_modification saves insertion hooks here
77 to be run later by report_interval_modification. */
78 Lisp_Object interval_insert_behind_hooks;
79 Lisp_Object interval_insert_in_front_hooks;
82 /* Signal a `text-read-only' error. This function makes it easier
83 to capture that error in GDB by putting a breakpoint on it. */
85 static void
86 text_read_only ()
88 Fsignal (Qtext_read_only, Qnil);
93 /* Extract the interval at the position pointed to by BEGIN from
94 OBJECT, a string or buffer. Additionally, check that the positions
95 pointed to by BEGIN and END are within the bounds of OBJECT, and
96 reverse them if *BEGIN is greater than *END. The objects pointed
97 to by BEGIN and END may be integers or markers; if the latter, they
98 are coerced to integers.
100 When OBJECT is a string, we increment *BEGIN and *END
101 to make them origin-one.
103 Note that buffer points don't correspond to interval indices.
104 For example, point-max is 1 greater than the index of the last
105 character. This difference is handled in the caller, which uses
106 the validated points to determine a length, and operates on that.
107 Exceptions are Ftext_properties_at, Fnext_property_change, and
108 Fprevious_property_change which call this function with BEGIN == END.
109 Handle this case specially.
111 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
112 create an interval tree for OBJECT if one doesn't exist, provided
113 the object actually contains text. In the current design, if there
114 is no text, there can be no text properties. */
116 #define soft 0
117 #define hard 1
119 INTERVAL
120 validate_interval_range (object, begin, end, force)
121 Lisp_Object object, *begin, *end;
122 int force;
124 register INTERVAL i;
125 int searchpos;
127 CHECK_STRING_OR_BUFFER (object);
128 CHECK_NUMBER_COERCE_MARKER (*begin);
129 CHECK_NUMBER_COERCE_MARKER (*end);
131 /* If we are asked for a point, but from a subr which operates
132 on a range, then return nothing. */
133 if (EQ (*begin, *end) && begin != end)
134 return NULL_INTERVAL;
136 if (XINT (*begin) > XINT (*end))
138 Lisp_Object n;
139 n = *begin;
140 *begin = *end;
141 *end = n;
144 if (BUFFERP (object))
146 register struct buffer *b = XBUFFER (object);
148 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
149 && XINT (*end) <= BUF_ZV (b)))
150 args_out_of_range (*begin, *end);
151 i = BUF_INTERVALS (b);
153 /* If there's no text, there are no properties. */
154 if (BUF_BEGV (b) == BUF_ZV (b))
155 return NULL_INTERVAL;
157 searchpos = XINT (*begin);
159 else
161 register struct Lisp_String *s = XSTRING (object);
163 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
164 && XINT (*end) <= s->size))
165 args_out_of_range (*begin, *end);
166 XSETFASTINT (*begin, XFASTINT (*begin));
167 if (begin != end)
168 XSETFASTINT (*end, XFASTINT (*end));
169 i = s->intervals;
171 if (s->size == 0)
172 return NULL_INTERVAL;
174 searchpos = XINT (*begin);
177 if (NULL_INTERVAL_P (i))
178 return (force ? create_root_interval (object) : i);
180 return find_interval (i, searchpos);
183 /* Validate LIST as a property list. If LIST is not a list, then
184 make one consisting of (LIST nil). Otherwise, verify that LIST
185 is even numbered and thus suitable as a plist. */
187 static Lisp_Object
188 validate_plist (list)
189 Lisp_Object list;
191 if (NILP (list))
192 return Qnil;
194 if (CONSP (list))
196 register int i;
197 register Lisp_Object tail;
198 for (i = 0, tail = list; !NILP (tail); i++)
200 tail = Fcdr (tail);
201 QUIT;
203 if (i & 1)
204 error ("Odd length text property list");
205 return list;
208 return Fcons (list, Fcons (Qnil, Qnil));
211 /* Return nonzero if interval I has all the properties,
212 with the same values, of list PLIST. */
214 static int
215 interval_has_all_properties (plist, i)
216 Lisp_Object plist;
217 INTERVAL i;
219 register Lisp_Object tail1, tail2, sym1;
220 register int found;
222 /* Go through each element of PLIST. */
223 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
225 sym1 = Fcar (tail1);
226 found = 0;
228 /* Go through I's plist, looking for sym1 */
229 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
230 if (EQ (sym1, Fcar (tail2)))
232 /* Found the same property on both lists. If the
233 values are unequal, return zero. */
234 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (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 (plist, i)
254 Lisp_Object plist;
255 INTERVAL i;
257 register Lisp_Object tail1, tail2, sym;
259 /* Go through each element of PLIST. */
260 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
262 sym = Fcar (tail1);
264 /* Go through i's plist, looking for tail1 */
265 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
266 if (EQ (sym, Fcar (tail2)))
267 return 1;
270 return 0;
273 /* Return nonzero if the plist of interval I has any of the
274 property names in LIST, regardless of their values. */
276 static INLINE int
277 interval_has_some_properties_list (list, i)
278 Lisp_Object list;
279 INTERVAL i;
281 register Lisp_Object tail1, tail2, sym;
283 /* Go through each element of LIST. */
284 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
286 sym = Fcar (tail1);
288 /* Go through i's plist, looking for tail1 */
289 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
290 if (EQ (sym, XCAR (tail2)))
291 return 1;
294 return 0;
297 /* Changing the plists of individual intervals. */
299 /* Return the value of PROP in property-list PLIST, or Qunbound if it
300 has none. */
301 static Lisp_Object
302 property_value (plist, prop)
303 Lisp_Object plist, prop;
305 Lisp_Object value;
307 while (PLIST_ELT_P (plist, value))
308 if (EQ (XCAR (plist), prop))
309 return XCAR (value);
310 else
311 plist = XCDR (value);
313 return Qunbound;
316 /* Set the properties of INTERVAL to PROPERTIES,
317 and record undo info for the previous values.
318 OBJECT is the string or buffer that INTERVAL belongs to. */
320 static void
321 set_properties (properties, interval, object)
322 Lisp_Object properties, object;
323 INTERVAL interval;
325 Lisp_Object sym, value;
327 if (BUFFERP (object))
329 /* For each property in the old plist which is missing from PROPERTIES,
330 or has a different value in PROPERTIES, make an undo record. */
331 for (sym = interval->plist;
332 PLIST_ELT_P (sym, value);
333 sym = XCDR (value))
334 if (! EQ (property_value (properties, XCAR (sym)),
335 XCAR (value)))
337 record_property_change (interval->position, LENGTH (interval),
338 XCAR (sym), XCAR (value),
339 object);
342 /* For each new property that has no value at all in the old plist,
343 make an undo record binding it to nil, so it will be removed. */
344 for (sym = properties;
345 PLIST_ELT_P (sym, value);
346 sym = XCDR (value))
347 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
349 record_property_change (interval->position, LENGTH (interval),
350 XCAR (sym), Qnil,
351 object);
355 /* Store new properties. */
356 interval->plist = Fcopy_sequence (properties);
359 /* Add the properties of PLIST to the interval I, or set
360 the value of I's property to the value of the property on PLIST
361 if they are different.
363 OBJECT should be the string or buffer the interval is in.
365 Return nonzero if this changes I (i.e., if any members of PLIST
366 are actually added to I's plist) */
368 static int
369 add_properties (plist, i, object)
370 Lisp_Object plist;
371 INTERVAL i;
372 Lisp_Object object;
374 Lisp_Object tail1, tail2, sym1, val1;
375 register int changed = 0;
376 register int found;
377 struct gcpro gcpro1, gcpro2, gcpro3;
379 tail1 = plist;
380 sym1 = Qnil;
381 val1 = Qnil;
382 /* No need to protect OBJECT, because we can GC only in the case
383 where it is a buffer, and live buffers are always protected.
384 I and its plist are also protected, via OBJECT. */
385 GCPRO3 (tail1, sym1, val1);
387 /* Go through each element of PLIST. */
388 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
390 sym1 = Fcar (tail1);
391 val1 = Fcar (Fcdr (tail1));
392 found = 0;
394 /* Go through I's plist, looking for sym1 */
395 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
396 if (EQ (sym1, Fcar (tail2)))
398 /* No need to gcpro, because tail2 protects this
399 and it must be a cons cell (we get an error otherwise). */
400 register Lisp_Object this_cdr;
402 this_cdr = Fcdr (tail2);
403 /* Found the property. Now check its value. */
404 found = 1;
406 /* The properties have the same value on both lists.
407 Continue to the next property. */
408 if (EQ (val1, Fcar (this_cdr)))
409 break;
411 /* Record this change in the buffer, for undo purposes. */
412 if (BUFFERP (object))
414 record_property_change (i->position, LENGTH (i),
415 sym1, Fcar (this_cdr), object);
418 /* I's property has a different value -- change it */
419 Fsetcar (this_cdr, val1);
420 changed++;
421 break;
424 if (! found)
426 /* Record this change in the buffer, for undo purposes. */
427 if (BUFFERP (object))
429 record_property_change (i->position, LENGTH (i),
430 sym1, Qnil, object);
432 i->plist = Fcons (sym1, Fcons (val1, i->plist));
433 changed++;
437 UNGCPRO;
439 return changed;
442 /* For any members of PLIST, or LIST,
443 which are properties of I, remove them from I's plist.
444 (If PLIST is non-nil, use that, otherwise use LIST.)
445 OBJECT is the string or buffer containing I. */
447 static int
448 remove_properties (plist, list, i, object)
449 Lisp_Object plist, list;
450 INTERVAL i;
451 Lisp_Object object;
453 register Lisp_Object tail1, tail2, sym, current_plist;
454 register int changed = 0;
456 /* Nonzero means tail1 is a plist, otherwise it is a list. */
457 int use_plist;
459 current_plist = i->plist;
461 if (! NILP (plist))
462 tail1 = plist, use_plist = 1;
463 else
464 tail1 = list, use_plist = 0;
466 /* Go through each element of LIST or PLIST. */
467 while (CONSP (tail1))
469 sym = XCAR (tail1);
471 /* First, remove the symbol if it's at the head of the list */
472 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
474 if (BUFFERP (object))
475 record_property_change (i->position, LENGTH (i),
476 sym, XCAR (XCDR (current_plist)),
477 object);
479 current_plist = XCDR (XCDR (current_plist));
480 changed++;
483 /* Go through I's plist, looking for SYM. */
484 tail2 = current_plist;
485 while (! NILP (tail2))
487 register Lisp_Object this;
488 this = XCDR (XCDR (tail2));
489 if (CONSP (this) && EQ (sym, XCAR (this)))
491 if (BUFFERP (object))
492 record_property_change (i->position, LENGTH (i),
493 sym, XCAR (XCDR (this)), object);
495 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
496 changed++;
498 tail2 = this;
501 /* Advance thru TAIL1 one way or the other. */
502 tail1 = XCDR (tail1);
503 if (use_plist && CONSP (tail1))
504 tail1 = XCDR (tail1);
507 if (changed)
508 i->plist = current_plist;
509 return changed;
512 #if 0
513 /* Remove all properties from interval I. Return non-zero
514 if this changes the interval. */
516 static INLINE int
517 erase_properties (i)
518 INTERVAL i;
520 if (NILP (i->plist))
521 return 0;
523 i->plist = Qnil;
524 return 1;
526 #endif
528 /* Returns the interval of POSITION in OBJECT.
529 POSITION is BEG-based. */
531 INTERVAL
532 interval_of (position, object)
533 int position;
534 Lisp_Object object;
536 register INTERVAL i;
537 int beg, end;
539 if (NILP (object))
540 XSETBUFFER (object, current_buffer);
541 else if (EQ (object, Qt))
542 return NULL_INTERVAL;
544 CHECK_STRING_OR_BUFFER (object);
546 if (BUFFERP (object))
548 register struct buffer *b = XBUFFER (object);
550 beg = BUF_BEGV (b);
551 end = BUF_ZV (b);
552 i = BUF_INTERVALS (b);
554 else
556 register struct Lisp_String *s = XSTRING (object);
558 beg = 0;
559 end = s->size;
560 i = s->intervals;
563 if (!(beg <= position && position <= end))
564 args_out_of_range (make_number (position), make_number (position));
565 if (beg == end || NULL_INTERVAL_P (i))
566 return NULL_INTERVAL;
568 return find_interval (i, position);
571 DEFUN ("text-properties-at", Ftext_properties_at,
572 Stext_properties_at, 1, 2, 0,
573 doc: /* Return the list of properties of the character at POSITION in OBJECT.
574 OBJECT is the string or buffer to look for the properties in;
575 nil means the current buffer.
576 If POSITION is at the end of OBJECT, the value is nil. */)
577 (position, object)
578 Lisp_Object position, object;
580 register INTERVAL i;
582 if (NILP (object))
583 XSETBUFFER (object, current_buffer);
585 i = validate_interval_range (object, &position, &position, soft);
586 if (NULL_INTERVAL_P (i))
587 return Qnil;
588 /* If POSITION is at the end of the interval,
589 it means it's the end of OBJECT.
590 There are no properties at the very end,
591 since no character follows. */
592 if (XINT (position) == LENGTH (i) + i->position)
593 return Qnil;
595 return i->plist;
598 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
599 doc: /* Return the value of POSITION's property PROP, in OBJECT.
600 OBJECT is optional and defaults to the current buffer.
601 If POSITION is at the end of OBJECT, the value is nil. */)
602 (position, prop, object)
603 Lisp_Object position, object;
604 Lisp_Object prop;
606 return textget (Ftext_properties_at (position, object), prop);
609 /* Return the value of POSITION's property PROP, in OBJECT.
610 OBJECT is optional and defaults to the current buffer.
611 If OVERLAY is non-0, then in the case that the returned property is from
612 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
613 returned in *OVERLAY.
614 If POSITION is at the end of OBJECT, the value is nil.
615 If OBJECT is a buffer, then overlay properties are considered as well as
616 text properties.
617 If OBJECT is a window, then that window's buffer is used, but
618 window-specific overlays are considered only if they are associated
619 with OBJECT. */
620 Lisp_Object
621 get_char_property_and_overlay (position, prop, object, overlay)
622 Lisp_Object position, object;
623 register Lisp_Object prop;
624 Lisp_Object *overlay;
626 struct window *w = 0;
628 CHECK_NUMBER_COERCE_MARKER (position);
630 if (NILP (object))
631 XSETBUFFER (object, current_buffer);
633 if (WINDOWP (object))
635 w = XWINDOW (object);
636 object = w->buffer;
638 if (BUFFERP (object))
640 int posn = XINT (position);
641 int noverlays;
642 Lisp_Object *overlay_vec, tem;
643 int next_overlay;
644 int len;
645 struct buffer *obuf = current_buffer;
647 set_buffer_temp (XBUFFER (object));
649 /* First try with room for 40 overlays. */
650 len = 40;
651 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
653 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
654 &next_overlay, NULL, 0);
656 /* If there are more than 40,
657 make enough space for all, and try again. */
658 if (noverlays > len)
660 len = noverlays;
661 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
662 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
663 &next_overlay, NULL, 0);
665 noverlays = sort_overlays (overlay_vec, noverlays, w);
667 set_buffer_temp (obuf);
669 /* Now check the overlays in order of decreasing priority. */
670 while (--noverlays >= 0)
672 tem = Foverlay_get (overlay_vec[noverlays], prop);
673 if (!NILP (tem))
675 if (overlay)
676 /* Return the overlay we got the property from. */
677 *overlay = overlay_vec[noverlays];
678 return tem;
683 if (overlay)
684 /* Indicate that the return value is not from an overlay. */
685 *overlay = Qnil;
687 /* Not a buffer, or no appropriate overlay, so fall through to the
688 simpler case. */
689 return Fget_text_property (position, prop, object);
692 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
693 doc: /* Return the value of POSITION's property PROP, in OBJECT.
694 Both overlay properties and text properties are checked.
695 OBJECT is optional and defaults to the current buffer.
696 If POSITION is at the end of OBJECT, the value is nil.
697 If OBJECT is a buffer, then overlay properties are considered as well as
698 text properties.
699 If OBJECT is a window, then that window's buffer is used, but window-specific
700 overlays are considered only if they are associated with OBJECT. */)
701 (position, prop, object)
702 Lisp_Object position, object;
703 register Lisp_Object prop;
705 return get_char_property_and_overlay (position, prop, object, 0);
708 DEFUN ("next-char-property-change", Fnext_char_property_change,
709 Snext_char_property_change, 1, 2, 0,
710 doc: /* Return the position of next text property or overlay change.
711 This scans characters forward from POSITION till it finds a change in
712 some text property, or the beginning or end of an overlay, and returns
713 the position of that.
714 If none is found, the function returns (point-max).
716 If the optional third argument LIMIT is non-nil, don't search
717 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
718 (position, limit)
719 Lisp_Object position, limit;
721 Lisp_Object temp;
723 temp = Fnext_overlay_change (position);
724 if (! NILP (limit))
726 CHECK_NUMBER (limit);
727 if (XINT (limit) < XINT (temp))
728 temp = limit;
730 return Fnext_property_change (position, Qnil, temp);
733 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
734 Sprevious_char_property_change, 1, 2, 0,
735 doc: /* Return the position of previous text property or overlay change.
736 Scans characters backward from POSITION till it finds a change in some
737 text property, or the beginning or end of an overlay, and returns the
738 position of that.
739 If none is found, the function returns (point-max).
741 If the optional third argument LIMIT is non-nil, don't search
742 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
743 (position, limit)
744 Lisp_Object position, limit;
746 Lisp_Object temp;
748 temp = Fprevious_overlay_change (position);
749 if (! NILP (limit))
751 CHECK_NUMBER (limit);
752 if (XINT (limit) > XINT (temp))
753 temp = limit;
755 return Fprevious_property_change (position, Qnil, temp);
759 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
760 Snext_single_char_property_change, 2, 4, 0,
761 doc: /* Return the position of next text property or overlay change for a specific property.
762 Scans characters forward from POSITION till it finds
763 a change in the PROP property, then returns the position of the change.
764 The optional third argument OBJECT is the string or buffer to scan.
765 The property values are compared with `eq'.
766 If the property is constant all the way to the end of OBJECT, return the
767 last valid position in OBJECT.
768 If the optional fourth argument LIMIT is non-nil, don't search
769 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
770 (position, prop, object, limit)
771 Lisp_Object prop, position, object, limit;
773 if (STRINGP (object))
775 position = Fnext_single_property_change (position, prop, object, limit);
776 if (NILP (position))
778 if (NILP (limit))
779 position = make_number (XSTRING (object)->size);
780 else
781 position = limit;
784 else
786 Lisp_Object initial_value, value;
787 int count = specpdl_ptr - specpdl;
789 if (! NILP (object))
790 CHECK_BUFFER (object);
792 if (BUFFERP (object) && current_buffer != XBUFFER (object))
794 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
795 Fset_buffer (object);
798 initial_value = Fget_char_property (position, prop, object);
800 if (NILP (limit))
801 XSETFASTINT (limit, BUF_ZV (current_buffer));
802 else
803 CHECK_NUMBER_COERCE_MARKER (limit);
805 for (;;)
807 position = Fnext_char_property_change (position, limit);
808 if (XFASTINT (position) >= XFASTINT (limit)) {
809 position = limit;
810 break;
813 value = Fget_char_property (position, prop, object);
814 if (!EQ (value, initial_value))
815 break;
818 unbind_to (count, Qnil);
821 return position;
824 DEFUN ("previous-single-char-property-change",
825 Fprevious_single_char_property_change,
826 Sprevious_single_char_property_change, 2, 4, 0,
827 doc: /* Return the position of previous text property or overlay change for a specific property.
828 Scans characters backward from POSITION till it finds
829 a change in the PROP property, then returns the position of the change.
830 The optional third argument OBJECT is the string or buffer to scan.
831 The property values are compared with `eq'.
832 If the property is constant all the way to the start of OBJECT, return the
833 first valid position in OBJECT.
834 If the optional fourth argument LIMIT is non-nil, don't search
835 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
836 (position, prop, object, limit)
837 Lisp_Object prop, position, object, limit;
839 if (STRINGP (object))
841 position = Fprevious_single_property_change (position, prop, object, limit);
842 if (NILP (position))
844 if (NILP (limit))
845 position = make_number (XSTRING (object)->size);
846 else
847 position = limit;
850 else
852 int count = specpdl_ptr - specpdl;
854 if (! NILP (object))
855 CHECK_BUFFER (object);
857 if (BUFFERP (object) && current_buffer != XBUFFER (object))
859 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
860 Fset_buffer (object);
863 if (NILP (limit))
864 XSETFASTINT (limit, BUF_BEGV (current_buffer));
865 else
866 CHECK_NUMBER_COERCE_MARKER (limit);
868 if (XFASTINT (position) <= XFASTINT (limit))
869 position = limit;
870 else
872 Lisp_Object initial_value =
873 Fget_char_property (make_number (XFASTINT (position) - 1),
874 prop, object);
876 for (;;)
878 position = Fprevious_char_property_change (position, limit);
880 if (XFASTINT (position) <= XFASTINT (limit))
882 position = limit;
883 break;
885 else
887 Lisp_Object value =
888 Fget_char_property (make_number (XFASTINT (position) - 1),
889 prop, object);
891 if (!EQ (value, initial_value))
892 break;
897 unbind_to (count, Qnil);
900 return position;
903 DEFUN ("next-property-change", Fnext_property_change,
904 Snext_property_change, 1, 3, 0,
905 doc: /* Return the position of next property change.
906 Scans characters forward from POSITION in OBJECT till it finds
907 a change in some text property, then returns the position of the change.
908 The optional second argument OBJECT is the string or buffer to scan.
909 Return nil if the property is constant all the way to the end of OBJECT.
910 If the value is non-nil, it is a position greater than POSITION, never equal.
912 If the optional third argument LIMIT is non-nil, don't search
913 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
914 (position, object, limit)
915 Lisp_Object position, object, limit;
917 register INTERVAL i, next;
919 if (NILP (object))
920 XSETBUFFER (object, current_buffer);
922 if (!NILP (limit) && !EQ (limit, Qt))
923 CHECK_NUMBER_COERCE_MARKER (limit);
925 i = validate_interval_range (object, &position, &position, soft);
927 /* If LIMIT is t, return start of next interval--don't
928 bother checking further intervals. */
929 if (EQ (limit, Qt))
931 if (NULL_INTERVAL_P (i))
932 next = i;
933 else
934 next = next_interval (i);
936 if (NULL_INTERVAL_P (next))
937 XSETFASTINT (position, (STRINGP (object)
938 ? XSTRING (object)->size
939 : BUF_ZV (XBUFFER (object))));
940 else
941 XSETFASTINT (position, next->position);
942 return position;
945 if (NULL_INTERVAL_P (i))
946 return limit;
948 next = next_interval (i);
950 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
951 && (NILP (limit) || next->position < XFASTINT (limit)))
952 next = next_interval (next);
954 if (NULL_INTERVAL_P (next))
955 return limit;
956 if (NILP (limit))
957 XSETFASTINT (limit, (STRINGP (object)
958 ? XSTRING (object)->size
959 : BUF_ZV (XBUFFER (object))));
960 if (!(next->position < XFASTINT (limit)))
961 return limit;
963 XSETFASTINT (position, next->position);
964 return position;
967 /* Return 1 if there's a change in some property between BEG and END. */
970 property_change_between_p (beg, end)
971 int beg, end;
973 register INTERVAL i, next;
974 Lisp_Object object, pos;
976 XSETBUFFER (object, current_buffer);
977 XSETFASTINT (pos, beg);
979 i = validate_interval_range (object, &pos, &pos, soft);
980 if (NULL_INTERVAL_P (i))
981 return 0;
983 next = next_interval (i);
984 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
986 next = next_interval (next);
987 if (NULL_INTERVAL_P (next))
988 return 0;
989 if (next->position >= end)
990 return 0;
993 if (NULL_INTERVAL_P (next))
994 return 0;
996 return 1;
999 DEFUN ("next-single-property-change", Fnext_single_property_change,
1000 Snext_single_property_change, 2, 4, 0,
1001 doc: /* Return the position of next property change for a specific property.
1002 Scans characters forward from POSITION till it finds
1003 a change in the PROP property, then returns the position of the change.
1004 The optional third argument OBJECT is the string or buffer to scan.
1005 The property values are compared with `eq'.
1006 Return nil if the property is constant all the way to the end of OBJECT.
1007 If the value is non-nil, it is a position greater than POSITION, never equal.
1009 If the optional fourth argument LIMIT is non-nil, don't search
1010 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1011 (position, prop, object, limit)
1012 Lisp_Object position, prop, object, limit;
1014 register INTERVAL i, next;
1015 register Lisp_Object here_val;
1017 if (NILP (object))
1018 XSETBUFFER (object, current_buffer);
1020 if (!NILP (limit))
1021 CHECK_NUMBER_COERCE_MARKER (limit);
1023 i = validate_interval_range (object, &position, &position, soft);
1024 if (NULL_INTERVAL_P (i))
1025 return limit;
1027 here_val = textget (i->plist, prop);
1028 next = next_interval (i);
1029 while (! NULL_INTERVAL_P (next)
1030 && EQ (here_val, textget (next->plist, prop))
1031 && (NILP (limit) || next->position < XFASTINT (limit)))
1032 next = next_interval (next);
1034 if (NULL_INTERVAL_P (next))
1035 return limit;
1036 if (NILP (limit))
1037 XSETFASTINT (limit, (STRINGP (object)
1038 ? XSTRING (object)->size
1039 : BUF_ZV (XBUFFER (object))));
1040 if (!(next->position < XFASTINT (limit)))
1041 return limit;
1043 return make_number (next->position);
1046 DEFUN ("previous-property-change", Fprevious_property_change,
1047 Sprevious_property_change, 1, 3, 0,
1048 doc: /* Return the position of previous property change.
1049 Scans characters backwards from POSITION in OBJECT till it finds
1050 a change in some text property, then returns the position of the change.
1051 The optional second argument OBJECT is the string or buffer to scan.
1052 Return nil if the property is constant all the way to the start of OBJECT.
1053 If the value is non-nil, it is a position less than POSITION, never equal.
1055 If the optional third argument LIMIT is non-nil, don't search
1056 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1057 (position, object, limit)
1058 Lisp_Object position, object, limit;
1060 register INTERVAL i, previous;
1062 if (NILP (object))
1063 XSETBUFFER (object, current_buffer);
1065 if (!NILP (limit))
1066 CHECK_NUMBER_COERCE_MARKER (limit);
1068 i = validate_interval_range (object, &position, &position, soft);
1069 if (NULL_INTERVAL_P (i))
1070 return limit;
1072 /* Start with the interval containing the char before point. */
1073 if (i->position == XFASTINT (position))
1074 i = previous_interval (i);
1076 previous = previous_interval (i);
1077 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1078 && (NILP (limit)
1079 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1080 previous = previous_interval (previous);
1081 if (NULL_INTERVAL_P (previous))
1082 return limit;
1083 if (NILP (limit))
1084 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1085 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1086 return limit;
1088 return make_number (previous->position + LENGTH (previous));
1091 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1092 Sprevious_single_property_change, 2, 4, 0,
1093 doc: /* Return the position of previous property change for a specific property.
1094 Scans characters backward from POSITION till it finds
1095 a change in the PROP property, then returns the position of the change.
1096 The optional third argument OBJECT is the string or buffer to scan.
1097 The property values are compared with `eq'.
1098 Return nil if the property is constant all the way to the start of OBJECT.
1099 If the value is non-nil, it is a position less than POSITION, never equal.
1101 If the optional fourth argument LIMIT is non-nil, don't search
1102 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1103 (position, prop, object, limit)
1104 Lisp_Object position, prop, object, limit;
1106 register INTERVAL i, previous;
1107 register Lisp_Object here_val;
1109 if (NILP (object))
1110 XSETBUFFER (object, current_buffer);
1112 if (!NILP (limit))
1113 CHECK_NUMBER_COERCE_MARKER (limit);
1115 i = validate_interval_range (object, &position, &position, soft);
1117 /* Start with the interval containing the char before point. */
1118 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1119 i = previous_interval (i);
1121 if (NULL_INTERVAL_P (i))
1122 return limit;
1124 here_val = textget (i->plist, prop);
1125 previous = previous_interval (i);
1126 while (!NULL_INTERVAL_P (previous)
1127 && EQ (here_val, textget (previous->plist, prop))
1128 && (NILP (limit)
1129 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1130 previous = previous_interval (previous);
1131 if (NULL_INTERVAL_P (previous))
1132 return limit;
1133 if (NILP (limit))
1134 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1135 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1136 return limit;
1138 return make_number (previous->position + LENGTH (previous));
1141 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1143 DEFUN ("add-text-properties", Fadd_text_properties,
1144 Sadd_text_properties, 3, 4, 0,
1145 doc: /* Add properties to the text from START to END.
1146 The third argument PROPERTIES is a property list
1147 specifying the property values to add.
1148 The optional fourth argument, OBJECT,
1149 is the string or buffer containing the text.
1150 Return t if any property value actually changed, nil otherwise. */)
1151 (start, end, properties, object)
1152 Lisp_Object start, end, properties, object;
1154 register INTERVAL i, unchanged;
1155 register int s, len, modified = 0;
1156 struct gcpro gcpro1;
1158 properties = validate_plist (properties);
1159 if (NILP (properties))
1160 return Qnil;
1162 if (NILP (object))
1163 XSETBUFFER (object, current_buffer);
1165 i = validate_interval_range (object, &start, &end, hard);
1166 if (NULL_INTERVAL_P (i))
1167 return Qnil;
1169 s = XINT (start);
1170 len = XINT (end) - s;
1172 /* No need to protect OBJECT, because we GC only if it's a buffer,
1173 and live buffers are always protected. */
1174 GCPRO1 (properties);
1176 /* If we're not starting on an interval boundary, we have to
1177 split this interval. */
1178 if (i->position != s)
1180 /* If this interval already has the properties, we can
1181 skip it. */
1182 if (interval_has_all_properties (properties, i))
1184 int got = (LENGTH (i) - (s - i->position));
1185 if (got >= len)
1186 RETURN_UNGCPRO (Qnil);
1187 len -= got;
1188 i = next_interval (i);
1190 else
1192 unchanged = i;
1193 i = split_interval_right (unchanged, s - unchanged->position);
1194 copy_properties (unchanged, i);
1198 if (BUFFERP (object))
1199 modify_region (XBUFFER (object), XINT (start), XINT (end));
1201 /* We are at the beginning of interval I, with LEN chars to scan. */
1202 for (;;)
1204 if (i == 0)
1205 abort ();
1207 if (LENGTH (i) >= len)
1209 /* We can UNGCPRO safely here, because there will be just
1210 one more chance to gc, in the next call to add_properties,
1211 and after that we will not need PROPERTIES or OBJECT again. */
1212 UNGCPRO;
1214 if (interval_has_all_properties (properties, i))
1216 if (BUFFERP (object))
1217 signal_after_change (XINT (start), XINT (end) - XINT (start),
1218 XINT (end) - XINT (start));
1220 return modified ? Qt : Qnil;
1223 if (LENGTH (i) == len)
1225 add_properties (properties, i, object);
1226 if (BUFFERP (object))
1227 signal_after_change (XINT (start), XINT (end) - XINT (start),
1228 XINT (end) - XINT (start));
1229 return Qt;
1232 /* i doesn't have the properties, and goes past the change limit */
1233 unchanged = i;
1234 i = split_interval_left (unchanged, len);
1235 copy_properties (unchanged, i);
1236 add_properties (properties, i, object);
1237 if (BUFFERP (object))
1238 signal_after_change (XINT (start), XINT (end) - XINT (start),
1239 XINT (end) - XINT (start));
1240 return Qt;
1243 len -= LENGTH (i);
1244 modified += add_properties (properties, i, object);
1245 i = next_interval (i);
1249 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1251 DEFUN ("put-text-property", Fput_text_property,
1252 Sput_text_property, 4, 5, 0,
1253 doc: /* Set one property of the text from START to END.
1254 The third and fourth arguments PROPERTY and VALUE
1255 specify the property to add.
1256 The optional fifth argument, OBJECT,
1257 is the string or buffer containing the text. */)
1258 (start, end, property, value, object)
1259 Lisp_Object start, end, property, value, object;
1261 Fadd_text_properties (start, end,
1262 Fcons (property, Fcons (value, Qnil)),
1263 object);
1264 return Qnil;
1267 DEFUN ("set-text-properties", Fset_text_properties,
1268 Sset_text_properties, 3, 4, 0,
1269 doc: /* Completely replace properties of text from START to END.
1270 The third argument PROPERTIES is the new property list.
1271 The optional fourth argument, OBJECT,
1272 is the string or buffer containing the text.
1273 If OBJECT is omitted or nil, it defaults to the current buffer.
1274 If PROPERTIES is nil, the effect is to remove all properties from
1275 the designated part of OBJECT. */)
1276 (start, end, properties, object)
1277 Lisp_Object start, end, properties, object;
1279 return set_text_properties (start, end, properties, object, Qt);
1283 /* Replace properties of text from START to END with new list of
1284 properties PROPERTIES. OBJECT is the buffer or string containing
1285 the text. OBJECT nil means use the current buffer.
1286 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1287 is non-nil if properties were replaced; it is nil if there weren't
1288 any properties to replace. */
1290 Lisp_Object
1291 set_text_properties (start, end, properties, object, signal_after_change_p)
1292 Lisp_Object start, end, properties, object, signal_after_change_p;
1294 register INTERVAL i;
1295 Lisp_Object ostart, oend;
1297 ostart = start;
1298 oend = end;
1300 properties = validate_plist (properties);
1302 if (NILP (object))
1303 XSETBUFFER (object, current_buffer);
1305 /* If we want no properties for a whole string,
1306 get rid of its intervals. */
1307 if (NILP (properties) && STRINGP (object)
1308 && XFASTINT (start) == 0
1309 && XFASTINT (end) == XSTRING (object)->size)
1311 if (! XSTRING (object)->intervals)
1312 return Qt;
1314 XSTRING (object)->intervals = 0;
1315 return Qt;
1318 i = validate_interval_range (object, &start, &end, soft);
1320 if (NULL_INTERVAL_P (i))
1322 /* If buffer has no properties, and we want none, return now. */
1323 if (NILP (properties))
1324 return Qnil;
1326 /* Restore the original START and END values
1327 because validate_interval_range increments them for strings. */
1328 start = ostart;
1329 end = oend;
1331 i = validate_interval_range (object, &start, &end, hard);
1332 /* This can return if start == end. */
1333 if (NULL_INTERVAL_P (i))
1334 return Qnil;
1337 if (BUFFERP (object))
1338 modify_region (XBUFFER (object), XINT (start), XINT (end));
1340 set_text_properties_1 (start, end, properties, object, i);
1342 if (BUFFERP (object) && !NILP (signal_after_change_p))
1343 signal_after_change (XINT (start), XINT (end) - XINT (start),
1344 XINT (end) - XINT (start));
1345 return Qt;
1348 /* Replace properties of text from START to END with new list of
1349 properties PROPERTIES. BUFFER is the buffer containing
1350 the text. This does not obey any hooks.
1351 You can provide the interval that START is located in as I,
1352 or pass NULL for I and this function will find it.
1353 START and END can be in any order. */
1355 void
1356 set_text_properties_1 (start, end, properties, buffer, i)
1357 Lisp_Object start, end, properties, buffer;
1358 INTERVAL i;
1360 register INTERVAL prev_changed = NULL_INTERVAL;
1361 register int s, len;
1362 INTERVAL unchanged;
1364 s = XINT (start);
1365 len = XINT (end) - s;
1366 if (len == 0)
1367 return;
1368 if (len < 0)
1370 s = s + len;
1371 len = - len;
1374 if (i == 0)
1375 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1377 if (i->position != s)
1379 unchanged = i;
1380 i = split_interval_right (unchanged, s - unchanged->position);
1382 if (LENGTH (i) > len)
1384 copy_properties (unchanged, i);
1385 i = split_interval_left (i, len);
1386 set_properties (properties, i, buffer);
1387 return;
1390 set_properties (properties, i, buffer);
1392 if (LENGTH (i) == len)
1393 return;
1395 prev_changed = i;
1396 len -= LENGTH (i);
1397 i = next_interval (i);
1400 /* We are starting at the beginning of an interval, I */
1401 while (len > 0)
1403 if (i == 0)
1404 abort ();
1406 if (LENGTH (i) >= len)
1408 if (LENGTH (i) > len)
1409 i = split_interval_left (i, len);
1411 /* We have to call set_properties even if we are going to
1412 merge the intervals, so as to make the undo records
1413 and cause redisplay to happen. */
1414 set_properties (properties, i, buffer);
1415 if (!NULL_INTERVAL_P (prev_changed))
1416 merge_interval_left (i);
1417 return;
1420 len -= LENGTH (i);
1422 /* We have to call set_properties even if we are going to
1423 merge the intervals, so as to make the undo records
1424 and cause redisplay to happen. */
1425 set_properties (properties, i, buffer);
1426 if (NULL_INTERVAL_P (prev_changed))
1427 prev_changed = i;
1428 else
1429 prev_changed = i = merge_interval_left (i);
1431 i = next_interval (i);
1435 DEFUN ("remove-text-properties", Fremove_text_properties,
1436 Sremove_text_properties, 3, 4, 0,
1437 doc: /* Remove some properties from text from START to END.
1438 The third argument PROPERTIES is a property list
1439 whose property names specify the properties to remove.
1440 \(The values stored in PROPERTIES are ignored.)
1441 The optional fourth argument, OBJECT,
1442 is the string or buffer containing the text.
1443 Return t if any property was actually removed, nil otherwise. */)
1444 (start, end, properties, object)
1445 Lisp_Object start, end, properties, object;
1447 register INTERVAL i, unchanged;
1448 register int s, len, modified = 0;
1450 if (NILP (object))
1451 XSETBUFFER (object, current_buffer);
1453 i = validate_interval_range (object, &start, &end, soft);
1454 if (NULL_INTERVAL_P (i))
1455 return Qnil;
1457 s = XINT (start);
1458 len = XINT (end) - s;
1460 if (i->position != s)
1462 /* No properties on this first interval -- return if
1463 it covers the entire region. */
1464 if (! interval_has_some_properties (properties, i))
1466 int got = (LENGTH (i) - (s - i->position));
1467 if (got >= len)
1468 return Qnil;
1469 len -= got;
1470 i = next_interval (i);
1472 /* Split away the beginning of this interval; what we don't
1473 want to modify. */
1474 else
1476 unchanged = i;
1477 i = split_interval_right (unchanged, s - unchanged->position);
1478 copy_properties (unchanged, i);
1482 if (BUFFERP (object))
1483 modify_region (XBUFFER (object), XINT (start), XINT (end));
1485 /* We are at the beginning of an interval, with len to scan */
1486 for (;;)
1488 if (i == 0)
1489 abort ();
1491 if (LENGTH (i) >= len)
1493 if (! interval_has_some_properties (properties, i))
1494 return modified ? Qt : Qnil;
1496 if (LENGTH (i) == len)
1498 remove_properties (properties, Qnil, i, object);
1499 if (BUFFERP (object))
1500 signal_after_change (XINT (start), XINT (end) - XINT (start),
1501 XINT (end) - XINT (start));
1502 return Qt;
1505 /* i has the properties, and goes past the change limit */
1506 unchanged = i;
1507 i = split_interval_left (i, len);
1508 copy_properties (unchanged, i);
1509 remove_properties (properties, Qnil, i, object);
1510 if (BUFFERP (object))
1511 signal_after_change (XINT (start), XINT (end) - XINT (start),
1512 XINT (end) - XINT (start));
1513 return Qt;
1516 len -= LENGTH (i);
1517 modified += remove_properties (properties, Qnil, i, object);
1518 i = next_interval (i);
1522 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1523 Sremove_list_of_text_properties, 3, 4, 0,
1524 doc: /* Remove some properties from text from START to END.
1525 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1526 The optional fourth argument, OBJECT,
1527 is the string or buffer containing the text, defaulting to the current buffer.
1528 Return t if any property was actually removed, nil otherwise. */)
1529 (start, end, list_of_properties, object)
1530 Lisp_Object start, end, list_of_properties, object;
1532 register INTERVAL i, unchanged;
1533 register int s, len, modified = 0;
1534 Lisp_Object properties;
1535 properties = list_of_properties;
1537 if (NILP (object))
1538 XSETBUFFER (object, current_buffer);
1540 i = validate_interval_range (object, &start, &end, soft);
1541 if (NULL_INTERVAL_P (i))
1542 return Qnil;
1544 s = XINT (start);
1545 len = XINT (end) - s;
1547 if (i->position != s)
1549 /* No properties on this first interval -- return if
1550 it covers the entire region. */
1551 if (! interval_has_some_properties_list (properties, i))
1553 int got = (LENGTH (i) - (s - i->position));
1554 if (got >= len)
1555 return Qnil;
1556 len -= got;
1557 i = next_interval (i);
1559 /* Split away the beginning of this interval; what we don't
1560 want to modify. */
1561 else
1563 unchanged = i;
1564 i = split_interval_right (unchanged, s - unchanged->position);
1565 copy_properties (unchanged, i);
1569 if (BUFFERP (object))
1570 modify_region (XBUFFER (object), XINT (start), XINT (end));
1572 /* We are at the beginning of an interval, with len to scan */
1573 for (;;)
1575 if (i == 0)
1576 abort ();
1578 if (LENGTH (i) >= len)
1580 if (! interval_has_some_properties_list (properties, i))
1581 return modified ? Qt : Qnil;
1583 if (LENGTH (i) == len)
1585 remove_properties (Qnil, properties, i, object);
1586 if (BUFFERP (object))
1587 signal_after_change (XINT (start), XINT (end) - XINT (start),
1588 XINT (end) - XINT (start));
1589 return Qt;
1592 /* i has the properties, and goes past the change limit */
1593 unchanged = i;
1594 i = split_interval_left (i, len);
1595 copy_properties (unchanged, i);
1596 remove_properties (Qnil, properties, i, object);
1597 if (BUFFERP (object))
1598 signal_after_change (XINT (start), XINT (end) - XINT (start),
1599 XINT (end) - XINT (start));
1600 return Qt;
1603 len -= LENGTH (i);
1604 modified += remove_properties (Qnil, properties, i, object);
1605 i = next_interval (i);
1609 DEFUN ("text-property-any", Ftext_property_any,
1610 Stext_property_any, 4, 5, 0,
1611 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1612 If so, return the position of the first character whose property PROPERTY
1613 is `eq' to VALUE. Otherwise return nil.
1614 The optional fifth argument, OBJECT, is the string or buffer
1615 containing the text. */)
1616 (start, end, property, value, object)
1617 Lisp_Object start, end, property, value, object;
1619 register INTERVAL i;
1620 register int e, pos;
1622 if (NILP (object))
1623 XSETBUFFER (object, current_buffer);
1624 i = validate_interval_range (object, &start, &end, soft);
1625 if (NULL_INTERVAL_P (i))
1626 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1627 e = XINT (end);
1629 while (! NULL_INTERVAL_P (i))
1631 if (i->position >= e)
1632 break;
1633 if (EQ (textget (i->plist, property), value))
1635 pos = i->position;
1636 if (pos < XINT (start))
1637 pos = XINT (start);
1638 return make_number (pos);
1640 i = next_interval (i);
1642 return Qnil;
1645 DEFUN ("text-property-not-all", Ftext_property_not_all,
1646 Stext_property_not_all, 4, 5, 0,
1647 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1648 If so, return the position of the first character whose property PROPERTY
1649 is not `eq' to VALUE. Otherwise, return nil.
1650 The optional fifth argument, OBJECT, is the string or buffer
1651 containing the text. */)
1652 (start, end, property, value, object)
1653 Lisp_Object start, end, property, value, object;
1655 register INTERVAL i;
1656 register int s, e;
1658 if (NILP (object))
1659 XSETBUFFER (object, current_buffer);
1660 i = validate_interval_range (object, &start, &end, soft);
1661 if (NULL_INTERVAL_P (i))
1662 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1663 s = XINT (start);
1664 e = XINT (end);
1666 while (! NULL_INTERVAL_P (i))
1668 if (i->position >= e)
1669 break;
1670 if (! EQ (textget (i->plist, property), value))
1672 if (i->position > s)
1673 s = i->position;
1674 return make_number (s);
1676 i = next_interval (i);
1678 return Qnil;
1682 /* Return the direction from which the text-property PROP would be
1683 inherited by any new text inserted at POS: 1 if it would be
1684 inherited from the char after POS, -1 if it would be inherited from
1685 the char before POS, and 0 if from neither. */
1688 text_property_stickiness (prop, pos)
1689 Lisp_Object prop;
1690 Lisp_Object pos;
1692 Lisp_Object prev_pos, front_sticky;
1693 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1695 if (XINT (pos) > BEGV)
1696 /* Consider previous character. */
1698 Lisp_Object rear_non_sticky;
1700 prev_pos = make_number (XINT (pos) - 1);
1701 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1703 if (!NILP (CONSP (rear_non_sticky)
1704 ? Fmemq (prop, rear_non_sticky)
1705 : rear_non_sticky))
1706 /* PROP is rear-non-sticky. */
1707 is_rear_sticky = 0;
1710 /* Consider following character. */
1711 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1713 if (EQ (front_sticky, Qt)
1714 || (CONSP (front_sticky)
1715 && !NILP (Fmemq (prop, front_sticky))))
1716 /* PROP is inherited from after. */
1717 is_front_sticky = 1;
1719 /* Simple cases, where the properties are consistent. */
1720 if (is_rear_sticky && !is_front_sticky)
1721 return -1;
1722 else if (!is_rear_sticky && is_front_sticky)
1723 return 1;
1724 else if (!is_rear_sticky && !is_front_sticky)
1725 return 0;
1727 /* The stickiness properties are inconsistent, so we have to
1728 disambiguate. Basically, rear-sticky wins, _except_ if the
1729 property that would be inherited has a value of nil, in which case
1730 front-sticky wins. */
1731 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1732 return 1;
1733 else
1734 return -1;
1738 /* I don't think this is the right interface to export; how often do you
1739 want to do something like this, other than when you're copying objects
1740 around?
1742 I think it would be better to have a pair of functions, one which
1743 returns the text properties of a region as a list of ranges and
1744 plists, and another which applies such a list to another object. */
1746 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1747 SRC and DEST may each refer to strings or buffers.
1748 Optional sixth argument PROP causes only that property to be copied.
1749 Properties are copied to DEST as if by `add-text-properties'.
1750 Return t if any property value actually changed, nil otherwise. */
1752 /* Note this can GC when DEST is a buffer. */
1754 Lisp_Object
1755 copy_text_properties (start, end, src, pos, dest, prop)
1756 Lisp_Object start, end, src, pos, dest, prop;
1758 INTERVAL i;
1759 Lisp_Object res;
1760 Lisp_Object stuff;
1761 Lisp_Object plist;
1762 int s, e, e2, p, len, modified = 0;
1763 struct gcpro gcpro1, gcpro2;
1765 i = validate_interval_range (src, &start, &end, soft);
1766 if (NULL_INTERVAL_P (i))
1767 return Qnil;
1769 CHECK_NUMBER_COERCE_MARKER (pos);
1771 Lisp_Object dest_start, dest_end;
1773 dest_start = pos;
1774 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1775 /* Apply this to a copy of pos; it will try to increment its arguments,
1776 which we don't want. */
1777 validate_interval_range (dest, &dest_start, &dest_end, soft);
1780 s = XINT (start);
1781 e = XINT (end);
1782 p = XINT (pos);
1784 stuff = Qnil;
1786 while (s < e)
1788 e2 = i->position + LENGTH (i);
1789 if (e2 > e)
1790 e2 = e;
1791 len = e2 - s;
1793 plist = i->plist;
1794 if (! NILP (prop))
1795 while (! NILP (plist))
1797 if (EQ (Fcar (plist), prop))
1799 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1800 break;
1802 plist = Fcdr (Fcdr (plist));
1804 if (! NILP (plist))
1806 /* Must defer modifications to the interval tree in case src
1807 and dest refer to the same string or buffer. */
1808 stuff = Fcons (Fcons (make_number (p),
1809 Fcons (make_number (p + len),
1810 Fcons (plist, Qnil))),
1811 stuff);
1814 i = next_interval (i);
1815 if (NULL_INTERVAL_P (i))
1816 break;
1818 p += len;
1819 s = i->position;
1822 GCPRO2 (stuff, dest);
1824 while (! NILP (stuff))
1826 res = Fcar (stuff);
1827 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1828 Fcar (Fcdr (Fcdr (res))), dest);
1829 if (! NILP (res))
1830 modified++;
1831 stuff = Fcdr (stuff);
1834 UNGCPRO;
1836 return modified ? Qt : Qnil;
1840 /* Return a list representing the text properties of OBJECT between
1841 START and END. if PROP is non-nil, report only on that property.
1842 Each result list element has the form (S E PLIST), where S and E
1843 are positions in OBJECT and PLIST is a property list containing the
1844 text properties of OBJECT between S and E. Value is nil if OBJECT
1845 doesn't contain text properties between START and END. */
1847 Lisp_Object
1848 text_property_list (object, start, end, prop)
1849 Lisp_Object object, start, end, prop;
1851 struct interval *i;
1852 Lisp_Object result;
1854 result = Qnil;
1856 i = validate_interval_range (object, &start, &end, soft);
1857 if (!NULL_INTERVAL_P (i))
1859 int s = XINT (start);
1860 int e = XINT (end);
1862 while (s < e)
1864 int interval_end, len;
1865 Lisp_Object plist;
1867 interval_end = i->position + LENGTH (i);
1868 if (interval_end > e)
1869 interval_end = e;
1870 len = interval_end - s;
1872 plist = i->plist;
1874 if (!NILP (prop))
1875 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1876 if (EQ (Fcar (plist), prop))
1878 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1879 break;
1882 if (!NILP (plist))
1883 result = Fcons (Fcons (make_number (s),
1884 Fcons (make_number (s + len),
1885 Fcons (plist, Qnil))),
1886 result);
1888 i = next_interval (i);
1889 if (NULL_INTERVAL_P (i))
1890 break;
1891 s = i->position;
1895 return result;
1899 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1900 (START END PLIST), where START and END are positions and PLIST is a
1901 property list containing the text properties to add. Adjust START
1902 and END positions by DELTA before adding properties. Value is
1903 non-zero if OBJECT was modified. */
1906 add_text_properties_from_list (object, list, delta)
1907 Lisp_Object object, list, delta;
1909 struct gcpro gcpro1, gcpro2;
1910 int modified_p = 0;
1912 GCPRO2 (list, object);
1914 for (; CONSP (list); list = XCDR (list))
1916 Lisp_Object item, start, end, plist, tem;
1918 item = XCAR (list);
1919 start = make_number (XINT (XCAR (item)) + XINT (delta));
1920 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1921 plist = XCAR (XCDR (XCDR (item)));
1923 tem = Fadd_text_properties (start, end, plist, object);
1924 if (!NILP (tem))
1925 modified_p = 1;
1928 UNGCPRO;
1929 return modified_p;
1934 /* Modify end-points of ranges in LIST destructively. LIST is a list
1935 as returned from text_property_list. Change end-points equal to
1936 OLD_END to NEW_END. */
1938 void
1939 extend_property_ranges (list, old_end, new_end)
1940 Lisp_Object list, old_end, new_end;
1942 for (; CONSP (list); list = XCDR (list))
1944 Lisp_Object item, end;
1946 item = XCAR (list);
1947 end = XCAR (XCDR (item));
1949 if (EQ (end, old_end))
1950 XSETCAR (XCDR (item), new_end);
1956 /* Call the modification hook functions in LIST, each with START and END. */
1958 static void
1959 call_mod_hooks (list, start, end)
1960 Lisp_Object list, start, end;
1962 struct gcpro gcpro1;
1963 GCPRO1 (list);
1964 while (!NILP (list))
1966 call2 (Fcar (list), start, end);
1967 list = Fcdr (list);
1969 UNGCPRO;
1972 /* Check for read-only intervals between character positions START ... END,
1973 in BUF, and signal an error if we find one.
1975 Then check for any modification hooks in the range.
1976 Create a list of all these hooks in lexicographic order,
1977 eliminating consecutive extra copies of the same hook. Then call
1978 those hooks in order, with START and END - 1 as arguments. */
1980 void
1981 verify_interval_modification (buf, start, end)
1982 struct buffer *buf;
1983 int start, end;
1985 register INTERVAL intervals = BUF_INTERVALS (buf);
1986 register INTERVAL i;
1987 Lisp_Object hooks;
1988 register Lisp_Object prev_mod_hooks;
1989 Lisp_Object mod_hooks;
1990 struct gcpro gcpro1;
1992 hooks = Qnil;
1993 prev_mod_hooks = Qnil;
1994 mod_hooks = Qnil;
1996 interval_insert_behind_hooks = Qnil;
1997 interval_insert_in_front_hooks = Qnil;
1999 if (NULL_INTERVAL_P (intervals))
2000 return;
2002 if (start > end)
2004 int temp = start;
2005 start = end;
2006 end = temp;
2009 /* For an insert operation, check the two chars around the position. */
2010 if (start == end)
2012 INTERVAL prev = NULL;
2013 Lisp_Object before, after;
2015 /* Set I to the interval containing the char after START,
2016 and PREV to the interval containing the char before START.
2017 Either one may be null. They may be equal. */
2018 i = find_interval (intervals, start);
2020 if (start == BUF_BEGV (buf))
2021 prev = 0;
2022 else if (i->position == start)
2023 prev = previous_interval (i);
2024 else if (i->position < start)
2025 prev = i;
2026 if (start == BUF_ZV (buf))
2027 i = 0;
2029 /* If Vinhibit_read_only is set and is not a list, we can
2030 skip the read_only checks. */
2031 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2033 /* If I and PREV differ we need to check for the read-only
2034 property together with its stickiness. If either I or
2035 PREV are 0, this check is all we need.
2036 We have to take special care, since read-only may be
2037 indirectly defined via the category property. */
2038 if (i != prev)
2040 if (! NULL_INTERVAL_P (i))
2042 after = textget (i->plist, Qread_only);
2044 /* If interval I is read-only and read-only is
2045 front-sticky, inhibit insertion.
2046 Check for read-only as well as category. */
2047 if (! NILP (after)
2048 && NILP (Fmemq (after, Vinhibit_read_only)))
2050 Lisp_Object tem;
2052 tem = textget (i->plist, Qfront_sticky);
2053 if (TMEM (Qread_only, tem)
2054 || (NILP (Fplist_get (i->plist, Qread_only))
2055 && TMEM (Qcategory, tem)))
2056 text_read_only ();
2060 if (! NULL_INTERVAL_P (prev))
2062 before = textget (prev->plist, Qread_only);
2064 /* If interval PREV is read-only and read-only isn't
2065 rear-nonsticky, inhibit insertion.
2066 Check for read-only as well as category. */
2067 if (! NILP (before)
2068 && NILP (Fmemq (before, Vinhibit_read_only)))
2070 Lisp_Object tem;
2072 tem = textget (prev->plist, Qrear_nonsticky);
2073 if (! TMEM (Qread_only, tem)
2074 && (! NILP (Fplist_get (prev->plist,Qread_only))
2075 || ! TMEM (Qcategory, tem)))
2076 text_read_only ();
2080 else if (! NULL_INTERVAL_P (i))
2082 after = textget (i->plist, Qread_only);
2084 /* If interval I is read-only and read-only is
2085 front-sticky, inhibit insertion.
2086 Check for read-only as well as category. */
2087 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2089 Lisp_Object tem;
2091 tem = textget (i->plist, Qfront_sticky);
2092 if (TMEM (Qread_only, tem)
2093 || (NILP (Fplist_get (i->plist, Qread_only))
2094 && TMEM (Qcategory, tem)))
2095 text_read_only ();
2097 tem = textget (prev->plist, Qrear_nonsticky);
2098 if (! TMEM (Qread_only, tem)
2099 && (! NILP (Fplist_get (prev->plist, Qread_only))
2100 || ! TMEM (Qcategory, tem)))
2101 text_read_only ();
2106 /* Run both insert hooks (just once if they're the same). */
2107 if (!NULL_INTERVAL_P (prev))
2108 interval_insert_behind_hooks
2109 = textget (prev->plist, Qinsert_behind_hooks);
2110 if (!NULL_INTERVAL_P (i))
2111 interval_insert_in_front_hooks
2112 = textget (i->plist, Qinsert_in_front_hooks);
2114 else
2116 /* Loop over intervals on or next to START...END,
2117 collecting their hooks. */
2119 i = find_interval (intervals, start);
2122 if (! INTERVAL_WRITABLE_P (i))
2123 text_read_only ();
2125 if (!inhibit_modification_hooks)
2127 mod_hooks = textget (i->plist, Qmodification_hooks);
2128 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2130 hooks = Fcons (mod_hooks, hooks);
2131 prev_mod_hooks = mod_hooks;
2135 i = next_interval (i);
2137 /* Keep going thru the interval containing the char before END. */
2138 while (! NULL_INTERVAL_P (i) && i->position < end);
2140 if (!inhibit_modification_hooks)
2142 GCPRO1 (hooks);
2143 hooks = Fnreverse (hooks);
2144 while (! EQ (hooks, Qnil))
2146 call_mod_hooks (Fcar (hooks), make_number (start),
2147 make_number (end));
2148 hooks = Fcdr (hooks);
2150 UNGCPRO;
2155 /* Run the interval hooks for an insertion on character range START ... END.
2156 verify_interval_modification chose which hooks to run;
2157 this function is called after the insertion happens
2158 so it can indicate the range of inserted text. */
2160 void
2161 report_interval_modification (start, end)
2162 Lisp_Object start, end;
2164 if (! NILP (interval_insert_behind_hooks))
2165 call_mod_hooks (interval_insert_behind_hooks, start, end);
2166 if (! NILP (interval_insert_in_front_hooks)
2167 && ! EQ (interval_insert_in_front_hooks,
2168 interval_insert_behind_hooks))
2169 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2172 void
2173 syms_of_textprop ()
2175 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2176 doc: /* Property-list used as default values.
2177 The value of a property in this list is seen as the value for every
2178 character that does not have its own value for that property. */);
2179 Vdefault_text_properties = Qnil;
2181 DEFVAR_LISP ("char-property-alias-alist", &Vchar_property_alias_alist,
2182 doc: /* Alist of alternative properties for properties without a value.
2183 Each element should look like (PROPERTY ALTERNATIVE1 ALTERNATIVE2...).
2184 If a piece of text has no direct value for a particular property, then
2185 this alist is consulted. If that property appears in the alist, then
2186 the first non-nil value from the associated alternative properties is
2187 returned. */);
2188 Vchar_property_alias_alist = Qnil;
2190 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2191 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2192 This also inhibits the use of the `intangible' text property. */);
2193 Vinhibit_point_motion_hooks = Qnil;
2195 DEFVAR_LISP ("text-property-default-nonsticky",
2196 &Vtext_property_default_nonsticky,
2197 doc: /* Alist of properties vs the corresponding non-stickinesses.
2198 Each element has the form (PROPERTY . NONSTICKINESS).
2200 If a character in a buffer has PROPERTY, new text inserted adjacent to
2201 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2202 inherits it if NONSTICKINESS is nil. The front-sticky and
2203 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2204 Vtext_property_default_nonsticky = Qnil;
2206 staticpro (&interval_insert_behind_hooks);
2207 staticpro (&interval_insert_in_front_hooks);
2208 interval_insert_behind_hooks = Qnil;
2209 interval_insert_in_front_hooks = Qnil;
2212 /* Common attributes one might give text */
2214 staticpro (&Qforeground);
2215 Qforeground = intern ("foreground");
2216 staticpro (&Qbackground);
2217 Qbackground = intern ("background");
2218 staticpro (&Qfont);
2219 Qfont = intern ("font");
2220 staticpro (&Qstipple);
2221 Qstipple = intern ("stipple");
2222 staticpro (&Qunderline);
2223 Qunderline = intern ("underline");
2224 staticpro (&Qread_only);
2225 Qread_only = intern ("read-only");
2226 staticpro (&Qinvisible);
2227 Qinvisible = intern ("invisible");
2228 staticpro (&Qintangible);
2229 Qintangible = intern ("intangible");
2230 staticpro (&Qcategory);
2231 Qcategory = intern ("category");
2232 staticpro (&Qlocal_map);
2233 Qlocal_map = intern ("local-map");
2234 staticpro (&Qfront_sticky);
2235 Qfront_sticky = intern ("front-sticky");
2236 staticpro (&Qrear_nonsticky);
2237 Qrear_nonsticky = intern ("rear-nonsticky");
2238 staticpro (&Qmouse_face);
2239 Qmouse_face = intern ("mouse-face");
2241 /* Properties that text might use to specify certain actions */
2243 staticpro (&Qmouse_left);
2244 Qmouse_left = intern ("mouse-left");
2245 staticpro (&Qmouse_entered);
2246 Qmouse_entered = intern ("mouse-entered");
2247 staticpro (&Qpoint_left);
2248 Qpoint_left = intern ("point-left");
2249 staticpro (&Qpoint_entered);
2250 Qpoint_entered = intern ("point-entered");
2252 defsubr (&Stext_properties_at);
2253 defsubr (&Sget_text_property);
2254 defsubr (&Sget_char_property);
2255 defsubr (&Snext_char_property_change);
2256 defsubr (&Sprevious_char_property_change);
2257 defsubr (&Snext_single_char_property_change);
2258 defsubr (&Sprevious_single_char_property_change);
2259 defsubr (&Snext_property_change);
2260 defsubr (&Snext_single_property_change);
2261 defsubr (&Sprevious_property_change);
2262 defsubr (&Sprevious_single_property_change);
2263 defsubr (&Sadd_text_properties);
2264 defsubr (&Sput_text_property);
2265 defsubr (&Sset_text_properties);
2266 defsubr (&Sremove_text_properties);
2267 defsubr (&Sremove_list_of_text_properties);
2268 defsubr (&Stext_property_any);
2269 defsubr (&Stext_property_not_all);
2270 /* defsubr (&Serase_text_properties); */
2271 /* defsubr (&Scopy_text_properties); */