(standard-latex-block-names): Add "math".
[emacs.git] / src / textprop.c
blob03e4b477b357d7264952309616a10d50a8f20025
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 #include <config.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
67 traversing plists. */
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70 Lisp_Object Vinhibit_point_motion_hooks;
71 Lisp_Object Vdefault_text_properties;
72 Lisp_Object Vtext_property_default_nonsticky;
74 /* verify_interval_modification saves insertion hooks here
75 to be run later by report_interval_modification. */
76 Lisp_Object interval_insert_behind_hooks;
77 Lisp_Object interval_insert_in_front_hooks;
79 /* Extract the interval at the position pointed to by BEGIN from
80 OBJECT, a string or buffer. Additionally, check that the positions
81 pointed to by BEGIN and END are within the bounds of OBJECT, and
82 reverse them if *BEGIN is greater than *END. The objects pointed
83 to by BEGIN and END may be integers or markers; if the latter, they
84 are coerced to integers.
86 When OBJECT is a string, we increment *BEGIN and *END
87 to make them origin-one.
89 Note that buffer points don't correspond to interval indices.
90 For example, point-max is 1 greater than the index of the last
91 character. This difference is handled in the caller, which uses
92 the validated points to determine a length, and operates on that.
93 Exceptions are Ftext_properties_at, Fnext_property_change, and
94 Fprevious_property_change which call this function with BEGIN == END.
95 Handle this case specially.
97 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
98 create an interval tree for OBJECT if one doesn't exist, provided
99 the object actually contains text. In the current design, if there
100 is no text, there can be no text properties. */
102 #define soft 0
103 #define hard 1
105 INTERVAL
106 validate_interval_range (object, begin, end, force)
107 Lisp_Object object, *begin, *end;
108 int force;
110 register INTERVAL i;
111 int searchpos;
113 CHECK_STRING_OR_BUFFER (object, 0);
114 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
115 CHECK_NUMBER_COERCE_MARKER (*end, 0);
117 /* If we are asked for a point, but from a subr which operates
118 on a range, then return nothing. */
119 if (EQ (*begin, *end) && begin != end)
120 return NULL_INTERVAL;
122 if (XINT (*begin) > XINT (*end))
124 Lisp_Object n;
125 n = *begin;
126 *begin = *end;
127 *end = n;
130 if (BUFFERP (object))
132 register struct buffer *b = XBUFFER (object);
134 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
135 && XINT (*end) <= BUF_ZV (b)))
136 args_out_of_range (*begin, *end);
137 i = BUF_INTERVALS (b);
139 /* If there's no text, there are no properties. */
140 if (BUF_BEGV (b) == BUF_ZV (b))
141 return NULL_INTERVAL;
143 searchpos = XINT (*begin);
145 else
147 register struct Lisp_String *s = XSTRING (object);
149 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
150 && XINT (*end) <= s->size))
151 args_out_of_range (*begin, *end);
152 XSETFASTINT (*begin, XFASTINT (*begin));
153 if (begin != end)
154 XSETFASTINT (*end, XFASTINT (*end));
155 i = s->intervals;
157 if (s->size == 0)
158 return NULL_INTERVAL;
160 searchpos = XINT (*begin);
163 if (NULL_INTERVAL_P (i))
164 return (force ? create_root_interval (object) : i);
166 return find_interval (i, searchpos);
169 /* Validate LIST as a property list. If LIST is not a list, then
170 make one consisting of (LIST nil). Otherwise, verify that LIST
171 is even numbered and thus suitable as a plist. */
173 static Lisp_Object
174 validate_plist (list)
175 Lisp_Object list;
177 if (NILP (list))
178 return Qnil;
180 if (CONSP (list))
182 register int i;
183 register Lisp_Object tail;
184 for (i = 0, tail = list; !NILP (tail); i++)
186 tail = Fcdr (tail);
187 QUIT;
189 if (i & 1)
190 error ("Odd length text property list");
191 return list;
194 return Fcons (list, Fcons (Qnil, Qnil));
197 /* Return nonzero if interval I has all the properties,
198 with the same values, of list PLIST. */
200 static int
201 interval_has_all_properties (plist, i)
202 Lisp_Object plist;
203 INTERVAL i;
205 register Lisp_Object tail1, tail2, sym1;
206 register int found;
208 /* Go through each element of PLIST. */
209 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
211 sym1 = Fcar (tail1);
212 found = 0;
214 /* Go through I's plist, looking for sym1 */
215 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
216 if (EQ (sym1, Fcar (tail2)))
218 /* Found the same property on both lists. If the
219 values are unequal, return zero. */
220 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
221 return 0;
223 /* Property has same value on both lists; go to next one. */
224 found = 1;
225 break;
228 if (! found)
229 return 0;
232 return 1;
235 /* Return nonzero if the plist of interval I has any of the
236 properties of PLIST, regardless of their values. */
238 static INLINE int
239 interval_has_some_properties (plist, i)
240 Lisp_Object plist;
241 INTERVAL i;
243 register Lisp_Object tail1, tail2, sym;
245 /* Go through each element of PLIST. */
246 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
248 sym = Fcar (tail1);
250 /* Go through i's plist, looking for tail1 */
251 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
252 if (EQ (sym, Fcar (tail2)))
253 return 1;
256 return 0;
259 /* Changing the plists of individual intervals. */
261 /* Return the value of PROP in property-list PLIST, or Qunbound if it
262 has none. */
263 static Lisp_Object
264 property_value (plist, prop)
265 Lisp_Object plist, prop;
267 Lisp_Object value;
269 while (PLIST_ELT_P (plist, value))
270 if (EQ (XCAR (plist), prop))
271 return XCAR (value);
272 else
273 plist = XCDR (value);
275 return Qunbound;
278 /* Set the properties of INTERVAL to PROPERTIES,
279 and record undo info for the previous values.
280 OBJECT is the string or buffer that INTERVAL belongs to. */
282 static void
283 set_properties (properties, interval, object)
284 Lisp_Object properties, object;
285 INTERVAL interval;
287 Lisp_Object sym, value;
289 if (BUFFERP (object))
291 /* For each property in the old plist which is missing from PROPERTIES,
292 or has a different value in PROPERTIES, make an undo record. */
293 for (sym = interval->plist;
294 PLIST_ELT_P (sym, value);
295 sym = XCDR (value))
296 if (! EQ (property_value (properties, XCAR (sym)),
297 XCAR (value)))
299 record_property_change (interval->position, LENGTH (interval),
300 XCAR (sym), XCAR (value),
301 object);
304 /* For each new property that has no value at all in the old plist,
305 make an undo record binding it to nil, so it will be removed. */
306 for (sym = properties;
307 PLIST_ELT_P (sym, value);
308 sym = XCDR (value))
309 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
311 record_property_change (interval->position, LENGTH (interval),
312 XCAR (sym), Qnil,
313 object);
317 /* Store new properties. */
318 interval->plist = Fcopy_sequence (properties);
321 /* Add the properties of PLIST to the interval I, or set
322 the value of I's property to the value of the property on PLIST
323 if they are different.
325 OBJECT should be the string or buffer the interval is in.
327 Return nonzero if this changes I (i.e., if any members of PLIST
328 are actually added to I's plist) */
330 static int
331 add_properties (plist, i, object)
332 Lisp_Object plist;
333 INTERVAL i;
334 Lisp_Object object;
336 Lisp_Object tail1, tail2, sym1, val1;
337 register int changed = 0;
338 register int found;
339 struct gcpro gcpro1, gcpro2, gcpro3;
341 tail1 = plist;
342 sym1 = Qnil;
343 val1 = Qnil;
344 /* No need to protect OBJECT, because we can GC only in the case
345 where it is a buffer, and live buffers are always protected.
346 I and its plist are also protected, via OBJECT. */
347 GCPRO3 (tail1, sym1, val1);
349 /* Go through each element of PLIST. */
350 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
352 sym1 = Fcar (tail1);
353 val1 = Fcar (Fcdr (tail1));
354 found = 0;
356 /* Go through I's plist, looking for sym1 */
357 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
358 if (EQ (sym1, Fcar (tail2)))
360 /* No need to gcpro, because tail2 protects this
361 and it must be a cons cell (we get an error otherwise). */
362 register Lisp_Object this_cdr;
364 this_cdr = Fcdr (tail2);
365 /* Found the property. Now check its value. */
366 found = 1;
368 /* The properties have the same value on both lists.
369 Continue to the next property. */
370 if (EQ (val1, Fcar (this_cdr)))
371 break;
373 /* Record this change in the buffer, for undo purposes. */
374 if (BUFFERP (object))
376 record_property_change (i->position, LENGTH (i),
377 sym1, Fcar (this_cdr), object);
380 /* I's property has a different value -- change it */
381 Fsetcar (this_cdr, val1);
382 changed++;
383 break;
386 if (! found)
388 /* Record this change in the buffer, for undo purposes. */
389 if (BUFFERP (object))
391 record_property_change (i->position, LENGTH (i),
392 sym1, Qnil, object);
394 i->plist = Fcons (sym1, Fcons (val1, i->plist));
395 changed++;
399 UNGCPRO;
401 return changed;
404 /* For any members of PLIST which are properties of I, remove them
405 from I's plist.
406 OBJECT is the string or buffer containing I. */
408 static int
409 remove_properties (plist, i, object)
410 Lisp_Object plist;
411 INTERVAL i;
412 Lisp_Object object;
414 register Lisp_Object tail1, tail2, sym, current_plist;
415 register int changed = 0;
417 current_plist = i->plist;
418 /* Go through each element of plist. */
419 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
421 sym = Fcar (tail1);
423 /* First, remove the symbol if its at the head of the list */
424 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
426 if (BUFFERP (object))
428 record_property_change (i->position, LENGTH (i),
429 sym, Fcar (Fcdr (current_plist)),
430 object);
433 current_plist = Fcdr (Fcdr (current_plist));
434 changed++;
437 /* Go through i's plist, looking for sym */
438 tail2 = current_plist;
439 while (! NILP (tail2))
441 register Lisp_Object this;
442 this = Fcdr (Fcdr (tail2));
443 if (EQ (sym, Fcar (this)))
445 if (BUFFERP (object))
447 record_property_change (i->position, LENGTH (i),
448 sym, Fcar (Fcdr (this)), object);
451 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
452 changed++;
454 tail2 = this;
458 if (changed)
459 i->plist = current_plist;
460 return changed;
463 #if 0
464 /* Remove all properties from interval I. Return non-zero
465 if this changes the interval. */
467 static INLINE int
468 erase_properties (i)
469 INTERVAL i;
471 if (NILP (i->plist))
472 return 0;
474 i->plist = Qnil;
475 return 1;
477 #endif
479 /* Returns the interval of POSITION in OBJECT.
480 POSITION is BEG-based. */
482 INTERVAL
483 interval_of (position, object)
484 int position;
485 Lisp_Object object;
487 register INTERVAL i;
488 int beg, end;
490 if (NILP (object))
491 XSETBUFFER (object, current_buffer);
492 else if (EQ (object, Qt))
493 return NULL_INTERVAL;
495 CHECK_STRING_OR_BUFFER (object, 0);
497 if (BUFFERP (object))
499 register struct buffer *b = XBUFFER (object);
501 beg = BUF_BEGV (b);
502 end = BUF_ZV (b);
503 i = BUF_INTERVALS (b);
505 else
507 register struct Lisp_String *s = XSTRING (object);
509 beg = 0;
510 end = s->size;
511 i = s->intervals;
514 if (!(beg <= position && position <= end))
515 args_out_of_range (make_number (position), make_number (position));
516 if (beg == end || NULL_INTERVAL_P (i))
517 return NULL_INTERVAL;
519 return find_interval (i, position);
522 DEFUN ("text-properties-at", Ftext_properties_at,
523 Stext_properties_at, 1, 2, 0,
524 "Return the list of properties of the character at POSITION in OBJECT.\n\
525 OBJECT is the string or buffer to look for the properties in;\n\
526 nil means the current buffer.\n\
527 If POSITION is at the end of OBJECT, the value is nil.")
528 (position, object)
529 Lisp_Object position, object;
531 register INTERVAL i;
533 if (NILP (object))
534 XSETBUFFER (object, current_buffer);
536 i = validate_interval_range (object, &position, &position, soft);
537 if (NULL_INTERVAL_P (i))
538 return Qnil;
539 /* If POSITION is at the end of the interval,
540 it means it's the end of OBJECT.
541 There are no properties at the very end,
542 since no character follows. */
543 if (XINT (position) == LENGTH (i) + i->position)
544 return Qnil;
546 return i->plist;
549 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
550 "Return the value of POSITION's property PROP, in OBJECT.\n\
551 OBJECT is optional and defaults to the current buffer.\n\
552 If POSITION is at the end of OBJECT, the value is nil.")
553 (position, prop, object)
554 Lisp_Object position, object;
555 Lisp_Object prop;
557 return textget (Ftext_properties_at (position, object), prop);
560 /* Return the value of POSITION's property PROP, in OBJECT.
561 OBJECT is optional and defaults to the current buffer.
562 If OVERLAY is non-0, then in the case that the returned property is from
563 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
564 returned in *OVERLAY.
565 If POSITION is at the end of OBJECT, the value is nil.
566 If OBJECT is a buffer, then overlay properties are considered as well as
567 text properties.
568 If OBJECT is a window, then that window's buffer is used, but
569 window-specific overlays are considered only if they are associated
570 with OBJECT. */
571 Lisp_Object
572 get_char_property_and_overlay (position, prop, object, overlay)
573 Lisp_Object position, object;
574 register Lisp_Object prop;
575 Lisp_Object *overlay;
577 struct window *w = 0;
579 CHECK_NUMBER_COERCE_MARKER (position, 0);
581 if (NILP (object))
582 XSETBUFFER (object, current_buffer);
584 if (WINDOWP (object))
586 w = XWINDOW (object);
587 object = w->buffer;
589 if (BUFFERP (object))
591 int posn = XINT (position);
592 int noverlays;
593 Lisp_Object *overlay_vec, tem;
594 int next_overlay;
595 int len;
596 struct buffer *obuf = current_buffer;
598 set_buffer_temp (XBUFFER (object));
600 /* First try with room for 40 overlays. */
601 len = 40;
602 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
604 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
605 &next_overlay, NULL, 0);
607 /* If there are more than 40,
608 make enough space for all, and try again. */
609 if (noverlays > len)
611 len = noverlays;
612 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
613 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
614 &next_overlay, NULL, 0);
616 noverlays = sort_overlays (overlay_vec, noverlays, w);
618 set_buffer_temp (obuf);
620 /* Now check the overlays in order of decreasing priority. */
621 while (--noverlays >= 0)
623 tem = Foverlay_get (overlay_vec[noverlays], prop);
624 if (!NILP (tem))
626 if (overlay)
627 /* Return the overlay we got the property from. */
628 *overlay = overlay_vec[noverlays];
629 return tem;
634 if (overlay)
635 /* Indicate that the return value is not from an overlay. */
636 *overlay = Qnil;
638 /* Not a buffer, or no appropriate overlay, so fall through to the
639 simpler case. */
640 return Fget_text_property (position, prop, object);
643 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
644 "Return the value of POSITION's property PROP, in OBJECT.\n\
645 OBJECT is optional and defaults to the current buffer.\n\
646 If POSITION is at the end of OBJECT, the value is nil.\n\
647 If OBJECT is a buffer, then overlay properties are considered as well as\n\
648 text properties.\n\
649 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
650 overlays are considered only if they are associated with OBJECT.")
651 (position, prop, object)
652 Lisp_Object position, object;
653 register Lisp_Object prop;
655 return get_char_property_and_overlay (position, prop, object, 0);
658 DEFUN ("next-char-property-change", Fnext_char_property_change,
659 Snext_char_property_change, 1, 2, 0,
660 "Return the position of next text property or overlay change.\n\
661 This scans characters forward from POSITION in OBJECT till it finds\n\
662 a change in some text property, or the beginning or end of an overlay,\n\
663 and returns the position of that.\n\
664 If none is found, the function returns (point-max).\n\
666 If the optional third argument LIMIT is non-nil, don't search\n\
667 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
668 (position, limit)
669 Lisp_Object position, limit;
671 Lisp_Object temp;
673 temp = Fnext_overlay_change (position);
674 if (! NILP (limit))
676 CHECK_NUMBER (limit, 2);
677 if (XINT (limit) < XINT (temp))
678 temp = limit;
680 return Fnext_property_change (position, Qnil, temp);
683 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
684 Sprevious_char_property_change, 1, 2, 0,
685 "Return the position of previous text property or overlay change.\n\
686 Scans characters backward from POSITION in OBJECT till it finds\n\
687 a change in some text property, or the beginning or end of an overlay,\n\
688 and returns the position of that.\n\
689 If none is found, the function returns (point-max).\n\
691 If the optional third argument LIMIT is non-nil, don't search\n\
692 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
693 (position, limit)
694 Lisp_Object position, limit;
696 Lisp_Object temp;
698 temp = Fprevious_overlay_change (position);
699 if (! NILP (limit))
701 CHECK_NUMBER (limit, 2);
702 if (XINT (limit) > XINT (temp))
703 temp = limit;
705 return Fprevious_property_change (position, Qnil, temp);
709 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
710 Snext_single_char_property_change, 2, 4, 0,
711 "Return the position of next text property or overlay change for a specific property.\n\
712 Scans characters forward from POSITION till it finds\n\
713 a change in the PROP property, then returns the position of the change.\n\
714 The optional third argument OBJECT is the string or buffer to scan.\n\
715 The property values are compared with `eq'.\n\
716 Return nil if the property is constant all the way to the end of OBJECT.\n\
717 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
718 If the optional fourth argument LIMIT is non-nil, don't search\n\
719 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
720 (position, prop, object, limit)
721 Lisp_Object prop, position, object, limit;
723 if (STRINGP (object))
725 position = Fnext_single_property_change (position, prop, object, limit);
726 if (NILP (position))
728 if (NILP (limit))
729 position = make_number (XSTRING (object)->size);
730 else
731 position = limit;
734 else
736 Lisp_Object initial_value, value;
737 int count = specpdl_ptr - specpdl;
739 if (! NILP (object))
740 CHECK_BUFFER (object, 0);
742 if (BUFFERP (object) && current_buffer != XBUFFER (object))
744 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
745 Fset_buffer (object);
748 initial_value = Fget_char_property (position, prop, object);
750 if (NILP (limit))
751 XSETFASTINT (limit, BUF_ZV (current_buffer));
752 else
753 CHECK_NUMBER_COERCE_MARKER (limit, 0);
755 for (;;)
757 position = Fnext_char_property_change (position, limit);
758 if (XFASTINT (position) >= XFASTINT (limit)) {
759 position = limit;
760 break;
763 value = Fget_char_property (position, prop, object);
764 if (!EQ (value, initial_value))
765 break;
768 unbind_to (count, Qnil);
771 return position;
774 DEFUN ("previous-single-char-property-change",
775 Fprevious_single_char_property_change,
776 Sprevious_single_char_property_change, 2, 4, 0,
777 "Return the position of previous text property or overlay change for a specific property.\n\
778 Scans characters backward from POSITION till it finds\n\
779 a change in the PROP property, then returns the position of the change.\n\
780 The optional third argument OBJECT is the string or buffer to scan.\n\
781 The property values are compared with `eq'.\n\
782 Return nil if the property is constant all the way to the start of OBJECT.\n\
783 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
784 If the optional fourth argument LIMIT is non-nil, don't search\n\
785 back past position LIMIT; return LIMIT if nothing is found before LIMIT.")
786 (position, prop, object, limit)
787 Lisp_Object prop, position, object, limit;
789 if (STRINGP (object))
791 position = Fprevious_single_property_change (position, prop, object, limit);
792 if (NILP (position))
794 if (NILP (limit))
795 position = make_number (XSTRING (object)->size);
796 else
797 position = limit;
800 else
802 int count = specpdl_ptr - specpdl;
804 if (! NILP (object))
805 CHECK_BUFFER (object, 0);
807 if (BUFFERP (object) && current_buffer != XBUFFER (object))
809 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
810 Fset_buffer (object);
813 if (NILP (limit))
814 XSETFASTINT (limit, BUF_BEGV (current_buffer));
815 else
816 CHECK_NUMBER_COERCE_MARKER (limit, 0);
818 if (XFASTINT (position) <= XFASTINT (limit))
819 position = limit;
820 else
822 Lisp_Object initial_value =
823 Fget_char_property (make_number (XFASTINT (position) - 1),
824 prop, object);
826 for (;;)
828 position = Fprevious_char_property_change (position, limit);
830 if (XFASTINT (position) <= XFASTINT (limit))
832 position = limit;
833 break;
835 else
837 Lisp_Object value =
838 Fget_char_property (make_number (XFASTINT (position) - 1),
839 prop, object);
841 if (!EQ (value, initial_value))
842 break;
847 unbind_to (count, Qnil);
850 return position;
853 DEFUN ("next-property-change", Fnext_property_change,
854 Snext_property_change, 1, 3, 0,
855 "Return the position of next property change.\n\
856 Scans characters forward from POSITION in OBJECT till it finds\n\
857 a change in some text property, then returns the position of the change.\n\
858 The optional second argument OBJECT is the string or buffer to scan.\n\
859 Return nil if the property is constant all the way to the end of OBJECT.\n\
860 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
861 If the optional third argument LIMIT is non-nil, don't search\n\
862 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
863 (position, object, limit)
864 Lisp_Object position, object, limit;
866 register INTERVAL i, next;
868 if (NILP (object))
869 XSETBUFFER (object, current_buffer);
871 if (! NILP (limit) && ! EQ (limit, Qt))
872 CHECK_NUMBER_COERCE_MARKER (limit, 0);
874 i = validate_interval_range (object, &position, &position, soft);
876 /* If LIMIT is t, return start of next interval--don't
877 bother checking further intervals. */
878 if (EQ (limit, Qt))
880 if (NULL_INTERVAL_P (i))
881 next = i;
882 else
883 next = next_interval (i);
885 if (NULL_INTERVAL_P (next))
886 XSETFASTINT (position, (STRINGP (object)
887 ? XSTRING (object)->size
888 : BUF_ZV (XBUFFER (object))));
889 else
890 XSETFASTINT (position, next->position);
891 return position;
894 if (NULL_INTERVAL_P (i))
895 return limit;
897 next = next_interval (i);
899 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
900 && (NILP (limit) || next->position < XFASTINT (limit)))
901 next = next_interval (next);
903 if (NULL_INTERVAL_P (next))
904 return limit;
905 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
906 return limit;
908 XSETFASTINT (position, next->position);
909 return position;
912 /* Return 1 if there's a change in some property between BEG and END. */
915 property_change_between_p (beg, end)
916 int beg, end;
918 register INTERVAL i, next;
919 Lisp_Object object, pos;
921 XSETBUFFER (object, current_buffer);
922 XSETFASTINT (pos, beg);
924 i = validate_interval_range (object, &pos, &pos, soft);
925 if (NULL_INTERVAL_P (i))
926 return 0;
928 next = next_interval (i);
929 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
931 next = next_interval (next);
932 if (NULL_INTERVAL_P (next))
933 return 0;
934 if (next->position >= end)
935 return 0;
938 if (NULL_INTERVAL_P (next))
939 return 0;
941 return 1;
944 DEFUN ("next-single-property-change", Fnext_single_property_change,
945 Snext_single_property_change, 2, 4, 0,
946 "Return the position of next property change for a specific property.\n\
947 Scans characters forward from POSITION till it finds\n\
948 a change in the PROP property, then returns the position of the change.\n\
949 The optional third argument OBJECT is the string or buffer to scan.\n\
950 The property values are compared with `eq'.\n\
951 Return nil if the property is constant all the way to the end of OBJECT.\n\
952 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
953 If the optional fourth argument LIMIT is non-nil, don't search\n\
954 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
955 (position, prop, object, limit)
956 Lisp_Object position, prop, object, limit;
958 register INTERVAL i, next;
959 register Lisp_Object here_val;
961 if (NILP (object))
962 XSETBUFFER (object, current_buffer);
964 if (!NILP (limit))
965 CHECK_NUMBER_COERCE_MARKER (limit, 0);
967 i = validate_interval_range (object, &position, &position, soft);
968 if (NULL_INTERVAL_P (i))
969 return limit;
971 here_val = textget (i->plist, prop);
972 next = next_interval (i);
973 while (! NULL_INTERVAL_P (next)
974 && EQ (here_val, textget (next->plist, prop))
975 && (NILP (limit) || next->position < XFASTINT (limit)))
976 next = next_interval (next);
978 if (NULL_INTERVAL_P (next))
979 return limit;
980 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
981 return limit;
983 return make_number (next->position);
986 DEFUN ("previous-property-change", Fprevious_property_change,
987 Sprevious_property_change, 1, 3, 0,
988 "Return the position of previous property change.\n\
989 Scans characters backwards from POSITION in OBJECT till it finds\n\
990 a change in some text property, then returns the position of the change.\n\
991 The optional second argument OBJECT is the string or buffer to scan.\n\
992 Return nil if the property is constant all the way to the start of OBJECT.\n\
993 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
994 If the optional third argument LIMIT is non-nil, don't search\n\
995 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
996 (position, object, limit)
997 Lisp_Object position, object, limit;
999 register INTERVAL i, previous;
1001 if (NILP (object))
1002 XSETBUFFER (object, current_buffer);
1004 if (!NILP (limit))
1005 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1007 i = validate_interval_range (object, &position, &position, soft);
1008 if (NULL_INTERVAL_P (i))
1009 return limit;
1011 /* Start with the interval containing the char before point. */
1012 if (i->position == XFASTINT (position))
1013 i = previous_interval (i);
1015 previous = previous_interval (i);
1016 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1017 && (NILP (limit)
1018 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1019 previous = previous_interval (previous);
1020 if (NULL_INTERVAL_P (previous))
1021 return limit;
1022 if (!NILP (limit)
1023 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
1024 return limit;
1026 return make_number (previous->position + LENGTH (previous));
1029 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1030 Sprevious_single_property_change, 2, 4, 0,
1031 "Return the position of previous property change for a specific property.\n\
1032 Scans characters backward from POSITION till it finds\n\
1033 a change in the PROP property, then returns the position of the change.\n\
1034 The optional third argument OBJECT is the string or buffer to scan.\n\
1035 The property values are compared with `eq'.\n\
1036 Return nil if the property is constant all the way to the start of OBJECT.\n\
1037 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1038 If the optional fourth argument LIMIT is non-nil, don't search\n\
1039 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1040 (position, prop, object, limit)
1041 Lisp_Object position, prop, object, limit;
1043 register INTERVAL i, previous;
1044 register Lisp_Object here_val;
1046 if (NILP (object))
1047 XSETBUFFER (object, current_buffer);
1049 if (!NILP (limit))
1050 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1052 i = validate_interval_range (object, &position, &position, soft);
1054 /* Start with the interval containing the char before point. */
1055 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1056 i = previous_interval (i);
1058 if (NULL_INTERVAL_P (i))
1059 return limit;
1061 here_val = textget (i->plist, prop);
1062 previous = previous_interval (i);
1063 while (! NULL_INTERVAL_P (previous)
1064 && EQ (here_val, textget (previous->plist, prop))
1065 && (NILP (limit)
1066 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1067 previous = previous_interval (previous);
1068 if (NULL_INTERVAL_P (previous))
1069 return limit;
1070 if (!NILP (limit)
1071 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
1072 return limit;
1074 return make_number (previous->position + LENGTH (previous));
1077 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1079 DEFUN ("add-text-properties", Fadd_text_properties,
1080 Sadd_text_properties, 3, 4, 0,
1081 "Add properties to the text from START to END.\n\
1082 The third argument PROPERTIES is a property list\n\
1083 specifying the property values to add.\n\
1084 The optional fourth argument, OBJECT,\n\
1085 is the string or buffer containing the text.\n\
1086 Return t if any property value actually changed, nil otherwise.")
1087 (start, end, properties, object)
1088 Lisp_Object start, end, properties, object;
1090 register INTERVAL i, unchanged;
1091 register int s, len, modified = 0;
1092 struct gcpro gcpro1;
1094 properties = validate_plist (properties);
1095 if (NILP (properties))
1096 return Qnil;
1098 if (NILP (object))
1099 XSETBUFFER (object, current_buffer);
1101 i = validate_interval_range (object, &start, &end, hard);
1102 if (NULL_INTERVAL_P (i))
1103 return Qnil;
1105 s = XINT (start);
1106 len = XINT (end) - s;
1108 /* No need to protect OBJECT, because we GC only if it's a buffer,
1109 and live buffers are always protected. */
1110 GCPRO1 (properties);
1112 /* If we're not starting on an interval boundary, we have to
1113 split this interval. */
1114 if (i->position != s)
1116 /* If this interval already has the properties, we can
1117 skip it. */
1118 if (interval_has_all_properties (properties, i))
1120 int got = (LENGTH (i) - (s - i->position));
1121 if (got >= len)
1122 RETURN_UNGCPRO (Qnil);
1123 len -= got;
1124 i = next_interval (i);
1126 else
1128 unchanged = i;
1129 i = split_interval_right (unchanged, s - unchanged->position);
1130 copy_properties (unchanged, i);
1134 if (BUFFERP (object))
1135 modify_region (XBUFFER (object), XINT (start), XINT (end));
1137 /* We are at the beginning of interval I, with LEN chars to scan. */
1138 for (;;)
1140 if (i == 0)
1141 abort ();
1143 if (LENGTH (i) >= len)
1145 /* We can UNGCPRO safely here, because there will be just
1146 one more chance to gc, in the next call to add_properties,
1147 and after that we will not need PROPERTIES or OBJECT again. */
1148 UNGCPRO;
1150 if (interval_has_all_properties (properties, i))
1152 if (BUFFERP (object))
1153 signal_after_change (XINT (start), XINT (end) - XINT (start),
1154 XINT (end) - XINT (start));
1156 return modified ? Qt : Qnil;
1159 if (LENGTH (i) == len)
1161 add_properties (properties, i, object);
1162 if (BUFFERP (object))
1163 signal_after_change (XINT (start), XINT (end) - XINT (start),
1164 XINT (end) - XINT (start));
1165 return Qt;
1168 /* i doesn't have the properties, and goes past the change limit */
1169 unchanged = i;
1170 i = split_interval_left (unchanged, len);
1171 copy_properties (unchanged, i);
1172 add_properties (properties, i, object);
1173 if (BUFFERP (object))
1174 signal_after_change (XINT (start), XINT (end) - XINT (start),
1175 XINT (end) - XINT (start));
1176 return Qt;
1179 len -= LENGTH (i);
1180 modified += add_properties (properties, i, object);
1181 i = next_interval (i);
1185 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1187 DEFUN ("put-text-property", Fput_text_property,
1188 Sput_text_property, 4, 5, 0,
1189 "Set one property of the text from START to END.\n\
1190 The third and fourth arguments PROPERTY and VALUE\n\
1191 specify the property to add.\n\
1192 The optional fifth argument, OBJECT,\n\
1193 is the string or buffer containing the text.")
1194 (start, end, property, value, object)
1195 Lisp_Object start, end, property, value, object;
1197 Fadd_text_properties (start, end,
1198 Fcons (property, Fcons (value, Qnil)),
1199 object);
1200 return Qnil;
1203 DEFUN ("set-text-properties", Fset_text_properties,
1204 Sset_text_properties, 3, 4, 0,
1205 "Completely replace properties of text from START to END.\n\
1206 The third argument PROPERTIES is the new property list.\n\
1207 The optional fourth argument, OBJECT,\n\
1208 is the string or buffer containing the text.")
1209 (start, end, properties, object)
1210 Lisp_Object start, end, properties, object;
1212 return set_text_properties (start, end, properties, object, Qt);
1216 /* Replace properties of text from START to END with new list of
1217 properties PROPERTIES. OBJECT is the buffer or string containing
1218 the text. OBJECT nil means use the current buffer.
1219 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1220 is non-nil if properties were replaced; it is nil if there weren't
1221 any properties to replace. */
1223 Lisp_Object
1224 set_text_properties (start, end, properties, object, signal_after_change_p)
1225 Lisp_Object start, end, properties, object, signal_after_change_p;
1227 register INTERVAL i, unchanged;
1228 register INTERVAL prev_changed = NULL_INTERVAL;
1229 register int s, len;
1230 Lisp_Object ostart, oend;
1232 ostart = start;
1233 oend = end;
1235 properties = validate_plist (properties);
1237 if (NILP (object))
1238 XSETBUFFER (object, current_buffer);
1240 /* If we want no properties for a whole string,
1241 get rid of its intervals. */
1242 if (NILP (properties) && STRINGP (object)
1243 && XFASTINT (start) == 0
1244 && XFASTINT (end) == XSTRING (object)->size)
1246 if (! XSTRING (object)->intervals)
1247 return Qt;
1249 XSTRING (object)->intervals = 0;
1250 return Qt;
1253 i = validate_interval_range (object, &start, &end, soft);
1255 if (NULL_INTERVAL_P (i))
1257 /* If buffer has no properties, and we want none, return now. */
1258 if (NILP (properties))
1259 return Qnil;
1261 /* Restore the original START and END values
1262 because validate_interval_range increments them for strings. */
1263 start = ostart;
1264 end = oend;
1266 i = validate_interval_range (object, &start, &end, hard);
1267 /* This can return if start == end. */
1268 if (NULL_INTERVAL_P (i))
1269 return Qnil;
1272 s = XINT (start);
1273 len = XINT (end) - s;
1275 if (BUFFERP (object))
1276 modify_region (XBUFFER (object), XINT (start), XINT (end));
1278 if (i->position != s)
1280 unchanged = i;
1281 i = split_interval_right (unchanged, s - unchanged->position);
1283 if (LENGTH (i) > len)
1285 copy_properties (unchanged, i);
1286 i = split_interval_left (i, len);
1287 set_properties (properties, i, object);
1288 if (BUFFERP (object) && !NILP (signal_after_change_p))
1289 signal_after_change (XINT (start), XINT (end) - XINT (start),
1290 XINT (end) - XINT (start));
1292 return Qt;
1295 set_properties (properties, i, object);
1297 if (LENGTH (i) == len)
1299 if (BUFFERP (object) && !NILP (signal_after_change_p))
1300 signal_after_change (XINT (start), XINT (end) - XINT (start),
1301 XINT (end) - XINT (start));
1303 return Qt;
1306 prev_changed = i;
1307 len -= LENGTH (i);
1308 i = next_interval (i);
1311 /* We are starting at the beginning of an interval, I */
1312 while (len > 0)
1314 if (i == 0)
1315 abort ();
1317 if (LENGTH (i) >= len)
1319 if (LENGTH (i) > len)
1320 i = split_interval_left (i, len);
1322 /* We have to call set_properties even if we are going to
1323 merge the intervals, so as to make the undo records
1324 and cause redisplay to happen. */
1325 set_properties (properties, i, object);
1326 if (!NULL_INTERVAL_P (prev_changed))
1327 merge_interval_left (i);
1328 if (BUFFERP (object) && !NILP (signal_after_change_p))
1329 signal_after_change (XINT (start), XINT (end) - XINT (start),
1330 XINT (end) - XINT (start));
1331 return Qt;
1334 len -= LENGTH (i);
1336 /* We have to call set_properties even if we are going to
1337 merge the intervals, so as to make the undo records
1338 and cause redisplay to happen. */
1339 set_properties (properties, i, object);
1340 if (NULL_INTERVAL_P (prev_changed))
1341 prev_changed = i;
1342 else
1343 prev_changed = i = merge_interval_left (i);
1345 i = next_interval (i);
1348 if (BUFFERP (object) && !NILP (signal_after_change_p))
1349 signal_after_change (XINT (start), XINT (end) - XINT (start),
1350 XINT (end) - XINT (start));
1351 return Qt;
1354 DEFUN ("remove-text-properties", Fremove_text_properties,
1355 Sremove_text_properties, 3, 4, 0,
1356 "Remove some properties from text from START to END.\n\
1357 The third argument PROPERTIES is a property list\n\
1358 whose property names specify the properties to remove.\n\
1359 \(The values stored in PROPERTIES are ignored.)\n\
1360 The optional fourth argument, OBJECT,\n\
1361 is the string or buffer containing the text.\n\
1362 Return t if any property was actually removed, nil otherwise.")
1363 (start, end, properties, object)
1364 Lisp_Object start, end, properties, object;
1366 register INTERVAL i, unchanged;
1367 register int s, len, modified = 0;
1369 if (NILP (object))
1370 XSETBUFFER (object, current_buffer);
1372 i = validate_interval_range (object, &start, &end, soft);
1373 if (NULL_INTERVAL_P (i))
1374 return Qnil;
1376 s = XINT (start);
1377 len = XINT (end) - s;
1379 if (i->position != s)
1381 /* No properties on this first interval -- return if
1382 it covers the entire region. */
1383 if (! interval_has_some_properties (properties, i))
1385 int got = (LENGTH (i) - (s - i->position));
1386 if (got >= len)
1387 return Qnil;
1388 len -= got;
1389 i = next_interval (i);
1391 /* Split away the beginning of this interval; what we don't
1392 want to modify. */
1393 else
1395 unchanged = i;
1396 i = split_interval_right (unchanged, s - unchanged->position);
1397 copy_properties (unchanged, i);
1401 if (BUFFERP (object))
1402 modify_region (XBUFFER (object), XINT (start), XINT (end));
1404 /* We are at the beginning of an interval, with len to scan */
1405 for (;;)
1407 if (i == 0)
1408 abort ();
1410 if (LENGTH (i) >= len)
1412 if (! interval_has_some_properties (properties, i))
1413 return modified ? Qt : Qnil;
1415 if (LENGTH (i) == len)
1417 remove_properties (properties, i, object);
1418 if (BUFFERP (object))
1419 signal_after_change (XINT (start), XINT (end) - XINT (start),
1420 XINT (end) - XINT (start));
1421 return Qt;
1424 /* i has the properties, and goes past the change limit */
1425 unchanged = i;
1426 i = split_interval_left (i, len);
1427 copy_properties (unchanged, i);
1428 remove_properties (properties, i, object);
1429 if (BUFFERP (object))
1430 signal_after_change (XINT (start), XINT (end) - XINT (start),
1431 XINT (end) - XINT (start));
1432 return Qt;
1435 len -= LENGTH (i);
1436 modified += remove_properties (properties, i, object);
1437 i = next_interval (i);
1441 DEFUN ("text-property-any", Ftext_property_any,
1442 Stext_property_any, 4, 5, 0,
1443 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1444 If so, return the position of the first character whose property PROPERTY\n\
1445 is `eq' to VALUE. Otherwise return nil.\n\
1446 The optional fifth argument, OBJECT, is the string or buffer\n\
1447 containing the text.")
1448 (start, end, property, value, object)
1449 Lisp_Object start, end, property, value, object;
1451 register INTERVAL i;
1452 register int e, pos;
1454 if (NILP (object))
1455 XSETBUFFER (object, current_buffer);
1456 i = validate_interval_range (object, &start, &end, soft);
1457 if (NULL_INTERVAL_P (i))
1458 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1459 e = XINT (end);
1461 while (! NULL_INTERVAL_P (i))
1463 if (i->position >= e)
1464 break;
1465 if (EQ (textget (i->plist, property), value))
1467 pos = i->position;
1468 if (pos < XINT (start))
1469 pos = XINT (start);
1470 return make_number (pos);
1472 i = next_interval (i);
1474 return Qnil;
1477 DEFUN ("text-property-not-all", Ftext_property_not_all,
1478 Stext_property_not_all, 4, 5, 0,
1479 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1480 If so, return the position of the first character whose property PROPERTY\n\
1481 is not `eq' to VALUE. Otherwise, return nil.\n\
1482 The optional fifth argument, OBJECT, is the string or buffer\n\
1483 containing the text.")
1484 (start, end, property, value, object)
1485 Lisp_Object start, end, property, value, object;
1487 register INTERVAL i;
1488 register int s, e;
1490 if (NILP (object))
1491 XSETBUFFER (object, current_buffer);
1492 i = validate_interval_range (object, &start, &end, soft);
1493 if (NULL_INTERVAL_P (i))
1494 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1495 s = XINT (start);
1496 e = XINT (end);
1498 while (! NULL_INTERVAL_P (i))
1500 if (i->position >= e)
1501 break;
1502 if (! EQ (textget (i->plist, property), value))
1504 if (i->position > s)
1505 s = i->position;
1506 return make_number (s);
1508 i = next_interval (i);
1510 return Qnil;
1513 /* I don't think this is the right interface to export; how often do you
1514 want to do something like this, other than when you're copying objects
1515 around?
1517 I think it would be better to have a pair of functions, one which
1518 returns the text properties of a region as a list of ranges and
1519 plists, and another which applies such a list to another object. */
1521 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1522 SRC and DEST may each refer to strings or buffers.
1523 Optional sixth argument PROP causes only that property to be copied.
1524 Properties are copied to DEST as if by `add-text-properties'.
1525 Return t if any property value actually changed, nil otherwise. */
1527 /* Note this can GC when DEST is a buffer. */
1529 Lisp_Object
1530 copy_text_properties (start, end, src, pos, dest, prop)
1531 Lisp_Object start, end, src, pos, dest, prop;
1533 INTERVAL i;
1534 Lisp_Object res;
1535 Lisp_Object stuff;
1536 Lisp_Object plist;
1537 int s, e, e2, p, len, modified = 0;
1538 struct gcpro gcpro1, gcpro2;
1540 i = validate_interval_range (src, &start, &end, soft);
1541 if (NULL_INTERVAL_P (i))
1542 return Qnil;
1544 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1546 Lisp_Object dest_start, dest_end;
1548 dest_start = pos;
1549 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1550 /* Apply this to a copy of pos; it will try to increment its arguments,
1551 which we don't want. */
1552 validate_interval_range (dest, &dest_start, &dest_end, soft);
1555 s = XINT (start);
1556 e = XINT (end);
1557 p = XINT (pos);
1559 stuff = Qnil;
1561 while (s < e)
1563 e2 = i->position + LENGTH (i);
1564 if (e2 > e)
1565 e2 = e;
1566 len = e2 - s;
1568 plist = i->plist;
1569 if (! NILP (prop))
1570 while (! NILP (plist))
1572 if (EQ (Fcar (plist), prop))
1574 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1575 break;
1577 plist = Fcdr (Fcdr (plist));
1579 if (! NILP (plist))
1581 /* Must defer modifications to the interval tree in case src
1582 and dest refer to the same string or buffer. */
1583 stuff = Fcons (Fcons (make_number (p),
1584 Fcons (make_number (p + len),
1585 Fcons (plist, Qnil))),
1586 stuff);
1589 i = next_interval (i);
1590 if (NULL_INTERVAL_P (i))
1591 break;
1593 p += len;
1594 s = i->position;
1597 GCPRO2 (stuff, dest);
1599 while (! NILP (stuff))
1601 res = Fcar (stuff);
1602 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1603 Fcar (Fcdr (Fcdr (res))), dest);
1604 if (! NILP (res))
1605 modified++;
1606 stuff = Fcdr (stuff);
1609 UNGCPRO;
1611 return modified ? Qt : Qnil;
1615 /* Return a list representing the text properties of OBJECT between
1616 START and END. if PROP is non-nil, report only on that property.
1617 Each result list element has the form (S E PLIST), where S and E
1618 are positions in OBJECT and PLIST is a property list containing the
1619 text properties of OBJECT between S and E. Value is nil if OBJECT
1620 doesn't contain text properties between START and END. */
1622 Lisp_Object
1623 text_property_list (object, start, end, prop)
1624 Lisp_Object object, start, end, prop;
1626 struct interval *i;
1627 Lisp_Object result;
1629 result = Qnil;
1631 i = validate_interval_range (object, &start, &end, soft);
1632 if (!NULL_INTERVAL_P (i))
1634 int s = XINT (start);
1635 int e = XINT (end);
1637 while (s < e)
1639 int interval_end, len;
1640 Lisp_Object plist;
1642 interval_end = i->position + LENGTH (i);
1643 if (interval_end > e)
1644 interval_end = e;
1645 len = interval_end - s;
1647 plist = i->plist;
1649 if (!NILP (prop))
1650 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1651 if (EQ (Fcar (plist), prop))
1653 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1654 break;
1657 if (!NILP (plist))
1658 result = Fcons (Fcons (make_number (s),
1659 Fcons (make_number (s + len),
1660 Fcons (plist, Qnil))),
1661 result);
1663 i = next_interval (i);
1664 if (NULL_INTERVAL_P (i))
1665 break;
1666 s = i->position;
1670 return result;
1674 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1675 (START END PLIST), where START and END are positions and PLIST is a
1676 property list containing the text properties to add. Adjust START
1677 and END positions by DELTA before adding properties. Value is
1678 non-zero if OBJECT was modified. */
1681 add_text_properties_from_list (object, list, delta)
1682 Lisp_Object object, list, delta;
1684 struct gcpro gcpro1, gcpro2;
1685 int modified_p = 0;
1687 GCPRO2 (list, object);
1689 for (; CONSP (list); list = XCDR (list))
1691 Lisp_Object item, start, end, plist, tem;
1693 item = XCAR (list);
1694 start = make_number (XINT (XCAR (item)) + XINT (delta));
1695 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1696 plist = XCAR (XCDR (XCDR (item)));
1698 tem = Fadd_text_properties (start, end, plist, object);
1699 if (!NILP (tem))
1700 modified_p = 1;
1703 UNGCPRO;
1704 return modified_p;
1709 /* Modify end-points of ranges in LIST destructively. LIST is a list
1710 as returned from text_property_list. Change end-points equal to
1711 OLD_END to NEW_END. */
1713 void
1714 extend_property_ranges (list, old_end, new_end)
1715 Lisp_Object list, old_end, new_end;
1717 for (; CONSP (list); list = XCDR (list))
1719 Lisp_Object item, end;
1721 item = XCAR (list);
1722 end = XCAR (XCDR (item));
1724 if (EQ (end, old_end))
1725 XCAR (XCDR (item)) = new_end;
1731 /* Call the modification hook functions in LIST, each with START and END. */
1733 static void
1734 call_mod_hooks (list, start, end)
1735 Lisp_Object list, start, end;
1737 struct gcpro gcpro1;
1738 GCPRO1 (list);
1739 while (!NILP (list))
1741 call2 (Fcar (list), start, end);
1742 list = Fcdr (list);
1744 UNGCPRO;
1747 /* Check for read-only intervals between character positions START ... END,
1748 in BUF, and signal an error if we find one.
1750 Then check for any modification hooks in the range.
1751 Create a list of all these hooks in lexicographic order,
1752 eliminating consecutive extra copies of the same hook. Then call
1753 those hooks in order, with START and END - 1 as arguments. */
1755 void
1756 verify_interval_modification (buf, start, end)
1757 struct buffer *buf;
1758 int start, end;
1760 register INTERVAL intervals = BUF_INTERVALS (buf);
1761 register INTERVAL i;
1762 Lisp_Object hooks;
1763 register Lisp_Object prev_mod_hooks;
1764 Lisp_Object mod_hooks;
1765 struct gcpro gcpro1;
1767 hooks = Qnil;
1768 prev_mod_hooks = Qnil;
1769 mod_hooks = Qnil;
1771 interval_insert_behind_hooks = Qnil;
1772 interval_insert_in_front_hooks = Qnil;
1774 if (NULL_INTERVAL_P (intervals))
1775 return;
1777 if (start > end)
1779 int temp = start;
1780 start = end;
1781 end = temp;
1784 /* For an insert operation, check the two chars around the position. */
1785 if (start == end)
1787 INTERVAL prev;
1788 Lisp_Object before, after;
1790 /* Set I to the interval containing the char after START,
1791 and PREV to the interval containing the char before START.
1792 Either one may be null. They may be equal. */
1793 i = find_interval (intervals, start);
1795 if (start == BUF_BEGV (buf))
1796 prev = 0;
1797 else if (i->position == start)
1798 prev = previous_interval (i);
1799 else if (i->position < start)
1800 prev = i;
1801 if (start == BUF_ZV (buf))
1802 i = 0;
1804 /* If Vinhibit_read_only is set and is not a list, we can
1805 skip the read_only checks. */
1806 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1808 /* If I and PREV differ we need to check for the read-only
1809 property together with its stickiness. If either I or
1810 PREV are 0, this check is all we need.
1811 We have to take special care, since read-only may be
1812 indirectly defined via the category property. */
1813 if (i != prev)
1815 if (! NULL_INTERVAL_P (i))
1817 after = textget (i->plist, Qread_only);
1819 /* If interval I is read-only and read-only is
1820 front-sticky, inhibit insertion.
1821 Check for read-only as well as category. */
1822 if (! NILP (after)
1823 && NILP (Fmemq (after, Vinhibit_read_only)))
1825 Lisp_Object tem;
1827 tem = textget (i->plist, Qfront_sticky);
1828 if (TMEM (Qread_only, tem)
1829 || (NILP (Fplist_get (i->plist, Qread_only))
1830 && TMEM (Qcategory, tem)))
1831 Fsignal (Qtext_read_only, Qnil);
1835 if (! NULL_INTERVAL_P (prev))
1837 before = textget (prev->plist, Qread_only);
1839 /* If interval PREV is read-only and read-only isn't
1840 rear-nonsticky, inhibit insertion.
1841 Check for read-only as well as category. */
1842 if (! NILP (before)
1843 && NILP (Fmemq (before, Vinhibit_read_only)))
1845 Lisp_Object tem;
1847 tem = textget (prev->plist, Qrear_nonsticky);
1848 if (! TMEM (Qread_only, tem)
1849 && (! NILP (Fplist_get (prev->plist,Qread_only))
1850 || ! TMEM (Qcategory, tem)))
1851 Fsignal (Qtext_read_only, Qnil);
1855 else if (! NULL_INTERVAL_P (i))
1857 after = textget (i->plist, Qread_only);
1859 /* If interval I is read-only and read-only is
1860 front-sticky, inhibit insertion.
1861 Check for read-only as well as category. */
1862 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1864 Lisp_Object tem;
1866 tem = textget (i->plist, Qfront_sticky);
1867 if (TMEM (Qread_only, tem)
1868 || (NILP (Fplist_get (i->plist, Qread_only))
1869 && TMEM (Qcategory, tem)))
1870 Fsignal (Qtext_read_only, Qnil);
1872 tem = textget (prev->plist, Qrear_nonsticky);
1873 if (! TMEM (Qread_only, tem)
1874 && (! NILP (Fplist_get (prev->plist, Qread_only))
1875 || ! TMEM (Qcategory, tem)))
1876 Fsignal (Qtext_read_only, Qnil);
1881 /* Run both insert hooks (just once if they're the same). */
1882 if (!NULL_INTERVAL_P (prev))
1883 interval_insert_behind_hooks
1884 = textget (prev->plist, Qinsert_behind_hooks);
1885 if (!NULL_INTERVAL_P (i))
1886 interval_insert_in_front_hooks
1887 = textget (i->plist, Qinsert_in_front_hooks);
1889 else
1891 /* Loop over intervals on or next to START...END,
1892 collecting their hooks. */
1894 i = find_interval (intervals, start);
1897 if (! INTERVAL_WRITABLE_P (i))
1898 Fsignal (Qtext_read_only, Qnil);
1900 mod_hooks = textget (i->plist, Qmodification_hooks);
1901 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1903 hooks = Fcons (mod_hooks, hooks);
1904 prev_mod_hooks = mod_hooks;
1907 i = next_interval (i);
1909 /* Keep going thru the interval containing the char before END. */
1910 while (! NULL_INTERVAL_P (i) && i->position < end);
1912 GCPRO1 (hooks);
1913 hooks = Fnreverse (hooks);
1914 while (! EQ (hooks, Qnil))
1916 call_mod_hooks (Fcar (hooks), make_number (start),
1917 make_number (end));
1918 hooks = Fcdr (hooks);
1920 UNGCPRO;
1924 /* Run the interval hooks for an insertion on character range START ... END.
1925 verify_interval_modification chose which hooks to run;
1926 this function is called after the insertion happens
1927 so it can indicate the range of inserted text. */
1929 void
1930 report_interval_modification (start, end)
1931 Lisp_Object start, end;
1933 if (! NILP (interval_insert_behind_hooks))
1934 call_mod_hooks (interval_insert_behind_hooks, start, end);
1935 if (! NILP (interval_insert_in_front_hooks)
1936 && ! EQ (interval_insert_in_front_hooks,
1937 interval_insert_behind_hooks))
1938 call_mod_hooks (interval_insert_in_front_hooks, start, end);
1941 void
1942 syms_of_textprop ()
1944 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
1945 "Property-list used as default values.\n\
1946 The value of a property in this list is seen as the value for every\n\
1947 character that does not have its own value for that property.");
1948 Vdefault_text_properties = Qnil;
1950 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1951 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1952 This also inhibits the use of the `intangible' text property.");
1953 Vinhibit_point_motion_hooks = Qnil;
1955 DEFVAR_LISP ("text-property-default-nonsticky",
1956 &Vtext_property_default_nonsticky,
1957 "Alist of properties vs the corresponding non-stickinesses.\n\
1958 Each element has the form (PROPERTY . NONSTICKINESS).\n\
1960 If a character in a buffer has PROPERTY, new text inserted adjacent to\n\
1961 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,\n\
1962 inherits it if NONSTICKINESS is nil. The front-sticky and\n\
1963 rear-nonsticky properties of the character overrides NONSTICKINESS.");
1964 Vtext_property_default_nonsticky = Qnil;
1966 staticpro (&interval_insert_behind_hooks);
1967 staticpro (&interval_insert_in_front_hooks);
1968 interval_insert_behind_hooks = Qnil;
1969 interval_insert_in_front_hooks = Qnil;
1972 /* Common attributes one might give text */
1974 staticpro (&Qforeground);
1975 Qforeground = intern ("foreground");
1976 staticpro (&Qbackground);
1977 Qbackground = intern ("background");
1978 staticpro (&Qfont);
1979 Qfont = intern ("font");
1980 staticpro (&Qstipple);
1981 Qstipple = intern ("stipple");
1982 staticpro (&Qunderline);
1983 Qunderline = intern ("underline");
1984 staticpro (&Qread_only);
1985 Qread_only = intern ("read-only");
1986 staticpro (&Qinvisible);
1987 Qinvisible = intern ("invisible");
1988 staticpro (&Qintangible);
1989 Qintangible = intern ("intangible");
1990 staticpro (&Qcategory);
1991 Qcategory = intern ("category");
1992 staticpro (&Qlocal_map);
1993 Qlocal_map = intern ("local-map");
1994 staticpro (&Qfront_sticky);
1995 Qfront_sticky = intern ("front-sticky");
1996 staticpro (&Qrear_nonsticky);
1997 Qrear_nonsticky = intern ("rear-nonsticky");
1998 staticpro (&Qmouse_face);
1999 Qmouse_face = intern ("mouse-face");
2001 /* Properties that text might use to specify certain actions */
2003 staticpro (&Qmouse_left);
2004 Qmouse_left = intern ("mouse-left");
2005 staticpro (&Qmouse_entered);
2006 Qmouse_entered = intern ("mouse-entered");
2007 staticpro (&Qpoint_left);
2008 Qpoint_left = intern ("point-left");
2009 staticpro (&Qpoint_entered);
2010 Qpoint_entered = intern ("point-entered");
2012 defsubr (&Stext_properties_at);
2013 defsubr (&Sget_text_property);
2014 defsubr (&Sget_char_property);
2015 defsubr (&Snext_char_property_change);
2016 defsubr (&Sprevious_char_property_change);
2017 defsubr (&Snext_single_char_property_change);
2018 defsubr (&Sprevious_single_char_property_change);
2019 defsubr (&Snext_property_change);
2020 defsubr (&Snext_single_property_change);
2021 defsubr (&Sprevious_property_change);
2022 defsubr (&Sprevious_single_property_change);
2023 defsubr (&Sadd_text_properties);
2024 defsubr (&Sput_text_property);
2025 defsubr (&Sset_text_properties);
2026 defsubr (&Sremove_text_properties);
2027 defsubr (&Stext_property_any);
2028 defsubr (&Stext_property_not_all);
2029 /* defsubr (&Serase_text_properties); */
2030 /* defsubr (&Scopy_text_properties); */