(mail-user-agent): Replaces mua-paradigm.
[emacs.git] / src / textprop.c
blob5e9daf99fd0c5f16cf7aff8cdb4db68aeff7c872
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995 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. */
49 /* The rest of the file is within this conditional */
50 #ifdef USE_TEXT_PROPERTIES
52 /* Types of hooks. */
53 Lisp_Object Qmouse_left;
54 Lisp_Object Qmouse_entered;
55 Lisp_Object Qpoint_left;
56 Lisp_Object Qpoint_entered;
57 Lisp_Object Qcategory;
58 Lisp_Object Qlocal_map;
60 /* Visual properties text (including strings) may have. */
61 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
62 Lisp_Object Qinvisible, Qread_only, Qintangible;
64 /* Sticky properties */
65 Lisp_Object Qfront_sticky, Qrear_nonsticky;
67 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
68 the o1's cdr. Otherwise, return zero. This is handy for
69 traversing plists. */
70 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCONS (o1)->cdr, CONSP (o2)))
72 Lisp_Object Vinhibit_point_motion_hooks;
73 Lisp_Object Vdefault_text_properties;
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;
80 /* Extract the interval at the position pointed to by BEGIN from
81 OBJECT, a string or buffer. Additionally, check that the positions
82 pointed to by BEGIN and END are within the bounds of OBJECT, and
83 reverse them if *BEGIN is greater than *END. The objects pointed
84 to by BEGIN and END may be integers or markers; if the latter, they
85 are coerced to integers.
87 When OBJECT is a string, we increment *BEGIN and *END
88 to make them origin-one.
90 Note that buffer points don't correspond to interval indices.
91 For example, point-max is 1 greater than the index of the last
92 character. This difference is handled in the caller, which uses
93 the validated points to determine a length, and operates on that.
94 Exceptions are Ftext_properties_at, Fnext_property_change, and
95 Fprevious_property_change which call this function with BEGIN == END.
96 Handle this case specially.
98 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
99 create an interval tree for OBJECT if one doesn't exist, provided
100 the object actually contains text. In the current design, if there
101 is no text, there can be no text properties. */
103 #define soft 0
104 #define hard 1
106 static INTERVAL
107 validate_interval_range (object, begin, end, force)
108 Lisp_Object object, *begin, *end;
109 int force;
111 register INTERVAL i;
112 int searchpos;
114 CHECK_STRING_OR_BUFFER (object, 0);
115 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
116 CHECK_NUMBER_COERCE_MARKER (*end, 0);
118 /* If we are asked for a point, but from a subr which operates
119 on a range, then return nothing. */
120 if (EQ (*begin, *end) && begin != end)
121 return NULL_INTERVAL;
123 if (XINT (*begin) > XINT (*end))
125 Lisp_Object n;
126 n = *begin;
127 *begin = *end;
128 *end = n;
131 if (BUFFERP (object))
133 register struct buffer *b = XBUFFER (object);
135 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
136 && XINT (*end) <= BUF_ZV (b)))
137 args_out_of_range (*begin, *end);
138 i = BUF_INTERVALS (b);
140 /* If there's no text, there are no properties. */
141 if (BUF_BEGV (b) == BUF_ZV (b))
142 return NULL_INTERVAL;
144 searchpos = XINT (*begin);
146 else
148 register struct Lisp_String *s = XSTRING (object);
150 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
151 && XINT (*end) <= s->size))
152 args_out_of_range (*begin, *end);
153 /* User-level Positions in strings start with 0,
154 but the interval code always wants positions starting with 1. */
155 XSETFASTINT (*begin, XFASTINT (*begin) + 1);
156 if (begin != end)
157 XSETFASTINT (*end, XFASTINT (*end) + 1);
158 i = s->intervals;
160 if (s->size == 0)
161 return NULL_INTERVAL;
163 searchpos = XINT (*begin);
166 if (NULL_INTERVAL_P (i))
167 return (force ? create_root_interval (object) : i);
169 return find_interval (i, searchpos);
172 /* Validate LIST as a property list. If LIST is not a list, then
173 make one consisting of (LIST nil). Otherwise, verify that LIST
174 is even numbered and thus suitable as a plist. */
176 static Lisp_Object
177 validate_plist (list)
178 Lisp_Object list;
180 if (NILP (list))
181 return Qnil;
183 if (CONSP (list))
185 register int i;
186 register Lisp_Object tail;
187 for (i = 0, tail = list; !NILP (tail); i++)
189 tail = Fcdr (tail);
190 QUIT;
192 if (i & 1)
193 error ("Odd length text property list");
194 return list;
197 return Fcons (list, Fcons (Qnil, Qnil));
200 /* Return nonzero if interval I has all the properties,
201 with the same values, of list PLIST. */
203 static int
204 interval_has_all_properties (plist, i)
205 Lisp_Object plist;
206 INTERVAL i;
208 register Lisp_Object tail1, tail2, sym1, sym2;
209 register int found;
211 /* Go through each element of PLIST. */
212 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
214 sym1 = Fcar (tail1);
215 found = 0;
217 /* Go through I's plist, looking for sym1 */
218 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
219 if (EQ (sym1, Fcar (tail2)))
221 /* Found the same property on both lists. If the
222 values are unequal, return zero. */
223 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
224 return 0;
226 /* Property has same value on both lists; go to next one. */
227 found = 1;
228 break;
231 if (! found)
232 return 0;
235 return 1;
238 /* Return nonzero if the plist of interval I has any of the
239 properties of PLIST, regardless of their values. */
241 static INLINE int
242 interval_has_some_properties (plist, i)
243 Lisp_Object plist;
244 INTERVAL i;
246 register Lisp_Object tail1, tail2, sym;
248 /* Go through each element of PLIST. */
249 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
251 sym = Fcar (tail1);
253 /* Go through i's plist, looking for tail1 */
254 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
255 if (EQ (sym, Fcar (tail2)))
256 return 1;
259 return 0;
262 /* Changing the plists of individual intervals. */
264 /* Return the value of PROP in property-list PLIST, or Qunbound if it
265 has none. */
266 static Lisp_Object
267 property_value (plist, prop)
268 Lisp_Object plist, prop;
270 Lisp_Object value;
272 while (PLIST_ELT_P (plist, value))
273 if (EQ (XCONS (plist)->car, prop))
274 return XCONS (value)->car;
275 else
276 plist = XCONS (value)->cdr;
278 return Qunbound;
281 /* Set the properties of INTERVAL to PROPERTIES,
282 and record undo info for the previous values.
283 OBJECT is the string or buffer that INTERVAL belongs to. */
285 static void
286 set_properties (properties, interval, object)
287 Lisp_Object properties, object;
288 INTERVAL interval;
290 Lisp_Object sym, value;
292 if (BUFFERP (object))
294 /* For each property in the old plist which is missing from PROPERTIES,
295 or has a different value in PROPERTIES, make an undo record. */
296 for (sym = interval->plist;
297 PLIST_ELT_P (sym, value);
298 sym = XCONS (value)->cdr)
299 if (! EQ (property_value (properties, XCONS (sym)->car),
300 XCONS (value)->car))
302 modify_region (XBUFFER (object),
303 make_number (interval->position),
304 make_number (interval->position + LENGTH (interval)));
305 record_property_change (interval->position, LENGTH (interval),
306 XCONS (sym)->car, XCONS (value)->car,
307 object);
308 signal_after_change (interval->position, LENGTH (interval),
309 LENGTH (interval));
312 /* For each new property that has no value at all in the old plist,
313 make an undo record binding it to nil, so it will be removed. */
314 for (sym = properties;
315 PLIST_ELT_P (sym, value);
316 sym = XCONS (value)->cdr)
317 if (EQ (property_value (interval->plist, XCONS (sym)->car), Qunbound))
319 modify_region (XBUFFER (object),
320 make_number (interval->position),
321 make_number (interval->position + LENGTH (interval)));
322 record_property_change (interval->position, LENGTH (interval),
323 XCONS (sym)->car, Qnil,
324 object);
325 signal_after_change (interval->position, LENGTH (interval),
326 LENGTH (interval));
330 /* Store new properties. */
331 interval->plist = Fcopy_sequence (properties);
334 /* Add the properties of PLIST to the interval I, or set
335 the value of I's property to the value of the property on PLIST
336 if they are different.
338 OBJECT should be the string or buffer the interval is in.
340 Return nonzero if this changes I (i.e., if any members of PLIST
341 are actually added to I's plist) */
343 static int
344 add_properties (plist, i, object)
345 Lisp_Object plist;
346 INTERVAL i;
347 Lisp_Object object;
349 Lisp_Object tail1, tail2, sym1, val1;
350 register int changed = 0;
351 register int found;
352 struct gcpro gcpro1, gcpro2, gcpro3;
354 tail1 = plist;
355 sym1 = Qnil;
356 val1 = Qnil;
357 /* No need to protect OBJECT, because we can GC only in the case
358 where it is a buffer, and live buffers are always protected.
359 I and its plist are also protected, via OBJECT. */
360 GCPRO3 (tail1, sym1, val1);
362 /* Go through each element of PLIST. */
363 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
365 sym1 = Fcar (tail1);
366 val1 = Fcar (Fcdr (tail1));
367 found = 0;
369 /* Go through I's plist, looking for sym1 */
370 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
371 if (EQ (sym1, Fcar (tail2)))
373 /* No need to gcpro, because tail2 protects this
374 and it must be a cons cell (we get an error otherwise). */
375 register Lisp_Object this_cdr;
377 this_cdr = Fcdr (tail2);
378 /* Found the property. Now check its value. */
379 found = 1;
381 /* The properties have the same value on both lists.
382 Continue to the next property. */
383 if (EQ (val1, Fcar (this_cdr)))
384 break;
386 /* Record this change in the buffer, for undo purposes. */
387 if (BUFFERP (object))
389 modify_region (XBUFFER (object),
390 make_number (i->position),
391 make_number (i->position + LENGTH (i)));
392 record_property_change (i->position, LENGTH (i),
393 sym1, Fcar (this_cdr), object);
394 signal_after_change (i->position, LENGTH (i), LENGTH (i));
397 /* I's property has a different value -- change it */
398 Fsetcar (this_cdr, val1);
399 changed++;
400 break;
403 if (! found)
405 /* Record this change in the buffer, for undo purposes. */
406 if (BUFFERP (object))
408 modify_region (XBUFFER (object),
409 make_number (i->position),
410 make_number (i->position + LENGTH (i)));
411 record_property_change (i->position, LENGTH (i),
412 sym1, Qnil, object);
413 signal_after_change (i->position, LENGTH (i), LENGTH (i));
415 i->plist = Fcons (sym1, Fcons (val1, i->plist));
416 changed++;
420 UNGCPRO;
422 return changed;
425 /* For any members of PLIST which are properties of I, remove them
426 from I's plist.
427 OBJECT is the string or buffer containing I. */
429 static int
430 remove_properties (plist, i, object)
431 Lisp_Object plist;
432 INTERVAL i;
433 Lisp_Object object;
435 register Lisp_Object tail1, tail2, sym, current_plist;
436 register int changed = 0;
438 current_plist = i->plist;
439 /* Go through each element of plist. */
440 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
442 sym = Fcar (tail1);
444 /* First, remove the symbol if its at the head of the list */
445 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
447 if (BUFFERP (object))
449 modify_region (XBUFFER (object),
450 make_number (i->position),
451 make_number (i->position + LENGTH (i)));
452 record_property_change (i->position, LENGTH (i),
453 sym, Fcar (Fcdr (current_plist)),
454 object);
455 signal_after_change (i->position, LENGTH (i), LENGTH (i));
458 current_plist = Fcdr (Fcdr (current_plist));
459 changed++;
462 /* Go through i's plist, looking for sym */
463 tail2 = current_plist;
464 while (! NILP (tail2))
466 register Lisp_Object this;
467 this = Fcdr (Fcdr (tail2));
468 if (EQ (sym, Fcar (this)))
470 if (BUFFERP (object))
472 modify_region (XBUFFER (object),
473 make_number (i->position),
474 make_number (i->position + LENGTH (i)));
475 record_property_change (i->position, LENGTH (i),
476 sym, Fcar (Fcdr (this)), object);
477 signal_after_change (i->position, LENGTH (i), LENGTH (i));
480 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
481 changed++;
483 tail2 = this;
487 if (changed)
488 i->plist = current_plist;
489 return changed;
492 #if 0
493 /* Remove all properties from interval I. Return non-zero
494 if this changes the interval. */
496 static INLINE int
497 erase_properties (i)
498 INTERVAL i;
500 if (NILP (i->plist))
501 return 0;
503 i->plist = Qnil;
504 return 1;
506 #endif
508 DEFUN ("text-properties-at", Ftext_properties_at,
509 Stext_properties_at, 1, 2, 0,
510 "Return the list of properties held by the character at POSITION\n\
511 in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
512 defaults to the current buffer.\n\
513 If POSITION is at the end of OBJECT, the value is nil.")
514 (position, object)
515 Lisp_Object position, object;
517 register INTERVAL i;
519 if (NILP (object))
520 XSETBUFFER (object, current_buffer);
522 i = validate_interval_range (object, &position, &position, soft);
523 if (NULL_INTERVAL_P (i))
524 return Qnil;
525 /* If POSITION is at the end of the interval,
526 it means it's the end of OBJECT.
527 There are no properties at the very end,
528 since no character follows. */
529 if (XINT (position) == LENGTH (i) + i->position)
530 return Qnil;
532 return i->plist;
535 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
536 "Return the value of POSITION's property PROP, in OBJECT.\n\
537 OBJECT is optional and defaults to the current buffer.\n\
538 If POSITION is at the end of OBJECT, the value is nil.")
539 (position, prop, object)
540 Lisp_Object position, object;
541 Lisp_Object prop;
543 return textget (Ftext_properties_at (position, object), prop);
546 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
547 "Return the value of POSITION's property PROP, in OBJECT.\n\
548 OBJECT is optional and defaults to the current buffer.\n\
549 If POSITION is at the end of OBJECT, the value is nil.\n\
550 If OBJECT is a buffer, then overlay properties are considered as well as\n\
551 text properties.\n\
552 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
553 overlays are considered only if they are associated with OBJECT.")
554 (position, prop, object)
555 Lisp_Object position, object;
556 register Lisp_Object prop;
558 struct window *w = 0;
560 CHECK_NUMBER_COERCE_MARKER (position, 0);
562 if (NILP (object))
563 XSETBUFFER (object, current_buffer);
565 if (WINDOWP (object))
567 w = XWINDOW (object);
568 object = w->buffer;
570 if (BUFFERP (object))
572 int posn = XINT (position);
573 int noverlays;
574 Lisp_Object *overlay_vec, tem;
575 int next_overlay;
576 int len;
577 struct buffer *obuf = current_buffer;
579 set_buffer_temp (XBUFFER (object));
581 /* First try with room for 40 overlays. */
582 len = 40;
583 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
585 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
586 &next_overlay, NULL);
588 /* If there are more than 40,
589 make enough space for all, and try again. */
590 if (noverlays > len)
592 len = noverlays;
593 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
594 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
595 &next_overlay, NULL);
597 noverlays = sort_overlays (overlay_vec, noverlays, w);
599 set_buffer_temp (obuf);
601 /* Now check the overlays in order of decreasing priority. */
602 while (--noverlays >= 0)
604 tem = Foverlay_get (overlay_vec[noverlays], prop);
605 if (!NILP (tem))
606 return (tem);
609 /* Not a buffer, or no appropriate overlay, so fall through to the
610 simpler case. */
611 return (Fget_text_property (position, prop, object));
614 DEFUN ("next-property-change", Fnext_property_change,
615 Snext_property_change, 1, 3, 0,
616 "Return the position of next property change.\n\
617 Scans characters forward from POSITION in OBJECT till it finds\n\
618 a change in some text property, then returns the position of the change.\n\
619 The optional second argument OBJECT is the string or buffer to scan.\n\
620 Return nil if the property is constant all the way to the end of OBJECT.\n\
621 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
622 If the optional third argument LIMIT is non-nil, don't search\n\
623 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
624 (position, object, limit)
625 Lisp_Object position, object, limit;
627 register INTERVAL i, next;
629 if (NILP (object))
630 XSETBUFFER (object, current_buffer);
632 if (! NILP (limit) && ! EQ (limit, Qt))
633 CHECK_NUMBER_COERCE_MARKER (limit, 0);
635 i = validate_interval_range (object, &position, &position, soft);
637 /* If LIMIT is t, return start of next interval--don't
638 bother checking further intervals. */
639 if (EQ (limit, Qt))
641 if (NULL_INTERVAL_P (i))
642 next = i;
643 else
644 next = next_interval (i);
646 if (NULL_INTERVAL_P (next))
647 XSETFASTINT (position, (STRINGP (object)
648 ? XSTRING (object)->size
649 : BUF_ZV (XBUFFER (object))));
650 else
651 XSETFASTINT (position, next->position - (STRINGP (object)));
652 return position;
655 if (NULL_INTERVAL_P (i))
656 return limit;
658 next = next_interval (i);
660 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
661 && (NILP (limit) || next->position < XFASTINT (limit)))
662 next = next_interval (next);
664 if (NULL_INTERVAL_P (next))
665 return limit;
666 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
667 return limit;
669 XSETFASTINT (position, next->position - (STRINGP (object)));
670 return position;
673 /* Return 1 if there's a change in some property between BEG and END. */
676 property_change_between_p (beg, end)
677 int beg, end;
679 register INTERVAL i, next;
680 Lisp_Object object, pos;
682 XSETBUFFER (object, current_buffer);
683 XSETFASTINT (pos, beg);
685 i = validate_interval_range (object, &pos, &pos, soft);
686 if (NULL_INTERVAL_P (i))
687 return 0;
689 next = next_interval (i);
690 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
692 next = next_interval (next);
693 if (NULL_INTERVAL_P (next))
694 return 0;
695 if (next->position >= end)
696 return 0;
699 if (NULL_INTERVAL_P (next))
700 return 0;
702 return 1;
705 DEFUN ("next-single-property-change", Fnext_single_property_change,
706 Snext_single_property_change, 2, 4, 0,
707 "Return the position of next property change for a specific property.\n\
708 Scans characters forward from POSITION till it finds\n\
709 a change in the PROP property, then returns the position of the change.\n\
710 The optional third argument OBJECT is the string or buffer to scan.\n\
711 The property values are compared with `eq'.\n\
712 Return nil if the property is constant all the way to the end of OBJECT.\n\
713 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
714 If the optional fourth argument LIMIT is non-nil, don't search\n\
715 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
716 (position, prop, object, limit)
717 Lisp_Object position, prop, object, limit;
719 register INTERVAL i, next;
720 register Lisp_Object here_val;
722 if (NILP (object))
723 XSETBUFFER (object, current_buffer);
725 if (!NILP (limit))
726 CHECK_NUMBER_COERCE_MARKER (limit, 0);
728 i = validate_interval_range (object, &position, &position, soft);
729 if (NULL_INTERVAL_P (i))
730 return limit;
732 here_val = textget (i->plist, prop);
733 next = next_interval (i);
734 while (! NULL_INTERVAL_P (next)
735 && EQ (here_val, textget (next->plist, prop))
736 && (NILP (limit) || next->position < XFASTINT (limit)))
737 next = next_interval (next);
739 if (NULL_INTERVAL_P (next))
740 return limit;
741 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
742 return limit;
744 XSETFASTINT (position, next->position - (STRINGP (object)));
745 return position;
748 DEFUN ("previous-property-change", Fprevious_property_change,
749 Sprevious_property_change, 1, 3, 0,
750 "Return the position of previous property change.\n\
751 Scans characters backwards from POSITION in OBJECT till it finds\n\
752 a change in some text property, then returns the position of the change.\n\
753 The optional second argument OBJECT is the string or buffer to scan.\n\
754 Return nil if the property is constant all the way to the start of OBJECT.\n\
755 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
756 If the optional third argument LIMIT is non-nil, don't search\n\
757 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
758 (position, object, limit)
759 Lisp_Object position, object, limit;
761 register INTERVAL i, previous;
763 if (NILP (object))
764 XSETBUFFER (object, current_buffer);
766 if (!NILP (limit))
767 CHECK_NUMBER_COERCE_MARKER (limit, 0);
769 i = validate_interval_range (object, &position, &position, soft);
770 if (NULL_INTERVAL_P (i))
771 return limit;
773 /* Start with the interval containing the char before point. */
774 if (i->position == XFASTINT (position))
775 i = previous_interval (i);
777 previous = previous_interval (i);
778 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
779 && (NILP (limit)
780 || previous->position + LENGTH (previous) > XFASTINT (limit)))
781 previous = previous_interval (previous);
782 if (NULL_INTERVAL_P (previous))
783 return limit;
784 if (!NILP (limit)
785 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
786 return limit;
788 XSETFASTINT (position, (previous->position + LENGTH (previous)
789 - (STRINGP (object))));
790 return position;
793 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
794 Sprevious_single_property_change, 2, 4, 0,
795 "Return the position of previous property change for a specific property.\n\
796 Scans characters backward from POSITION till it finds\n\
797 a change in the PROP property, then returns the position of the change.\n\
798 The optional third argument OBJECT is the string or buffer to scan.\n\
799 The property values are compared with `eq'.\n\
800 Return nil if the property is constant all the way to the start of OBJECT.\n\
801 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
802 If the optional fourth argument LIMIT is non-nil, don't search\n\
803 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
804 (position, prop, object, limit)
805 Lisp_Object position, prop, object, limit;
807 register INTERVAL i, previous;
808 register Lisp_Object here_val;
810 if (NILP (object))
811 XSETBUFFER (object, current_buffer);
813 if (!NILP (limit))
814 CHECK_NUMBER_COERCE_MARKER (limit, 0);
816 i = validate_interval_range (object, &position, &position, soft);
818 /* Start with the interval containing the char before point. */
819 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
820 i = previous_interval (i);
822 if (NULL_INTERVAL_P (i))
823 return limit;
825 here_val = textget (i->plist, prop);
826 previous = previous_interval (i);
827 while (! NULL_INTERVAL_P (previous)
828 && EQ (here_val, textget (previous->plist, prop))
829 && (NILP (limit)
830 || previous->position + LENGTH (previous) > XFASTINT (limit)))
831 previous = previous_interval (previous);
832 if (NULL_INTERVAL_P (previous))
833 return limit;
834 if (!NILP (limit)
835 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
836 return limit;
838 XSETFASTINT (position, (previous->position + LENGTH (previous)
839 - (STRINGP (object))));
840 return position;
843 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
845 DEFUN ("add-text-properties", Fadd_text_properties,
846 Sadd_text_properties, 3, 4, 0,
847 "Add properties to the text from START to END.\n\
848 The third argument PROPERTIES is a property list\n\
849 specifying the property values to add.\n\
850 The optional fourth argument, OBJECT,\n\
851 is the string or buffer containing the text.\n\
852 Return t if any property value actually changed, nil otherwise.")
853 (start, end, properties, object)
854 Lisp_Object start, end, properties, object;
856 register INTERVAL i, unchanged;
857 register int s, len, modified = 0;
858 struct gcpro gcpro1;
860 properties = validate_plist (properties);
861 if (NILP (properties))
862 return Qnil;
864 if (NILP (object))
865 XSETBUFFER (object, current_buffer);
867 i = validate_interval_range (object, &start, &end, hard);
868 if (NULL_INTERVAL_P (i))
869 return Qnil;
871 s = XINT (start);
872 len = XINT (end) - s;
874 /* No need to protect OBJECT, because we GC only if it's a buffer,
875 and live buffers are always protected. */
876 GCPRO1 (properties);
878 /* If we're not starting on an interval boundary, we have to
879 split this interval. */
880 if (i->position != s)
882 /* If this interval already has the properties, we can
883 skip it. */
884 if (interval_has_all_properties (properties, i))
886 int got = (LENGTH (i) - (s - i->position));
887 if (got >= len)
888 RETURN_UNGCPRO (Qnil);
889 len -= got;
890 i = next_interval (i);
892 else
894 unchanged = i;
895 i = split_interval_right (unchanged, s - unchanged->position);
896 copy_properties (unchanged, i);
900 /* We are at the beginning of interval I, with LEN chars to scan. */
901 for (;;)
903 if (i == 0)
904 abort ();
906 if (LENGTH (i) >= len)
908 /* We can UNGCPRO safely here, because there will be just
909 one more chance to gc, in the next call to add_properties,
910 and after that we will not need PROPERTIES or OBJECT again. */
911 UNGCPRO;
913 if (interval_has_all_properties (properties, i))
914 return modified ? Qt : Qnil;
916 if (LENGTH (i) == len)
918 add_properties (properties, i, object);
919 return Qt;
922 /* i doesn't have the properties, and goes past the change limit */
923 unchanged = i;
924 i = split_interval_left (unchanged, len);
925 copy_properties (unchanged, i);
926 add_properties (properties, i, object);
927 return Qt;
930 len -= LENGTH (i);
931 modified += add_properties (properties, i, object);
932 i = next_interval (i);
936 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
938 DEFUN ("put-text-property", Fput_text_property,
939 Sput_text_property, 4, 5, 0,
940 "Set one property of the text from START to END.\n\
941 The third and fourth arguments PROPERTY and VALUE\n\
942 specify the property to add.\n\
943 The optional fifth argument, OBJECT,\n\
944 is the string or buffer containing the text.")
945 (start, end, property, value, object)
946 Lisp_Object start, end, property, value, object;
948 Fadd_text_properties (start, end,
949 Fcons (property, Fcons (value, Qnil)),
950 object);
951 return Qnil;
954 DEFUN ("set-text-properties", Fset_text_properties,
955 Sset_text_properties, 3, 4, 0,
956 "Completely replace properties of text from START to END.\n\
957 The third argument PROPERTIES is the new property list.\n\
958 The optional fourth argument, OBJECT,\n\
959 is the string or buffer containing the text.")
960 (start, end, properties, object)
961 Lisp_Object start, end, properties, object;
963 register INTERVAL i, unchanged;
964 register INTERVAL prev_changed = NULL_INTERVAL;
965 register int s, len;
966 Lisp_Object ostart, oend;
968 ostart = start;
969 oend = end;
971 properties = validate_plist (properties);
973 if (NILP (object))
974 XSETBUFFER (object, current_buffer);
976 /* If we want no properties for a whole string,
977 get rid of its intervals. */
978 if (NILP (properties) && STRINGP (object)
979 && XFASTINT (start) == 0
980 && XFASTINT (end) == XSTRING (object)->size)
982 XSTRING (object)->intervals = 0;
983 return Qt;
986 i = validate_interval_range (object, &start, &end, soft);
988 if (NULL_INTERVAL_P (i))
990 /* If buffer has no properties, and we want none, return now. */
991 if (NILP (properties))
992 return Qnil;
994 /* Restore the original START and END values
995 because validate_interval_range increments them for strings. */
996 start = ostart;
997 end = oend;
999 i = validate_interval_range (object, &start, &end, hard);
1000 /* This can return if start == end. */
1001 if (NULL_INTERVAL_P (i))
1002 return Qnil;
1005 s = XINT (start);
1006 len = XINT (end) - s;
1008 if (i->position != s)
1010 unchanged = i;
1011 i = split_interval_right (unchanged, s - unchanged->position);
1013 if (LENGTH (i) > len)
1015 copy_properties (unchanged, i);
1016 i = split_interval_left (i, len);
1017 set_properties (properties, i, object);
1018 return Qt;
1021 set_properties (properties, i, object);
1023 if (LENGTH (i) == len)
1024 return Qt;
1026 prev_changed = i;
1027 len -= LENGTH (i);
1028 i = next_interval (i);
1031 /* We are starting at the beginning of an interval, I */
1032 while (len > 0)
1034 if (i == 0)
1035 abort ();
1037 if (LENGTH (i) >= len)
1039 if (LENGTH (i) > len)
1040 i = split_interval_left (i, len);
1042 /* We have to call set_properties even if we are going to
1043 merge the intervals, so as to make the undo records
1044 and cause redisplay to happen. */
1045 set_properties (properties, i, object);
1046 if (!NULL_INTERVAL_P (prev_changed))
1047 merge_interval_left (i);
1048 return Qt;
1051 len -= LENGTH (i);
1053 /* We have to call set_properties even if we are going to
1054 merge the intervals, so as to make the undo records
1055 and cause redisplay to happen. */
1056 set_properties (properties, i, object);
1057 if (NULL_INTERVAL_P (prev_changed))
1058 prev_changed = i;
1059 else
1060 prev_changed = i = merge_interval_left (i);
1062 i = next_interval (i);
1065 return Qt;
1068 DEFUN ("remove-text-properties", Fremove_text_properties,
1069 Sremove_text_properties, 3, 4, 0,
1070 "Remove some properties from text from START to END.\n\
1071 The third argument PROPERTIES is a property list\n\
1072 whose property names specify the properties to remove.\n\
1073 \(The values stored in PROPERTIES are ignored.)\n\
1074 The optional fourth argument, OBJECT,\n\
1075 is the string or buffer containing the text.\n\
1076 Return t if any property was actually removed, nil otherwise.")
1077 (start, end, properties, object)
1078 Lisp_Object start, end, properties, object;
1080 register INTERVAL i, unchanged;
1081 register int s, len, modified = 0;
1083 if (NILP (object))
1084 XSETBUFFER (object, current_buffer);
1086 i = validate_interval_range (object, &start, &end, soft);
1087 if (NULL_INTERVAL_P (i))
1088 return Qnil;
1090 s = XINT (start);
1091 len = XINT (end) - s;
1093 if (i->position != s)
1095 /* No properties on this first interval -- return if
1096 it covers the entire region. */
1097 if (! interval_has_some_properties (properties, i))
1099 int got = (LENGTH (i) - (s - i->position));
1100 if (got >= len)
1101 return Qnil;
1102 len -= got;
1103 i = next_interval (i);
1105 /* Split away the beginning of this interval; what we don't
1106 want to modify. */
1107 else
1109 unchanged = i;
1110 i = split_interval_right (unchanged, s - unchanged->position);
1111 copy_properties (unchanged, i);
1115 /* We are at the beginning of an interval, with len to scan */
1116 for (;;)
1118 if (i == 0)
1119 abort ();
1121 if (LENGTH (i) >= len)
1123 if (! interval_has_some_properties (properties, i))
1124 return modified ? Qt : Qnil;
1126 if (LENGTH (i) == len)
1128 remove_properties (properties, i, object);
1129 return Qt;
1132 /* i has the properties, and goes past the change limit */
1133 unchanged = i;
1134 i = split_interval_left (i, len);
1135 copy_properties (unchanged, i);
1136 remove_properties (properties, i, object);
1137 return Qt;
1140 len -= LENGTH (i);
1141 modified += remove_properties (properties, i, object);
1142 i = next_interval (i);
1146 DEFUN ("text-property-any", Ftext_property_any,
1147 Stext_property_any, 4, 5, 0,
1148 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1149 If so, return the position of the first character whose property PROPERTY\n\
1150 is `eq' to VALUE. Otherwise return nil.\n\
1151 The optional fifth argument, OBJECT, is the string or buffer\n\
1152 containing the text.")
1153 (start, end, property, value, object)
1154 Lisp_Object start, end, property, value, object;
1156 register INTERVAL i;
1157 register int e, pos;
1159 if (NILP (object))
1160 XSETBUFFER (object, current_buffer);
1161 i = validate_interval_range (object, &start, &end, soft);
1162 if (NULL_INTERVAL_P (i))
1163 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1164 e = XINT (end);
1166 while (! NULL_INTERVAL_P (i))
1168 if (i->position >= e)
1169 break;
1170 if (EQ (textget (i->plist, property), value))
1172 pos = i->position;
1173 if (pos < XINT (start))
1174 pos = XINT (start);
1175 return make_number (pos - (STRINGP (object)));
1177 i = next_interval (i);
1179 return Qnil;
1182 DEFUN ("text-property-not-all", Ftext_property_not_all,
1183 Stext_property_not_all, 4, 5, 0,
1184 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1185 If so, return the position of the first character whose property PROPERTY\n\
1186 is not `eq' to VALUE. Otherwise, return nil.\n\
1187 The optional fifth argument, OBJECT, is the string or buffer\n\
1188 containing the text.")
1189 (start, end, property, value, object)
1190 Lisp_Object start, end, property, value, object;
1192 register INTERVAL i;
1193 register int s, e;
1195 if (NILP (object))
1196 XSETBUFFER (object, current_buffer);
1197 i = validate_interval_range (object, &start, &end, soft);
1198 if (NULL_INTERVAL_P (i))
1199 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1200 s = XINT (start);
1201 e = XINT (end);
1203 while (! NULL_INTERVAL_P (i))
1205 if (i->position >= e)
1206 break;
1207 if (! EQ (textget (i->plist, property), value))
1209 if (i->position > s)
1210 s = i->position;
1211 return make_number (s - (STRINGP (object)));
1213 i = next_interval (i);
1215 return Qnil;
1218 #if 0 /* You can use set-text-properties for this. */
1220 DEFUN ("erase-text-properties", Ferase_text_properties,
1221 Serase_text_properties, 2, 3, 0,
1222 "Remove all properties from the text from START to END.\n\
1223 The optional third argument, OBJECT,\n\
1224 is the string or buffer containing the text.")
1225 (start, end, object)
1226 Lisp_Object start, end, object;
1228 register INTERVAL i;
1229 register INTERVAL prev_changed = NULL_INTERVAL;
1230 register int s, len, modified;
1232 if (NILP (object))
1233 XSETBUFFER (object, current_buffer);
1235 i = validate_interval_range (object, &start, &end, soft);
1236 if (NULL_INTERVAL_P (i))
1237 return Qnil;
1239 s = XINT (start);
1240 len = XINT (end) - s;
1242 if (i->position != s)
1244 register int got;
1245 register INTERVAL unchanged = i;
1247 /* If there are properties here, then this text will be modified. */
1248 if (! NILP (i->plist))
1250 i = split_interval_right (unchanged, s - unchanged->position);
1251 i->plist = Qnil;
1252 modified++;
1254 if (LENGTH (i) > len)
1256 i = split_interval_right (i, len);
1257 copy_properties (unchanged, i);
1258 return Qt;
1261 if (LENGTH (i) == len)
1262 return Qt;
1264 got = LENGTH (i);
1266 /* If the text of I is without any properties, and contains
1267 LEN or more characters, then we may return without changing
1268 anything.*/
1269 else if (LENGTH (i) - (s - i->position) <= len)
1270 return Qnil;
1271 /* The amount of text to change extends past I, so just note
1272 how much we've gotten. */
1273 else
1274 got = LENGTH (i) - (s - i->position);
1276 len -= got;
1277 prev_changed = i;
1278 i = next_interval (i);
1281 /* We are starting at the beginning of an interval, I. */
1282 while (len > 0)
1284 if (LENGTH (i) >= len)
1286 /* If I has no properties, simply merge it if possible. */
1287 if (NILP (i->plist))
1289 if (! NULL_INTERVAL_P (prev_changed))
1290 merge_interval_left (i);
1292 return modified ? Qt : Qnil;
1295 if (LENGTH (i) > len)
1296 i = split_interval_left (i, len);
1297 if (! NULL_INTERVAL_P (prev_changed))
1298 merge_interval_left (i);
1299 else
1300 i->plist = Qnil;
1302 return Qt;
1305 /* Here if we still need to erase past the end of I */
1306 len -= LENGTH (i);
1307 if (NULL_INTERVAL_P (prev_changed))
1309 modified += erase_properties (i);
1310 prev_changed = i;
1312 else
1314 modified += ! NILP (i->plist);
1315 /* Merging I will give it the properties of PREV_CHANGED. */
1316 prev_changed = i = merge_interval_left (i);
1319 i = next_interval (i);
1322 return modified ? Qt : Qnil;
1324 #endif /* 0 */
1326 /* I don't think this is the right interface to export; how often do you
1327 want to do something like this, other than when you're copying objects
1328 around?
1330 I think it would be better to have a pair of functions, one which
1331 returns the text properties of a region as a list of ranges and
1332 plists, and another which applies such a list to another object. */
1334 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1335 SRC and DEST may each refer to strings or buffers.
1336 Optional sixth argument PROP causes only that property to be copied.
1337 Properties are copied to DEST as if by `add-text-properties'.
1338 Return t if any property value actually changed, nil otherwise. */
1340 /* Note this can GC when DEST is a buffer. */
1342 Lisp_Object
1343 copy_text_properties (start, end, src, pos, dest, prop)
1344 Lisp_Object start, end, src, pos, dest, prop;
1346 INTERVAL i;
1347 Lisp_Object res;
1348 Lisp_Object stuff;
1349 Lisp_Object plist;
1350 int s, e, e2, p, len, modified = 0;
1351 struct gcpro gcpro1, gcpro2;
1353 i = validate_interval_range (src, &start, &end, soft);
1354 if (NULL_INTERVAL_P (i))
1355 return Qnil;
1357 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1359 Lisp_Object dest_start, dest_end;
1361 dest_start = pos;
1362 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1363 /* Apply this to a copy of pos; it will try to increment its arguments,
1364 which we don't want. */
1365 validate_interval_range (dest, &dest_start, &dest_end, soft);
1368 s = XINT (start);
1369 e = XINT (end);
1370 p = XINT (pos);
1372 stuff = Qnil;
1374 while (s < e)
1376 e2 = i->position + LENGTH (i);
1377 if (e2 > e)
1378 e2 = e;
1379 len = e2 - s;
1381 plist = i->plist;
1382 if (! NILP (prop))
1383 while (! NILP (plist))
1385 if (EQ (Fcar (plist), prop))
1387 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1388 break;
1390 plist = Fcdr (Fcdr (plist));
1392 if (! NILP (plist))
1394 /* Must defer modifications to the interval tree in case src
1395 and dest refer to the same string or buffer. */
1396 stuff = Fcons (Fcons (make_number (p),
1397 Fcons (make_number (p + len),
1398 Fcons (plist, Qnil))),
1399 stuff);
1402 i = next_interval (i);
1403 if (NULL_INTERVAL_P (i))
1404 break;
1406 p += len;
1407 s = i->position;
1410 GCPRO2 (stuff, dest);
1412 while (! NILP (stuff))
1414 res = Fcar (stuff);
1415 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1416 Fcar (Fcdr (Fcdr (res))), dest);
1417 if (! NILP (res))
1418 modified++;
1419 stuff = Fcdr (stuff);
1422 UNGCPRO;
1424 return modified ? Qt : Qnil;
1427 /* Call the modification hook functions in LIST, each with START and END. */
1429 static void
1430 call_mod_hooks (list, start, end)
1431 Lisp_Object list, start, end;
1433 struct gcpro gcpro1;
1434 GCPRO1 (list);
1435 while (!NILP (list))
1437 call2 (Fcar (list), start, end);
1438 list = Fcdr (list);
1440 UNGCPRO;
1443 /* Check for read-only intervals and signal an error if we find one.
1444 Then check for any modification hooks in the range START up to
1445 (but not including) END. Create a list of all these hooks in
1446 lexicographic order, eliminating consecutive extra copies of the
1447 same hook. Then call those hooks in order, with START and END - 1
1448 as arguments. */
1450 void
1451 verify_interval_modification (buf, start, end)
1452 struct buffer *buf;
1453 int start, end;
1455 register INTERVAL intervals = BUF_INTERVALS (buf);
1456 register INTERVAL i, prev;
1457 Lisp_Object hooks;
1458 register Lisp_Object prev_mod_hooks;
1459 Lisp_Object mod_hooks;
1460 struct gcpro gcpro1;
1462 hooks = Qnil;
1463 prev_mod_hooks = Qnil;
1464 mod_hooks = Qnil;
1466 interval_insert_behind_hooks = Qnil;
1467 interval_insert_in_front_hooks = Qnil;
1469 if (NULL_INTERVAL_P (intervals))
1470 return;
1472 if (start > end)
1474 int temp = start;
1475 start = end;
1476 end = temp;
1479 /* For an insert operation, check the two chars around the position. */
1480 if (start == end)
1482 INTERVAL prev;
1483 Lisp_Object before, after;
1485 /* Set I to the interval containing the char after START,
1486 and PREV to the interval containing the char before START.
1487 Either one may be null. They may be equal. */
1488 i = find_interval (intervals, start);
1490 if (start == BUF_BEGV (buf))
1491 prev = 0;
1492 else if (i->position == start)
1493 prev = previous_interval (i);
1494 else if (i->position < start)
1495 prev = i;
1496 if (start == BUF_ZV (buf))
1497 i = 0;
1499 /* If Vinhibit_read_only is set and is not a list, we can
1500 skip the read_only checks. */
1501 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1503 /* If I and PREV differ we need to check for the read-only
1504 property together with its stickiness. If either I or
1505 PREV are 0, this check is all we need.
1506 We have to take special care, since read-only may be
1507 indirectly defined via the category property. */
1508 if (i != prev)
1510 if (! NULL_INTERVAL_P (i))
1512 after = textget (i->plist, Qread_only);
1514 /* If interval I is read-only and read-only is
1515 front-sticky, inhibit insertion.
1516 Check for read-only as well as category. */
1517 if (! NILP (after)
1518 && NILP (Fmemq (after, Vinhibit_read_only)))
1520 Lisp_Object tem;
1522 tem = textget (i->plist, Qfront_sticky);
1523 if (TMEM (Qread_only, tem)
1524 || (NILP (Fplist_get (i->plist, Qread_only))
1525 && TMEM (Qcategory, tem)))
1526 error ("Attempt to insert within read-only text");
1530 if (! NULL_INTERVAL_P (prev))
1532 before = textget (prev->plist, Qread_only);
1534 /* If interval PREV is read-only and read-only isn't
1535 rear-nonsticky, inhibit insertion.
1536 Check for read-only as well as category. */
1537 if (! NILP (before)
1538 && NILP (Fmemq (before, Vinhibit_read_only)))
1540 Lisp_Object tem;
1542 tem = textget (prev->plist, Qrear_nonsticky);
1543 if (! TMEM (Qread_only, tem)
1544 && (! NILP (Fplist_get (prev->plist,Qread_only))
1545 || ! TMEM (Qcategory, tem)))
1546 error ("Attempt to insert within read-only text");
1550 else if (! NULL_INTERVAL_P (i))
1552 after = textget (i->plist, Qread_only);
1554 /* If interval I is read-only and read-only is
1555 front-sticky, inhibit insertion.
1556 Check for read-only as well as category. */
1557 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1559 Lisp_Object tem;
1561 tem = textget (i->plist, Qfront_sticky);
1562 if (TMEM (Qread_only, tem)
1563 || (NILP (Fplist_get (i->plist, Qread_only))
1564 && TMEM (Qcategory, tem)))
1565 error ("Attempt to insert within read-only text");
1567 tem = textget (prev->plist, Qrear_nonsticky);
1568 if (! TMEM (Qread_only, tem)
1569 && (! NILP (Fplist_get (prev->plist, Qread_only))
1570 || ! TMEM (Qcategory, tem)))
1571 error ("Attempt to insert within read-only text");
1576 /* Run both insert hooks (just once if they're the same). */
1577 if (!NULL_INTERVAL_P (prev))
1578 interval_insert_behind_hooks
1579 = textget (prev->plist, Qinsert_behind_hooks);
1580 if (!NULL_INTERVAL_P (i))
1581 interval_insert_in_front_hooks
1582 = textget (i->plist, Qinsert_in_front_hooks);
1584 else
1586 /* Loop over intervals on or next to START...END,
1587 collecting their hooks. */
1589 i = find_interval (intervals, start);
1592 if (! INTERVAL_WRITABLE_P (i))
1593 error ("Attempt to modify read-only text");
1595 mod_hooks = textget (i->plist, Qmodification_hooks);
1596 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1598 hooks = Fcons (mod_hooks, hooks);
1599 prev_mod_hooks = mod_hooks;
1602 i = next_interval (i);
1604 /* Keep going thru the interval containing the char before END. */
1605 while (! NULL_INTERVAL_P (i) && i->position < end);
1607 GCPRO1 (hooks);
1608 hooks = Fnreverse (hooks);
1609 while (! EQ (hooks, Qnil))
1611 call_mod_hooks (Fcar (hooks), make_number (start),
1612 make_number (end));
1613 hooks = Fcdr (hooks);
1615 UNGCPRO;
1619 /* Run the interval hooks for an insertion.
1620 verify_interval_modification chose which hooks to run;
1621 this function is called after the insertion happens
1622 so it can indicate the range of inserted text. */
1624 void
1625 report_interval_modification (start, end)
1626 Lisp_Object start, end;
1628 if (! NILP (interval_insert_behind_hooks))
1629 call_mod_hooks (interval_insert_behind_hooks,
1630 make_number (start), make_number (end));
1631 if (! NILP (interval_insert_in_front_hooks)
1632 && ! EQ (interval_insert_in_front_hooks,
1633 interval_insert_behind_hooks))
1634 call_mod_hooks (interval_insert_in_front_hooks,
1635 make_number (start), make_number (end));
1638 void
1639 syms_of_textprop ()
1641 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
1642 "Property-list used as default values.\n\
1643 The value of a property in this list is seen as the value for every\n\
1644 character that does not have its own value for that property.");
1645 Vdefault_text_properties = Qnil;
1647 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1648 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1649 This also inhibits the use of the `intangible' text property.");
1650 Vinhibit_point_motion_hooks = Qnil;
1652 staticpro (&interval_insert_behind_hooks);
1653 staticpro (&interval_insert_in_front_hooks);
1654 interval_insert_behind_hooks = Qnil;
1655 interval_insert_in_front_hooks = Qnil;
1658 /* Common attributes one might give text */
1660 staticpro (&Qforeground);
1661 Qforeground = intern ("foreground");
1662 staticpro (&Qbackground);
1663 Qbackground = intern ("background");
1664 staticpro (&Qfont);
1665 Qfont = intern ("font");
1666 staticpro (&Qstipple);
1667 Qstipple = intern ("stipple");
1668 staticpro (&Qunderline);
1669 Qunderline = intern ("underline");
1670 staticpro (&Qread_only);
1671 Qread_only = intern ("read-only");
1672 staticpro (&Qinvisible);
1673 Qinvisible = intern ("invisible");
1674 staticpro (&Qintangible);
1675 Qintangible = intern ("intangible");
1676 staticpro (&Qcategory);
1677 Qcategory = intern ("category");
1678 staticpro (&Qlocal_map);
1679 Qlocal_map = intern ("local-map");
1680 staticpro (&Qfront_sticky);
1681 Qfront_sticky = intern ("front-sticky");
1682 staticpro (&Qrear_nonsticky);
1683 Qrear_nonsticky = intern ("rear-nonsticky");
1685 /* Properties that text might use to specify certain actions */
1687 staticpro (&Qmouse_left);
1688 Qmouse_left = intern ("mouse-left");
1689 staticpro (&Qmouse_entered);
1690 Qmouse_entered = intern ("mouse-entered");
1691 staticpro (&Qpoint_left);
1692 Qpoint_left = intern ("point-left");
1693 staticpro (&Qpoint_entered);
1694 Qpoint_entered = intern ("point-entered");
1696 defsubr (&Stext_properties_at);
1697 defsubr (&Sget_text_property);
1698 defsubr (&Sget_char_property);
1699 defsubr (&Snext_property_change);
1700 defsubr (&Snext_single_property_change);
1701 defsubr (&Sprevious_property_change);
1702 defsubr (&Sprevious_single_property_change);
1703 defsubr (&Sadd_text_properties);
1704 defsubr (&Sput_text_property);
1705 defsubr (&Sset_text_properties);
1706 defsubr (&Sremove_text_properties);
1707 defsubr (&Stext_property_any);
1708 defsubr (&Stext_property_not_all);
1709 /* defsubr (&Serase_text_properties); */
1710 /* defsubr (&Scopy_text_properties); */
1713 #else
1715 lose -- this shouldn't be compiled if USE_TEXT_PROPERTIES isn't defined
1717 #endif /* USE_TEXT_PROPERTIES */