lwlib/Imakefile is removed.
[emacs.git] / src / textprop.c
blobc85b4dcfd04dbd4c94618059f861e0880740848c
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 Vtext_property_default_nonsticky;
75 /* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77 Lisp_Object interval_insert_behind_hooks;
78 Lisp_Object interval_insert_in_front_hooks;
81 /* Signal a `text-read-only' error. This function makes it easier
82 to capture that error in GDB by putting a breakpoint on it. */
84 static void
85 text_read_only ()
87 Fsignal (Qtext_read_only, Qnil);
92 /* Extract the interval at the position pointed to by BEGIN from
93 OBJECT, a string or buffer. Additionally, check that the positions
94 pointed to by BEGIN and END are within the bounds of OBJECT, and
95 reverse them if *BEGIN is greater than *END. The objects pointed
96 to by BEGIN and END may be integers or markers; if the latter, they
97 are coerced to integers.
99 When OBJECT is a string, we increment *BEGIN and *END
100 to make them origin-one.
102 Note that buffer points don't correspond to interval indices.
103 For example, point-max is 1 greater than the index of the last
104 character. This difference is handled in the caller, which uses
105 the validated points to determine a length, and operates on that.
106 Exceptions are Ftext_properties_at, Fnext_property_change, and
107 Fprevious_property_change which call this function with BEGIN == END.
108 Handle this case specially.
110 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
111 create an interval tree for OBJECT if one doesn't exist, provided
112 the object actually contains text. In the current design, if there
113 is no text, there can be no text properties. */
115 #define soft 0
116 #define hard 1
118 INTERVAL
119 validate_interval_range (object, begin, end, force)
120 Lisp_Object object, *begin, *end;
121 int force;
123 register INTERVAL i;
124 int searchpos;
126 CHECK_STRING_OR_BUFFER (object);
127 CHECK_NUMBER_COERCE_MARKER (*begin);
128 CHECK_NUMBER_COERCE_MARKER (*end);
130 /* If we are asked for a point, but from a subr which operates
131 on a range, then return nothing. */
132 if (EQ (*begin, *end) && begin != end)
133 return NULL_INTERVAL;
135 if (XINT (*begin) > XINT (*end))
137 Lisp_Object n;
138 n = *begin;
139 *begin = *end;
140 *end = n;
143 if (BUFFERP (object))
145 register struct buffer *b = XBUFFER (object);
147 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
148 && XINT (*end) <= BUF_ZV (b)))
149 args_out_of_range (*begin, *end);
150 i = BUF_INTERVALS (b);
152 /* If there's no text, there are no properties. */
153 if (BUF_BEGV (b) == BUF_ZV (b))
154 return NULL_INTERVAL;
156 searchpos = XINT (*begin);
158 else
160 register struct Lisp_String *s = XSTRING (object);
162 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
163 && XINT (*end) <= s->size))
164 args_out_of_range (*begin, *end);
165 XSETFASTINT (*begin, XFASTINT (*begin));
166 if (begin != end)
167 XSETFASTINT (*end, XFASTINT (*end));
168 i = s->intervals;
170 if (s->size == 0)
171 return NULL_INTERVAL;
173 searchpos = XINT (*begin);
176 if (NULL_INTERVAL_P (i))
177 return (force ? create_root_interval (object) : i);
179 return find_interval (i, searchpos);
182 /* Validate LIST as a property list. If LIST is not a list, then
183 make one consisting of (LIST nil). Otherwise, verify that LIST
184 is even numbered and thus suitable as a plist. */
186 static Lisp_Object
187 validate_plist (list)
188 Lisp_Object list;
190 if (NILP (list))
191 return Qnil;
193 if (CONSP (list))
195 register int i;
196 register Lisp_Object tail;
197 for (i = 0, tail = list; !NILP (tail); i++)
199 tail = Fcdr (tail);
200 QUIT;
202 if (i & 1)
203 error ("Odd length text property list");
204 return list;
207 return Fcons (list, Fcons (Qnil, Qnil));
210 /* Return nonzero if interval I has all the properties,
211 with the same values, of list PLIST. */
213 static int
214 interval_has_all_properties (plist, i)
215 Lisp_Object plist;
216 INTERVAL i;
218 register Lisp_Object tail1, tail2, sym1;
219 register int found;
221 /* Go through each element of PLIST. */
222 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
224 sym1 = Fcar (tail1);
225 found = 0;
227 /* Go through I's plist, looking for sym1 */
228 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
229 if (EQ (sym1, Fcar (tail2)))
231 /* Found the same property on both lists. If the
232 values are unequal, return zero. */
233 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
234 return 0;
236 /* Property has same value on both lists; go to next one. */
237 found = 1;
238 break;
241 if (! found)
242 return 0;
245 return 1;
248 /* Return nonzero if the plist of interval I has any of the
249 properties of PLIST, regardless of their values. */
251 static INLINE int
252 interval_has_some_properties (plist, i)
253 Lisp_Object plist;
254 INTERVAL i;
256 register Lisp_Object tail1, tail2, sym;
258 /* Go through each element of PLIST. */
259 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
261 sym = Fcar (tail1);
263 /* Go through i's plist, looking for tail1 */
264 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
265 if (EQ (sym, Fcar (tail2)))
266 return 1;
269 return 0;
272 /* Return nonzero if the plist of interval I has any of the
273 property names in LIST, regardless of their values. */
275 static INLINE int
276 interval_has_some_properties_list (list, i)
277 Lisp_Object list;
278 INTERVAL i;
280 register Lisp_Object tail1, tail2, sym;
282 /* Go through each element of LIST. */
283 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
285 sym = Fcar (tail1);
287 /* Go through i's plist, looking for tail1 */
288 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
289 if (EQ (sym, XCAR (tail2)))
290 return 1;
293 return 0;
296 /* Changing the plists of individual intervals. */
298 /* Return the value of PROP in property-list PLIST, or Qunbound if it
299 has none. */
300 static Lisp_Object
301 property_value (plist, prop)
302 Lisp_Object plist, prop;
304 Lisp_Object value;
306 while (PLIST_ELT_P (plist, value))
307 if (EQ (XCAR (plist), prop))
308 return XCAR (value);
309 else
310 plist = XCDR (value);
312 return Qunbound;
315 /* Set the properties of INTERVAL to PROPERTIES,
316 and record undo info for the previous values.
317 OBJECT is the string or buffer that INTERVAL belongs to. */
319 static void
320 set_properties (properties, interval, object)
321 Lisp_Object properties, object;
322 INTERVAL interval;
324 Lisp_Object sym, value;
326 if (BUFFERP (object))
328 /* For each property in the old plist which is missing from PROPERTIES,
329 or has a different value in PROPERTIES, make an undo record. */
330 for (sym = interval->plist;
331 PLIST_ELT_P (sym, value);
332 sym = XCDR (value))
333 if (! EQ (property_value (properties, XCAR (sym)),
334 XCAR (value)))
336 record_property_change (interval->position, LENGTH (interval),
337 XCAR (sym), XCAR (value),
338 object);
341 /* For each new property that has no value at all in the old plist,
342 make an undo record binding it to nil, so it will be removed. */
343 for (sym = properties;
344 PLIST_ELT_P (sym, value);
345 sym = XCDR (value))
346 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
348 record_property_change (interval->position, LENGTH (interval),
349 XCAR (sym), Qnil,
350 object);
354 /* Store new properties. */
355 interval->plist = Fcopy_sequence (properties);
358 /* Add the properties of PLIST to the interval I, or set
359 the value of I's property to the value of the property on PLIST
360 if they are different.
362 OBJECT should be the string or buffer the interval is in.
364 Return nonzero if this changes I (i.e., if any members of PLIST
365 are actually added to I's plist) */
367 static int
368 add_properties (plist, i, object)
369 Lisp_Object plist;
370 INTERVAL i;
371 Lisp_Object object;
373 Lisp_Object tail1, tail2, sym1, val1;
374 register int changed = 0;
375 register int found;
376 struct gcpro gcpro1, gcpro2, gcpro3;
378 tail1 = plist;
379 sym1 = Qnil;
380 val1 = Qnil;
381 /* No need to protect OBJECT, because we can GC only in the case
382 where it is a buffer, and live buffers are always protected.
383 I and its plist are also protected, via OBJECT. */
384 GCPRO3 (tail1, sym1, val1);
386 /* Go through each element of PLIST. */
387 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
389 sym1 = Fcar (tail1);
390 val1 = Fcar (Fcdr (tail1));
391 found = 0;
393 /* Go through I's plist, looking for sym1 */
394 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
395 if (EQ (sym1, Fcar (tail2)))
397 /* No need to gcpro, because tail2 protects this
398 and it must be a cons cell (we get an error otherwise). */
399 register Lisp_Object this_cdr;
401 this_cdr = Fcdr (tail2);
402 /* Found the property. Now check its value. */
403 found = 1;
405 /* The properties have the same value on both lists.
406 Continue to the next property. */
407 if (EQ (val1, Fcar (this_cdr)))
408 break;
410 /* Record this change in the buffer, for undo purposes. */
411 if (BUFFERP (object))
413 record_property_change (i->position, LENGTH (i),
414 sym1, Fcar (this_cdr), object);
417 /* I's property has a different value -- change it */
418 Fsetcar (this_cdr, val1);
419 changed++;
420 break;
423 if (! found)
425 /* Record this change in the buffer, for undo purposes. */
426 if (BUFFERP (object))
428 record_property_change (i->position, LENGTH (i),
429 sym1, Qnil, object);
431 i->plist = Fcons (sym1, Fcons (val1, i->plist));
432 changed++;
436 UNGCPRO;
438 return changed;
441 /* For any members of PLIST, or LIST,
442 which are properties of I, remove them from I's plist.
443 (If PLIST is non-nil, use that, otherwise use LIST.)
444 OBJECT is the string or buffer containing I. */
446 static int
447 remove_properties (plist, list, i, object)
448 Lisp_Object plist, list;
449 INTERVAL i;
450 Lisp_Object object;
452 register Lisp_Object tail1, tail2, sym, current_plist;
453 register int changed = 0;
455 /* Nonzero means tail1 is a plist, otherwise it is a list. */
456 int use_plist;
458 current_plist = i->plist;
460 if (! NILP (plist))
461 tail1 = plist, use_plist = 1;
462 else
463 tail1 = list, use_plist = 0;
465 /* Go through each element of LIST or PLIST. */
466 while (! NILP (tail1))
468 sym = XCAR (tail1);
470 /* First, remove the symbol if it's at the head of the list */
471 while (! NILP (current_plist) && EQ (sym, XCAR (current_plist)))
473 if (BUFFERP (object))
474 record_property_change (i->position, LENGTH (i),
475 sym, XCAR (XCDR (current_plist)),
476 object);
478 current_plist = XCDR (XCDR (current_plist));
479 changed++;
482 /* Go through I's plist, looking for SYM. */
483 tail2 = current_plist;
484 while (! NILP (tail2))
486 register Lisp_Object this;
487 this = XCDR (XCDR (tail2));
488 if (EQ (sym, XCAR (this)))
490 if (BUFFERP (object))
491 record_property_change (i->position, LENGTH (i),
492 sym, XCAR (XCDR (this)), object);
494 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
495 changed++;
497 tail2 = this;
500 /* Advance thru TAIL1 one way or the other. */
501 tail1 = XCDR (tail1);
502 if (use_plist && CONSP (tail1))
503 tail1 = XCDR (tail1);
506 if (changed)
507 i->plist = current_plist;
508 return changed;
511 #if 0
512 /* Remove all properties from interval I. Return non-zero
513 if this changes the interval. */
515 static INLINE int
516 erase_properties (i)
517 INTERVAL i;
519 if (NILP (i->plist))
520 return 0;
522 i->plist = Qnil;
523 return 1;
525 #endif
527 /* Returns the interval of POSITION in OBJECT.
528 POSITION is BEG-based. */
530 INTERVAL
531 interval_of (position, object)
532 int position;
533 Lisp_Object object;
535 register INTERVAL i;
536 int beg, end;
538 if (NILP (object))
539 XSETBUFFER (object, current_buffer);
540 else if (EQ (object, Qt))
541 return NULL_INTERVAL;
543 CHECK_STRING_OR_BUFFER (object);
545 if (BUFFERP (object))
547 register struct buffer *b = XBUFFER (object);
549 beg = BUF_BEGV (b);
550 end = BUF_ZV (b);
551 i = BUF_INTERVALS (b);
553 else
555 register struct Lisp_String *s = XSTRING (object);
557 beg = 0;
558 end = s->size;
559 i = s->intervals;
562 if (!(beg <= position && position <= end))
563 args_out_of_range (make_number (position), make_number (position));
564 if (beg == end || NULL_INTERVAL_P (i))
565 return NULL_INTERVAL;
567 return find_interval (i, position);
570 DEFUN ("text-properties-at", Ftext_properties_at,
571 Stext_properties_at, 1, 2, 0,
572 doc: /* Return the list of properties of the character at POSITION in OBJECT.
573 OBJECT is the string or buffer to look for the properties in;
574 nil means the current buffer.
575 If POSITION is at the end of OBJECT, the value is nil. */)
576 (position, object)
577 Lisp_Object position, object;
579 register INTERVAL i;
581 if (NILP (object))
582 XSETBUFFER (object, current_buffer);
584 i = validate_interval_range (object, &position, &position, soft);
585 if (NULL_INTERVAL_P (i))
586 return Qnil;
587 /* If POSITION is at the end of the interval,
588 it means it's the end of OBJECT.
589 There are no properties at the very end,
590 since no character follows. */
591 if (XINT (position) == LENGTH (i) + i->position)
592 return Qnil;
594 return i->plist;
597 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
598 doc: /* Return the value of POSITION's property PROP, in OBJECT.
599 OBJECT is optional and defaults to the current buffer.
600 If POSITION is at the end of OBJECT, the value is nil. */)
601 (position, prop, object)
602 Lisp_Object position, object;
603 Lisp_Object prop;
605 return textget (Ftext_properties_at (position, object), prop);
608 /* Return the value of POSITION's property PROP, in OBJECT.
609 OBJECT is optional and defaults to the current buffer.
610 If OVERLAY is non-0, then in the case that the returned property is from
611 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
612 returned in *OVERLAY.
613 If POSITION is at the end of OBJECT, the value is nil.
614 If OBJECT is a buffer, then overlay properties are considered as well as
615 text properties.
616 If OBJECT is a window, then that window's buffer is used, but
617 window-specific overlays are considered only if they are associated
618 with OBJECT. */
619 Lisp_Object
620 get_char_property_and_overlay (position, prop, object, overlay)
621 Lisp_Object position, object;
622 register Lisp_Object prop;
623 Lisp_Object *overlay;
625 struct window *w = 0;
627 CHECK_NUMBER_COERCE_MARKER (position);
629 if (NILP (object))
630 XSETBUFFER (object, current_buffer);
632 if (WINDOWP (object))
634 w = XWINDOW (object);
635 object = w->buffer;
637 if (BUFFERP (object))
639 int posn = XINT (position);
640 int noverlays;
641 Lisp_Object *overlay_vec, tem;
642 int next_overlay;
643 int len;
644 struct buffer *obuf = current_buffer;
646 set_buffer_temp (XBUFFER (object));
648 /* First try with room for 40 overlays. */
649 len = 40;
650 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
652 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
653 &next_overlay, NULL, 0);
655 /* If there are more than 40,
656 make enough space for all, and try again. */
657 if (noverlays > len)
659 len = noverlays;
660 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
661 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
662 &next_overlay, NULL, 0);
664 noverlays = sort_overlays (overlay_vec, noverlays, w);
666 set_buffer_temp (obuf);
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays >= 0)
671 tem = Foverlay_get (overlay_vec[noverlays], prop);
672 if (!NILP (tem))
674 if (overlay)
675 /* Return the overlay we got the property from. */
676 *overlay = overlay_vec[noverlays];
677 return tem;
682 if (overlay)
683 /* Indicate that the return value is not from an overlay. */
684 *overlay = Qnil;
686 /* Not a buffer, or no appropriate overlay, so fall through to the
687 simpler case. */
688 return Fget_text_property (position, prop, object);
691 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
692 doc: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
697 text properties.
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (position, prop, object)
701 Lisp_Object position, object;
702 register Lisp_Object prop;
704 return get_char_property_and_overlay (position, prop, object, 0);
707 DEFUN ("next-char-property-change", Fnext_char_property_change,
708 Snext_char_property_change, 1, 2, 0,
709 doc: /* Return the position of next text property or overlay change.
710 This scans characters forward from POSITION till it finds a change in
711 some text property, or the beginning or end of an overlay, and returns
712 the position of that.
713 If none is found, the function returns (point-max).
715 If the optional third argument LIMIT is non-nil, don't search
716 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
717 (position, limit)
718 Lisp_Object position, limit;
720 Lisp_Object temp;
722 temp = Fnext_overlay_change (position);
723 if (! NILP (limit))
725 CHECK_NUMBER (limit);
726 if (XINT (limit) < XINT (temp))
727 temp = limit;
729 return Fnext_property_change (position, Qnil, temp);
732 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
733 Sprevious_char_property_change, 1, 2, 0,
734 doc: /* Return the position of previous text property or overlay change.
735 Scans characters backward from POSITION till it finds a change in some
736 text property, or the beginning or end of an overlay, and returns the
737 position of that.
738 If none is found, the function returns (point-max).
740 If the optional third argument LIMIT is non-nil, don't search
741 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
742 (position, limit)
743 Lisp_Object position, limit;
745 Lisp_Object temp;
747 temp = Fprevious_overlay_change (position);
748 if (! NILP (limit))
750 CHECK_NUMBER (limit);
751 if (XINT (limit) > XINT (temp))
752 temp = limit;
754 return Fprevious_property_change (position, Qnil, temp);
758 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
759 Snext_single_char_property_change, 2, 4, 0,
760 doc: /* Return the position of next text property or overlay change for a specific property.
761 Scans characters forward from POSITION till it finds
762 a change in the PROP property, then returns the position of the change.
763 The optional third argument OBJECT is the string or buffer to scan.
764 The property values are compared with `eq'.
765 If the property is constant all the way to the end of OBJECT, return the
766 last valid position in OBJECT.
767 If the optional fourth argument LIMIT is non-nil, don't search
768 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
769 (position, prop, object, limit)
770 Lisp_Object prop, position, object, limit;
772 if (STRINGP (object))
774 position = Fnext_single_property_change (position, prop, object, limit);
775 if (NILP (position))
777 if (NILP (limit))
778 position = make_number (XSTRING (object)->size);
779 else
780 position = limit;
783 else
785 Lisp_Object initial_value, value;
786 int count = specpdl_ptr - specpdl;
788 if (! NILP (object))
789 CHECK_BUFFER (object);
791 if (BUFFERP (object) && current_buffer != XBUFFER (object))
793 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
794 Fset_buffer (object);
797 initial_value = Fget_char_property (position, prop, object);
799 if (NILP (limit))
800 XSETFASTINT (limit, BUF_ZV (current_buffer));
801 else
802 CHECK_NUMBER_COERCE_MARKER (limit);
804 for (;;)
806 position = Fnext_char_property_change (position, limit);
807 if (XFASTINT (position) >= XFASTINT (limit)) {
808 position = limit;
809 break;
812 value = Fget_char_property (position, prop, object);
813 if (!EQ (value, initial_value))
814 break;
817 unbind_to (count, Qnil);
820 return position;
823 DEFUN ("previous-single-char-property-change",
824 Fprevious_single_char_property_change,
825 Sprevious_single_char_property_change, 2, 4, 0,
826 doc: /* Return the position of previous text property or overlay change for a specific property.
827 Scans characters backward from POSITION till it finds
828 a change in the PROP property, then returns the position of the change.
829 The optional third argument OBJECT is the string or buffer to scan.
830 The property values are compared with `eq'.
831 If the property is constant all the way to the start of OBJECT, return the
832 first valid position in OBJECT.
833 If the optional fourth argument LIMIT is non-nil, don't search
834 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
835 (position, prop, object, limit)
836 Lisp_Object prop, position, object, limit;
838 if (STRINGP (object))
840 position = Fprevious_single_property_change (position, prop, object, limit);
841 if (NILP (position))
843 if (NILP (limit))
844 position = make_number (XSTRING (object)->size);
845 else
846 position = limit;
849 else
851 int count = specpdl_ptr - specpdl;
853 if (! NILP (object))
854 CHECK_BUFFER (object);
856 if (BUFFERP (object) && current_buffer != XBUFFER (object))
858 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
859 Fset_buffer (object);
862 if (NILP (limit))
863 XSETFASTINT (limit, BUF_BEGV (current_buffer));
864 else
865 CHECK_NUMBER_COERCE_MARKER (limit);
867 if (XFASTINT (position) <= XFASTINT (limit))
868 position = limit;
869 else
871 Lisp_Object initial_value =
872 Fget_char_property (make_number (XFASTINT (position) - 1),
873 prop, object);
875 for (;;)
877 position = Fprevious_char_property_change (position, limit);
879 if (XFASTINT (position) <= XFASTINT (limit))
881 position = limit;
882 break;
884 else
886 Lisp_Object value =
887 Fget_char_property (make_number (XFASTINT (position) - 1),
888 prop, object);
890 if (!EQ (value, initial_value))
891 break;
896 unbind_to (count, Qnil);
899 return position;
902 DEFUN ("next-property-change", Fnext_property_change,
903 Snext_property_change, 1, 3, 0,
904 doc: /* Return the position of next property change.
905 Scans characters forward from POSITION in OBJECT till it finds
906 a change in some text property, then returns the position of the change.
907 The optional second argument OBJECT is the string or buffer to scan.
908 Return nil if the property is constant all the way to the end of OBJECT.
909 If the value is non-nil, it is a position greater than POSITION, never equal.
911 If the optional third argument LIMIT is non-nil, don't search
912 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
913 (position, object, limit)
914 Lisp_Object position, object, limit;
916 register INTERVAL i, next;
918 if (NILP (object))
919 XSETBUFFER (object, current_buffer);
921 if (!NILP (limit) && !EQ (limit, Qt))
922 CHECK_NUMBER_COERCE_MARKER (limit);
924 i = validate_interval_range (object, &position, &position, soft);
926 /* If LIMIT is t, return start of next interval--don't
927 bother checking further intervals. */
928 if (EQ (limit, Qt))
930 if (NULL_INTERVAL_P (i))
931 next = i;
932 else
933 next = next_interval (i);
935 if (NULL_INTERVAL_P (next))
936 XSETFASTINT (position, (STRINGP (object)
937 ? XSTRING (object)->size
938 : BUF_ZV (XBUFFER (object))));
939 else
940 XSETFASTINT (position, next->position);
941 return position;
944 if (NULL_INTERVAL_P (i))
945 return limit;
947 next = next_interval (i);
949 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
950 && (NILP (limit) || next->position < XFASTINT (limit)))
951 next = next_interval (next);
953 if (NULL_INTERVAL_P (next))
954 return limit;
955 if (NILP (limit))
956 XSETFASTINT (limit, (STRINGP (object)
957 ? XSTRING (object)->size
958 : BUF_ZV (XBUFFER (object))));
959 if (!(next->position < XFASTINT (limit)))
960 return limit;
962 XSETFASTINT (position, next->position);
963 return position;
966 /* Return 1 if there's a change in some property between BEG and END. */
969 property_change_between_p (beg, end)
970 int beg, end;
972 register INTERVAL i, next;
973 Lisp_Object object, pos;
975 XSETBUFFER (object, current_buffer);
976 XSETFASTINT (pos, beg);
978 i = validate_interval_range (object, &pos, &pos, soft);
979 if (NULL_INTERVAL_P (i))
980 return 0;
982 next = next_interval (i);
983 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
985 next = next_interval (next);
986 if (NULL_INTERVAL_P (next))
987 return 0;
988 if (next->position >= end)
989 return 0;
992 if (NULL_INTERVAL_P (next))
993 return 0;
995 return 1;
998 DEFUN ("next-single-property-change", Fnext_single_property_change,
999 Snext_single_property_change, 2, 4, 0,
1000 doc: /* Return the position of next property change for a specific property.
1001 Scans characters forward from POSITION till it finds
1002 a change in the PROP property, then returns the position of the change.
1003 The optional third argument OBJECT is the string or buffer to scan.
1004 The property values are compared with `eq'.
1005 Return nil if the property is constant all the way to the end of OBJECT.
1006 If the value is non-nil, it is a position greater than POSITION, never equal.
1008 If the optional fourth argument LIMIT is non-nil, don't search
1009 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1010 (position, prop, object, limit)
1011 Lisp_Object position, prop, object, limit;
1013 register INTERVAL i, next;
1014 register Lisp_Object here_val;
1016 if (NILP (object))
1017 XSETBUFFER (object, current_buffer);
1019 if (!NILP (limit))
1020 CHECK_NUMBER_COERCE_MARKER (limit);
1022 i = validate_interval_range (object, &position, &position, soft);
1023 if (NULL_INTERVAL_P (i))
1024 return limit;
1026 here_val = textget (i->plist, prop);
1027 next = next_interval (i);
1028 while (! NULL_INTERVAL_P (next)
1029 && EQ (here_val, textget (next->plist, prop))
1030 && (NILP (limit) || next->position < XFASTINT (limit)))
1031 next = next_interval (next);
1033 if (NULL_INTERVAL_P (next))
1034 return limit;
1035 if (NILP (limit))
1036 XSETFASTINT (limit, (STRINGP (object)
1037 ? XSTRING (object)->size
1038 : BUF_ZV (XBUFFER (object))));
1039 if (!(next->position < XFASTINT (limit)))
1040 return limit;
1042 return make_number (next->position);
1045 DEFUN ("previous-property-change", Fprevious_property_change,
1046 Sprevious_property_change, 1, 3, 0,
1047 doc: /* Return the position of previous property change.
1048 Scans characters backwards from POSITION in OBJECT till it finds
1049 a change in some text property, then returns the position of the change.
1050 The optional second argument OBJECT is the string or buffer to scan.
1051 Return nil if the property is constant all the way to the start of OBJECT.
1052 If the value is non-nil, it is a position less than POSITION, never equal.
1054 If the optional third argument LIMIT is non-nil, don't search
1055 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1056 (position, object, limit)
1057 Lisp_Object position, object, limit;
1059 register INTERVAL i, previous;
1061 if (NILP (object))
1062 XSETBUFFER (object, current_buffer);
1064 if (!NILP (limit))
1065 CHECK_NUMBER_COERCE_MARKER (limit);
1067 i = validate_interval_range (object, &position, &position, soft);
1068 if (NULL_INTERVAL_P (i))
1069 return limit;
1071 /* Start with the interval containing the char before point. */
1072 if (i->position == XFASTINT (position))
1073 i = previous_interval (i);
1075 previous = previous_interval (i);
1076 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1077 && (NILP (limit)
1078 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1079 previous = previous_interval (previous);
1080 if (NULL_INTERVAL_P (previous))
1081 return limit;
1082 if (NILP (limit))
1083 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1084 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1085 return limit;
1087 return make_number (previous->position + LENGTH (previous));
1090 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1091 Sprevious_single_property_change, 2, 4, 0,
1092 doc: /* Return the position of previous property change for a specific property.
1093 Scans characters backward from POSITION till it finds
1094 a change in the PROP property, then returns the position of the change.
1095 The optional third argument OBJECT is the string or buffer to scan.
1096 The property values are compared with `eq'.
1097 Return nil if the property is constant all the way to the start of OBJECT.
1098 If the value is non-nil, it is a position less than POSITION, never equal.
1100 If the optional fourth argument LIMIT is non-nil, don't search
1101 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1102 (position, prop, object, limit)
1103 Lisp_Object position, prop, object, limit;
1105 register INTERVAL i, previous;
1106 register Lisp_Object here_val;
1108 if (NILP (object))
1109 XSETBUFFER (object, current_buffer);
1111 if (!NILP (limit))
1112 CHECK_NUMBER_COERCE_MARKER (limit);
1114 i = validate_interval_range (object, &position, &position, soft);
1116 /* Start with the interval containing the char before point. */
1117 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1118 i = previous_interval (i);
1120 if (NULL_INTERVAL_P (i))
1121 return limit;
1123 here_val = textget (i->plist, prop);
1124 previous = previous_interval (i);
1125 while (!NULL_INTERVAL_P (previous)
1126 && EQ (here_val, textget (previous->plist, prop))
1127 && (NILP (limit)
1128 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1129 previous = previous_interval (previous);
1130 if (NULL_INTERVAL_P (previous))
1131 return limit;
1132 if (NILP (limit))
1133 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1134 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1135 return limit;
1137 return make_number (previous->position + LENGTH (previous));
1140 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1142 DEFUN ("add-text-properties", Fadd_text_properties,
1143 Sadd_text_properties, 3, 4, 0,
1144 doc: /* Add properties to the text from START to END.
1145 The third argument PROPERTIES is a property list
1146 specifying the property values to add.
1147 The optional fourth argument, OBJECT,
1148 is the string or buffer containing the text.
1149 Return t if any property value actually changed, nil otherwise. */)
1150 (start, end, properties, object)
1151 Lisp_Object start, end, properties, object;
1153 register INTERVAL i, unchanged;
1154 register int s, len, modified = 0;
1155 struct gcpro gcpro1;
1157 properties = validate_plist (properties);
1158 if (NILP (properties))
1159 return Qnil;
1161 if (NILP (object))
1162 XSETBUFFER (object, current_buffer);
1164 i = validate_interval_range (object, &start, &end, hard);
1165 if (NULL_INTERVAL_P (i))
1166 return Qnil;
1168 s = XINT (start);
1169 len = XINT (end) - s;
1171 /* No need to protect OBJECT, because we GC only if it's a buffer,
1172 and live buffers are always protected. */
1173 GCPRO1 (properties);
1175 /* If we're not starting on an interval boundary, we have to
1176 split this interval. */
1177 if (i->position != s)
1179 /* If this interval already has the properties, we can
1180 skip it. */
1181 if (interval_has_all_properties (properties, i))
1183 int got = (LENGTH (i) - (s - i->position));
1184 if (got >= len)
1185 RETURN_UNGCPRO (Qnil);
1186 len -= got;
1187 i = next_interval (i);
1189 else
1191 unchanged = i;
1192 i = split_interval_right (unchanged, s - unchanged->position);
1193 copy_properties (unchanged, i);
1197 if (BUFFERP (object))
1198 modify_region (XBUFFER (object), XINT (start), XINT (end));
1200 /* We are at the beginning of interval I, with LEN chars to scan. */
1201 for (;;)
1203 if (i == 0)
1204 abort ();
1206 if (LENGTH (i) >= len)
1208 /* We can UNGCPRO safely here, because there will be just
1209 one more chance to gc, in the next call to add_properties,
1210 and after that we will not need PROPERTIES or OBJECT again. */
1211 UNGCPRO;
1213 if (interval_has_all_properties (properties, i))
1215 if (BUFFERP (object))
1216 signal_after_change (XINT (start), XINT (end) - XINT (start),
1217 XINT (end) - XINT (start));
1219 return modified ? Qt : Qnil;
1222 if (LENGTH (i) == len)
1224 add_properties (properties, i, object);
1225 if (BUFFERP (object))
1226 signal_after_change (XINT (start), XINT (end) - XINT (start),
1227 XINT (end) - XINT (start));
1228 return Qt;
1231 /* i doesn't have the properties, and goes past the change limit */
1232 unchanged = i;
1233 i = split_interval_left (unchanged, len);
1234 copy_properties (unchanged, i);
1235 add_properties (properties, i, object);
1236 if (BUFFERP (object))
1237 signal_after_change (XINT (start), XINT (end) - XINT (start),
1238 XINT (end) - XINT (start));
1239 return Qt;
1242 len -= LENGTH (i);
1243 modified += add_properties (properties, i, object);
1244 i = next_interval (i);
1248 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1250 DEFUN ("put-text-property", Fput_text_property,
1251 Sput_text_property, 4, 5, 0,
1252 doc: /* Set one property of the text from START to END.
1253 The third and fourth arguments PROPERTY and VALUE
1254 specify the property to add.
1255 The optional fifth argument, OBJECT,
1256 is the string or buffer containing the text. */)
1257 (start, end, property, value, object)
1258 Lisp_Object start, end, property, value, object;
1260 Fadd_text_properties (start, end,
1261 Fcons (property, Fcons (value, Qnil)),
1262 object);
1263 return Qnil;
1266 DEFUN ("set-text-properties", Fset_text_properties,
1267 Sset_text_properties, 3, 4, 0,
1268 doc: /* Completely replace properties of text from START to END.
1269 The third argument PROPERTIES is the new property list.
1270 The optional fourth argument, OBJECT,
1271 is the string or buffer containing the text.
1272 If OBJECT is omitted or nil, it defaults to the current buffer.
1273 If PROPERTIES is nil, the effect is to remove all properties from
1274 the designated part of OBJECT. */)
1275 (start, end, properties, object)
1276 Lisp_Object start, end, properties, object;
1278 return set_text_properties (start, end, properties, object, Qt);
1282 /* Replace properties of text from START to END with new list of
1283 properties PROPERTIES. OBJECT is the buffer or string containing
1284 the text. OBJECT nil means use the current buffer.
1285 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1286 is non-nil if properties were replaced; it is nil if there weren't
1287 any properties to replace. */
1289 Lisp_Object
1290 set_text_properties (start, end, properties, object, signal_after_change_p)
1291 Lisp_Object start, end, properties, object, signal_after_change_p;
1293 register INTERVAL i;
1294 Lisp_Object ostart, oend;
1296 ostart = start;
1297 oend = end;
1299 properties = validate_plist (properties);
1301 if (NILP (object))
1302 XSETBUFFER (object, current_buffer);
1304 /* If we want no properties for a whole string,
1305 get rid of its intervals. */
1306 if (NILP (properties) && STRINGP (object)
1307 && XFASTINT (start) == 0
1308 && XFASTINT (end) == XSTRING (object)->size)
1310 if (! XSTRING (object)->intervals)
1311 return Qt;
1313 XSTRING (object)->intervals = 0;
1314 return Qt;
1317 i = validate_interval_range (object, &start, &end, soft);
1319 if (NULL_INTERVAL_P (i))
1321 /* If buffer has no properties, and we want none, return now. */
1322 if (NILP (properties))
1323 return Qnil;
1325 /* Restore the original START and END values
1326 because validate_interval_range increments them for strings. */
1327 start = ostart;
1328 end = oend;
1330 i = validate_interval_range (object, &start, &end, hard);
1331 /* This can return if start == end. */
1332 if (NULL_INTERVAL_P (i))
1333 return Qnil;
1336 if (BUFFERP (object))
1337 modify_region (XBUFFER (object), XINT (start), XINT (end));
1339 set_text_properties_1 (start, end, properties, object, i);
1341 if (BUFFERP (object) && !NILP (signal_after_change_p))
1342 signal_after_change (XINT (start), XINT (end) - XINT (start),
1343 XINT (end) - XINT (start));
1344 return Qt;
1347 /* Replace properties of text from START to END with new list of
1348 properties PROPERTIES. BUFFER is the buffer containing
1349 the text. This does not obey any hooks.
1350 You can provide the interval that START is located in as I,
1351 or pass NULL for I and this function will find it.
1352 START and END can be in any order. */
1354 void
1355 set_text_properties_1 (start, end, properties, buffer, i)
1356 Lisp_Object start, end, properties, buffer;
1357 INTERVAL i;
1359 register INTERVAL prev_changed = NULL_INTERVAL;
1360 register int s, len;
1361 INTERVAL unchanged;
1363 s = XINT (start);
1364 len = XINT (end) - s;
1365 if (len == 0)
1366 return;
1367 if (len < 0)
1369 s = s + len;
1370 len = - len;
1373 if (i == 0)
1374 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1376 if (i->position != s)
1378 unchanged = i;
1379 i = split_interval_right (unchanged, s - unchanged->position);
1381 if (LENGTH (i) > len)
1383 copy_properties (unchanged, i);
1384 i = split_interval_left (i, len);
1385 set_properties (properties, i, buffer);
1386 return;
1389 set_properties (properties, i, buffer);
1391 if (LENGTH (i) == len)
1392 return;
1394 prev_changed = i;
1395 len -= LENGTH (i);
1396 i = next_interval (i);
1399 /* We are starting at the beginning of an interval, I */
1400 while (len > 0)
1402 if (i == 0)
1403 abort ();
1405 if (LENGTH (i) >= len)
1407 if (LENGTH (i) > len)
1408 i = split_interval_left (i, len);
1410 /* We have to call set_properties even if we are going to
1411 merge the intervals, so as to make the undo records
1412 and cause redisplay to happen. */
1413 set_properties (properties, i, buffer);
1414 if (!NULL_INTERVAL_P (prev_changed))
1415 merge_interval_left (i);
1416 return;
1419 len -= LENGTH (i);
1421 /* We have to call set_properties even if we are going to
1422 merge the intervals, so as to make the undo records
1423 and cause redisplay to happen. */
1424 set_properties (properties, i, buffer);
1425 if (NULL_INTERVAL_P (prev_changed))
1426 prev_changed = i;
1427 else
1428 prev_changed = i = merge_interval_left (i);
1430 i = next_interval (i);
1434 DEFUN ("remove-text-properties", Fremove_text_properties,
1435 Sremove_text_properties, 3, 4, 0,
1436 doc: /* Remove some properties from text from START to END.
1437 The third argument PROPERTIES is a property list
1438 whose property names specify the properties to remove.
1439 \(The values stored in PROPERTIES are ignored.)
1440 The optional fourth argument, OBJECT,
1441 is the string or buffer containing the text.
1442 Return t if any property was actually removed, nil otherwise. */)
1443 (start, end, properties, object)
1444 Lisp_Object start, end, properties, object;
1446 register INTERVAL i, unchanged;
1447 register int s, len, modified = 0;
1449 if (NILP (object))
1450 XSETBUFFER (object, current_buffer);
1452 i = validate_interval_range (object, &start, &end, soft);
1453 if (NULL_INTERVAL_P (i))
1454 return Qnil;
1456 s = XINT (start);
1457 len = XINT (end) - s;
1459 if (i->position != s)
1461 /* No properties on this first interval -- return if
1462 it covers the entire region. */
1463 if (! interval_has_some_properties (properties, i))
1465 int got = (LENGTH (i) - (s - i->position));
1466 if (got >= len)
1467 return Qnil;
1468 len -= got;
1469 i = next_interval (i);
1471 /* Split away the beginning of this interval; what we don't
1472 want to modify. */
1473 else
1475 unchanged = i;
1476 i = split_interval_right (unchanged, s - unchanged->position);
1477 copy_properties (unchanged, i);
1481 if (BUFFERP (object))
1482 modify_region (XBUFFER (object), XINT (start), XINT (end));
1484 /* We are at the beginning of an interval, with len to scan */
1485 for (;;)
1487 if (i == 0)
1488 abort ();
1490 if (LENGTH (i) >= len)
1492 if (! interval_has_some_properties (properties, i))
1493 return modified ? Qt : Qnil;
1495 if (LENGTH (i) == len)
1497 remove_properties (properties, Qnil, i, object);
1498 if (BUFFERP (object))
1499 signal_after_change (XINT (start), XINT (end) - XINT (start),
1500 XINT (end) - XINT (start));
1501 return Qt;
1504 /* i has the properties, and goes past the change limit */
1505 unchanged = i;
1506 i = split_interval_left (i, len);
1507 copy_properties (unchanged, i);
1508 remove_properties (properties, Qnil, i, object);
1509 if (BUFFERP (object))
1510 signal_after_change (XINT (start), XINT (end) - XINT (start),
1511 XINT (end) - XINT (start));
1512 return Qt;
1515 len -= LENGTH (i);
1516 modified += remove_properties (properties, Qnil, i, object);
1517 i = next_interval (i);
1521 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1522 Sremove_list_of_text_properties, 3, 4, 0,
1523 doc: /* Remove some properties from text from START to END.
1524 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1525 The optional fourth argument, OBJECT,
1526 is the string or buffer containing the text, defaulting to the current buffer.
1527 Return t if any property was actually removed, nil otherwise. */)
1528 (start, end, list_of_properties, object)
1529 Lisp_Object start, end, list_of_properties, object;
1531 register INTERVAL i, unchanged;
1532 register int s, len, modified = 0;
1533 Lisp_Object properties;
1534 properties = list_of_properties;
1536 if (NILP (object))
1537 XSETBUFFER (object, current_buffer);
1539 i = validate_interval_range (object, &start, &end, soft);
1540 if (NULL_INTERVAL_P (i))
1541 return Qnil;
1543 s = XINT (start);
1544 len = XINT (end) - s;
1546 if (i->position != s)
1548 /* No properties on this first interval -- return if
1549 it covers the entire region. */
1550 if (! interval_has_some_properties_list (properties, i))
1552 int got = (LENGTH (i) - (s - i->position));
1553 if (got >= len)
1554 return Qnil;
1555 len -= got;
1556 i = next_interval (i);
1558 /* Split away the beginning of this interval; what we don't
1559 want to modify. */
1560 else
1562 unchanged = i;
1563 i = split_interval_right (unchanged, s - unchanged->position);
1564 copy_properties (unchanged, i);
1568 if (BUFFERP (object))
1569 modify_region (XBUFFER (object), XINT (start), XINT (end));
1571 /* We are at the beginning of an interval, with len to scan */
1572 for (;;)
1574 if (i == 0)
1575 abort ();
1577 if (LENGTH (i) >= len)
1579 if (! interval_has_some_properties_list (properties, i))
1580 return modified ? Qt : Qnil;
1582 if (LENGTH (i) == len)
1584 remove_properties (Qnil, properties, i, object);
1585 if (BUFFERP (object))
1586 signal_after_change (XINT (start), XINT (end) - XINT (start),
1587 XINT (end) - XINT (start));
1588 return Qt;
1591 /* i has the properties, and goes past the change limit */
1592 unchanged = i;
1593 i = split_interval_left (i, len);
1594 copy_properties (unchanged, i);
1595 remove_properties (Qnil, properties, i, object);
1596 if (BUFFERP (object))
1597 signal_after_change (XINT (start), XINT (end) - XINT (start),
1598 XINT (end) - XINT (start));
1599 return Qt;
1602 len -= LENGTH (i);
1603 modified += remove_properties (Qnil, properties, i, object);
1604 i = next_interval (i);
1608 DEFUN ("text-property-any", Ftext_property_any,
1609 Stext_property_any, 4, 5, 0,
1610 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1611 If so, return the position of the first character whose property PROPERTY
1612 is `eq' to VALUE. Otherwise return nil.
1613 The optional fifth argument, OBJECT, is the string or buffer
1614 containing the text. */)
1615 (start, end, property, value, object)
1616 Lisp_Object start, end, property, value, object;
1618 register INTERVAL i;
1619 register int e, pos;
1621 if (NILP (object))
1622 XSETBUFFER (object, current_buffer);
1623 i = validate_interval_range (object, &start, &end, soft);
1624 if (NULL_INTERVAL_P (i))
1625 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1626 e = XINT (end);
1628 while (! NULL_INTERVAL_P (i))
1630 if (i->position >= e)
1631 break;
1632 if (EQ (textget (i->plist, property), value))
1634 pos = i->position;
1635 if (pos < XINT (start))
1636 pos = XINT (start);
1637 return make_number (pos);
1639 i = next_interval (i);
1641 return Qnil;
1644 DEFUN ("text-property-not-all", Ftext_property_not_all,
1645 Stext_property_not_all, 4, 5, 0,
1646 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1647 If so, return the position of the first character whose property PROPERTY
1648 is not `eq' to VALUE. Otherwise, return nil.
1649 The optional fifth argument, OBJECT, is the string or buffer
1650 containing the text. */)
1651 (start, end, property, value, object)
1652 Lisp_Object start, end, property, value, object;
1654 register INTERVAL i;
1655 register int s, e;
1657 if (NILP (object))
1658 XSETBUFFER (object, current_buffer);
1659 i = validate_interval_range (object, &start, &end, soft);
1660 if (NULL_INTERVAL_P (i))
1661 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1662 s = XINT (start);
1663 e = XINT (end);
1665 while (! NULL_INTERVAL_P (i))
1667 if (i->position >= e)
1668 break;
1669 if (! EQ (textget (i->plist, property), value))
1671 if (i->position > s)
1672 s = i->position;
1673 return make_number (s);
1675 i = next_interval (i);
1677 return Qnil;
1681 /* Return the direction from which the text-property PROP would be
1682 inherited by any new text inserted at POS: 1 if it would be
1683 inherited from the char after POS, -1 if it would be inherited from
1684 the char before POS, and 0 if from neither. */
1687 text_property_stickiness (prop, pos)
1688 Lisp_Object prop;
1689 Lisp_Object pos;
1691 Lisp_Object prev_pos, front_sticky;
1692 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1694 if (XINT (pos) > BEGV)
1695 /* Consider previous character. */
1697 Lisp_Object rear_non_sticky;
1699 prev_pos = make_number (XINT (pos) - 1);
1700 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1702 if (!NILP (CONSP (rear_non_sticky)
1703 ? Fmemq (prop, rear_non_sticky)
1704 : rear_non_sticky))
1705 /* PROP is rear-non-sticky. */
1706 is_rear_sticky = 0;
1709 /* Consider following character. */
1710 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1712 if (EQ (front_sticky, Qt)
1713 || (CONSP (front_sticky)
1714 && !NILP (Fmemq (prop, front_sticky))))
1715 /* PROP is inherited from after. */
1716 is_front_sticky = 1;
1718 /* Simple cases, where the properties are consistent. */
1719 if (is_rear_sticky && !is_front_sticky)
1720 return -1;
1721 else if (!is_rear_sticky && is_front_sticky)
1722 return 1;
1723 else if (!is_rear_sticky && !is_front_sticky)
1724 return 0;
1726 /* The stickiness properties are inconsistent, so we have to
1727 disambiguate. Basically, rear-sticky wins, _except_ if the
1728 property that would be inherited has a value of nil, in which case
1729 front-sticky wins. */
1730 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1731 return 1;
1732 else
1733 return -1;
1737 /* I don't think this is the right interface to export; how often do you
1738 want to do something like this, other than when you're copying objects
1739 around?
1741 I think it would be better to have a pair of functions, one which
1742 returns the text properties of a region as a list of ranges and
1743 plists, and another which applies such a list to another object. */
1745 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1746 SRC and DEST may each refer to strings or buffers.
1747 Optional sixth argument PROP causes only that property to be copied.
1748 Properties are copied to DEST as if by `add-text-properties'.
1749 Return t if any property value actually changed, nil otherwise. */
1751 /* Note this can GC when DEST is a buffer. */
1753 Lisp_Object
1754 copy_text_properties (start, end, src, pos, dest, prop)
1755 Lisp_Object start, end, src, pos, dest, prop;
1757 INTERVAL i;
1758 Lisp_Object res;
1759 Lisp_Object stuff;
1760 Lisp_Object plist;
1761 int s, e, e2, p, len, modified = 0;
1762 struct gcpro gcpro1, gcpro2;
1764 i = validate_interval_range (src, &start, &end, soft);
1765 if (NULL_INTERVAL_P (i))
1766 return Qnil;
1768 CHECK_NUMBER_COERCE_MARKER (pos);
1770 Lisp_Object dest_start, dest_end;
1772 dest_start = pos;
1773 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1774 /* Apply this to a copy of pos; it will try to increment its arguments,
1775 which we don't want. */
1776 validate_interval_range (dest, &dest_start, &dest_end, soft);
1779 s = XINT (start);
1780 e = XINT (end);
1781 p = XINT (pos);
1783 stuff = Qnil;
1785 while (s < e)
1787 e2 = i->position + LENGTH (i);
1788 if (e2 > e)
1789 e2 = e;
1790 len = e2 - s;
1792 plist = i->plist;
1793 if (! NILP (prop))
1794 while (! NILP (plist))
1796 if (EQ (Fcar (plist), prop))
1798 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1799 break;
1801 plist = Fcdr (Fcdr (plist));
1803 if (! NILP (plist))
1805 /* Must defer modifications to the interval tree in case src
1806 and dest refer to the same string or buffer. */
1807 stuff = Fcons (Fcons (make_number (p),
1808 Fcons (make_number (p + len),
1809 Fcons (plist, Qnil))),
1810 stuff);
1813 i = next_interval (i);
1814 if (NULL_INTERVAL_P (i))
1815 break;
1817 p += len;
1818 s = i->position;
1821 GCPRO2 (stuff, dest);
1823 while (! NILP (stuff))
1825 res = Fcar (stuff);
1826 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1827 Fcar (Fcdr (Fcdr (res))), dest);
1828 if (! NILP (res))
1829 modified++;
1830 stuff = Fcdr (stuff);
1833 UNGCPRO;
1835 return modified ? Qt : Qnil;
1839 /* Return a list representing the text properties of OBJECT between
1840 START and END. if PROP is non-nil, report only on that property.
1841 Each result list element has the form (S E PLIST), where S and E
1842 are positions in OBJECT and PLIST is a property list containing the
1843 text properties of OBJECT between S and E. Value is nil if OBJECT
1844 doesn't contain text properties between START and END. */
1846 Lisp_Object
1847 text_property_list (object, start, end, prop)
1848 Lisp_Object object, start, end, prop;
1850 struct interval *i;
1851 Lisp_Object result;
1853 result = Qnil;
1855 i = validate_interval_range (object, &start, &end, soft);
1856 if (!NULL_INTERVAL_P (i))
1858 int s = XINT (start);
1859 int e = XINT (end);
1861 while (s < e)
1863 int interval_end, len;
1864 Lisp_Object plist;
1866 interval_end = i->position + LENGTH (i);
1867 if (interval_end > e)
1868 interval_end = e;
1869 len = interval_end - s;
1871 plist = i->plist;
1873 if (!NILP (prop))
1874 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1875 if (EQ (Fcar (plist), prop))
1877 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1878 break;
1881 if (!NILP (plist))
1882 result = Fcons (Fcons (make_number (s),
1883 Fcons (make_number (s + len),
1884 Fcons (plist, Qnil))),
1885 result);
1887 i = next_interval (i);
1888 if (NULL_INTERVAL_P (i))
1889 break;
1890 s = i->position;
1894 return result;
1898 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1899 (START END PLIST), where START and END are positions and PLIST is a
1900 property list containing the text properties to add. Adjust START
1901 and END positions by DELTA before adding properties. Value is
1902 non-zero if OBJECT was modified. */
1905 add_text_properties_from_list (object, list, delta)
1906 Lisp_Object object, list, delta;
1908 struct gcpro gcpro1, gcpro2;
1909 int modified_p = 0;
1911 GCPRO2 (list, object);
1913 for (; CONSP (list); list = XCDR (list))
1915 Lisp_Object item, start, end, plist, tem;
1917 item = XCAR (list);
1918 start = make_number (XINT (XCAR (item)) + XINT (delta));
1919 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1920 plist = XCAR (XCDR (XCDR (item)));
1922 tem = Fadd_text_properties (start, end, plist, object);
1923 if (!NILP (tem))
1924 modified_p = 1;
1927 UNGCPRO;
1928 return modified_p;
1933 /* Modify end-points of ranges in LIST destructively. LIST is a list
1934 as returned from text_property_list. Change end-points equal to
1935 OLD_END to NEW_END. */
1937 void
1938 extend_property_ranges (list, old_end, new_end)
1939 Lisp_Object list, old_end, new_end;
1941 for (; CONSP (list); list = XCDR (list))
1943 Lisp_Object item, end;
1945 item = XCAR (list);
1946 end = XCAR (XCDR (item));
1948 if (EQ (end, old_end))
1949 XSETCAR (XCDR (item), new_end);
1955 /* Call the modification hook functions in LIST, each with START and END. */
1957 static void
1958 call_mod_hooks (list, start, end)
1959 Lisp_Object list, start, end;
1961 struct gcpro gcpro1;
1962 GCPRO1 (list);
1963 while (!NILP (list))
1965 call2 (Fcar (list), start, end);
1966 list = Fcdr (list);
1968 UNGCPRO;
1971 /* Check for read-only intervals between character positions START ... END,
1972 in BUF, and signal an error if we find one.
1974 Then check for any modification hooks in the range.
1975 Create a list of all these hooks in lexicographic order,
1976 eliminating consecutive extra copies of the same hook. Then call
1977 those hooks in order, with START and END - 1 as arguments. */
1979 void
1980 verify_interval_modification (buf, start, end)
1981 struct buffer *buf;
1982 int start, end;
1984 register INTERVAL intervals = BUF_INTERVALS (buf);
1985 register INTERVAL i;
1986 Lisp_Object hooks;
1987 register Lisp_Object prev_mod_hooks;
1988 Lisp_Object mod_hooks;
1989 struct gcpro gcpro1;
1991 hooks = Qnil;
1992 prev_mod_hooks = Qnil;
1993 mod_hooks = Qnil;
1995 interval_insert_behind_hooks = Qnil;
1996 interval_insert_in_front_hooks = Qnil;
1998 if (NULL_INTERVAL_P (intervals))
1999 return;
2001 if (start > end)
2003 int temp = start;
2004 start = end;
2005 end = temp;
2008 /* For an insert operation, check the two chars around the position. */
2009 if (start == end)
2011 INTERVAL prev = NULL;
2012 Lisp_Object before, after;
2014 /* Set I to the interval containing the char after START,
2015 and PREV to the interval containing the char before START.
2016 Either one may be null. They may be equal. */
2017 i = find_interval (intervals, start);
2019 if (start == BUF_BEGV (buf))
2020 prev = 0;
2021 else if (i->position == start)
2022 prev = previous_interval (i);
2023 else if (i->position < start)
2024 prev = i;
2025 if (start == BUF_ZV (buf))
2026 i = 0;
2028 /* If Vinhibit_read_only is set and is not a list, we can
2029 skip the read_only checks. */
2030 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2032 /* If I and PREV differ we need to check for the read-only
2033 property together with its stickiness. If either I or
2034 PREV are 0, this check is all we need.
2035 We have to take special care, since read-only may be
2036 indirectly defined via the category property. */
2037 if (i != prev)
2039 if (! NULL_INTERVAL_P (i))
2041 after = textget (i->plist, Qread_only);
2043 /* If interval I is read-only and read-only is
2044 front-sticky, inhibit insertion.
2045 Check for read-only as well as category. */
2046 if (! NILP (after)
2047 && NILP (Fmemq (after, Vinhibit_read_only)))
2049 Lisp_Object tem;
2051 tem = textget (i->plist, Qfront_sticky);
2052 if (TMEM (Qread_only, tem)
2053 || (NILP (Fplist_get (i->plist, Qread_only))
2054 && TMEM (Qcategory, tem)))
2055 text_read_only ();
2059 if (! NULL_INTERVAL_P (prev))
2061 before = textget (prev->plist, Qread_only);
2063 /* If interval PREV is read-only and read-only isn't
2064 rear-nonsticky, inhibit insertion.
2065 Check for read-only as well as category. */
2066 if (! NILP (before)
2067 && NILP (Fmemq (before, Vinhibit_read_only)))
2069 Lisp_Object tem;
2071 tem = textget (prev->plist, Qrear_nonsticky);
2072 if (! TMEM (Qread_only, tem)
2073 && (! NILP (Fplist_get (prev->plist,Qread_only))
2074 || ! TMEM (Qcategory, tem)))
2075 text_read_only ();
2079 else if (! NULL_INTERVAL_P (i))
2081 after = textget (i->plist, Qread_only);
2083 /* If interval I is read-only and read-only is
2084 front-sticky, inhibit insertion.
2085 Check for read-only as well as category. */
2086 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2088 Lisp_Object tem;
2090 tem = textget (i->plist, Qfront_sticky);
2091 if (TMEM (Qread_only, tem)
2092 || (NILP (Fplist_get (i->plist, Qread_only))
2093 && TMEM (Qcategory, tem)))
2094 text_read_only ();
2096 tem = textget (prev->plist, Qrear_nonsticky);
2097 if (! TMEM (Qread_only, tem)
2098 && (! NILP (Fplist_get (prev->plist, Qread_only))
2099 || ! TMEM (Qcategory, tem)))
2100 text_read_only ();
2105 /* Run both insert hooks (just once if they're the same). */
2106 if (!NULL_INTERVAL_P (prev))
2107 interval_insert_behind_hooks
2108 = textget (prev->plist, Qinsert_behind_hooks);
2109 if (!NULL_INTERVAL_P (i))
2110 interval_insert_in_front_hooks
2111 = textget (i->plist, Qinsert_in_front_hooks);
2113 else
2115 /* Loop over intervals on or next to START...END,
2116 collecting their hooks. */
2118 i = find_interval (intervals, start);
2121 if (! INTERVAL_WRITABLE_P (i))
2122 text_read_only ();
2124 if (!inhibit_modification_hooks)
2126 mod_hooks = textget (i->plist, Qmodification_hooks);
2127 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2129 hooks = Fcons (mod_hooks, hooks);
2130 prev_mod_hooks = mod_hooks;
2134 i = next_interval (i);
2136 /* Keep going thru the interval containing the char before END. */
2137 while (! NULL_INTERVAL_P (i) && i->position < end);
2139 if (!inhibit_modification_hooks)
2141 GCPRO1 (hooks);
2142 hooks = Fnreverse (hooks);
2143 while (! EQ (hooks, Qnil))
2145 call_mod_hooks (Fcar (hooks), make_number (start),
2146 make_number (end));
2147 hooks = Fcdr (hooks);
2149 UNGCPRO;
2154 /* Run the interval hooks for an insertion on character range START ... END.
2155 verify_interval_modification chose which hooks to run;
2156 this function is called after the insertion happens
2157 so it can indicate the range of inserted text. */
2159 void
2160 report_interval_modification (start, end)
2161 Lisp_Object start, end;
2163 if (! NILP (interval_insert_behind_hooks))
2164 call_mod_hooks (interval_insert_behind_hooks, start, end);
2165 if (! NILP (interval_insert_in_front_hooks)
2166 && ! EQ (interval_insert_in_front_hooks,
2167 interval_insert_behind_hooks))
2168 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2171 void
2172 syms_of_textprop ()
2174 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2175 doc: /* Property-list used as default values.
2176 The value of a property in this list is seen as the value for every
2177 character that does not have its own value for that property. */);
2178 Vdefault_text_properties = Qnil;
2180 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2181 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2182 This also inhibits the use of the `intangible' text property. */);
2183 Vinhibit_point_motion_hooks = Qnil;
2185 DEFVAR_LISP ("text-property-default-nonsticky",
2186 &Vtext_property_default_nonsticky,
2187 doc: /* Alist of properties vs the corresponding non-stickinesses.
2188 Each element has the form (PROPERTY . NONSTICKINESS).
2190 If a character in a buffer has PROPERTY, new text inserted adjacent to
2191 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2192 inherits it if NONSTICKINESS is nil. The front-sticky and
2193 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2194 Vtext_property_default_nonsticky = Qnil;
2196 staticpro (&interval_insert_behind_hooks);
2197 staticpro (&interval_insert_in_front_hooks);
2198 interval_insert_behind_hooks = Qnil;
2199 interval_insert_in_front_hooks = Qnil;
2202 /* Common attributes one might give text */
2204 staticpro (&Qforeground);
2205 Qforeground = intern ("foreground");
2206 staticpro (&Qbackground);
2207 Qbackground = intern ("background");
2208 staticpro (&Qfont);
2209 Qfont = intern ("font");
2210 staticpro (&Qstipple);
2211 Qstipple = intern ("stipple");
2212 staticpro (&Qunderline);
2213 Qunderline = intern ("underline");
2214 staticpro (&Qread_only);
2215 Qread_only = intern ("read-only");
2216 staticpro (&Qinvisible);
2217 Qinvisible = intern ("invisible");
2218 staticpro (&Qintangible);
2219 Qintangible = intern ("intangible");
2220 staticpro (&Qcategory);
2221 Qcategory = intern ("category");
2222 staticpro (&Qlocal_map);
2223 Qlocal_map = intern ("local-map");
2224 staticpro (&Qfront_sticky);
2225 Qfront_sticky = intern ("front-sticky");
2226 staticpro (&Qrear_nonsticky);
2227 Qrear_nonsticky = intern ("rear-nonsticky");
2228 staticpro (&Qmouse_face);
2229 Qmouse_face = intern ("mouse-face");
2231 /* Properties that text might use to specify certain actions */
2233 staticpro (&Qmouse_left);
2234 Qmouse_left = intern ("mouse-left");
2235 staticpro (&Qmouse_entered);
2236 Qmouse_entered = intern ("mouse-entered");
2237 staticpro (&Qpoint_left);
2238 Qpoint_left = intern ("point-left");
2239 staticpro (&Qpoint_entered);
2240 Qpoint_entered = intern ("point-entered");
2242 defsubr (&Stext_properties_at);
2243 defsubr (&Sget_text_property);
2244 defsubr (&Sget_char_property);
2245 defsubr (&Snext_char_property_change);
2246 defsubr (&Sprevious_char_property_change);
2247 defsubr (&Snext_single_char_property_change);
2248 defsubr (&Sprevious_single_char_property_change);
2249 defsubr (&Snext_property_change);
2250 defsubr (&Snext_single_property_change);
2251 defsubr (&Sprevious_property_change);
2252 defsubr (&Sprevious_single_property_change);
2253 defsubr (&Sadd_text_properties);
2254 defsubr (&Sput_text_property);
2255 defsubr (&Sset_text_properties);
2256 defsubr (&Sremove_text_properties);
2257 defsubr (&Sremove_list_of_text_properties);
2258 defsubr (&Stext_property_any);
2259 defsubr (&Stext_property_not_all);
2260 /* defsubr (&Serase_text_properties); */
2261 /* defsubr (&Scopy_text_properties); */