(speedbar-update-current-file): Added call to
[emacs.git] / src / textprop.c
blobce2c7530d8975286014c3e3571516158522f6fb2
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997 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 XSETFASTINT (*begin, XFASTINT (*begin));
154 if (begin != end)
155 XSETFASTINT (*end, XFASTINT (*end));
156 i = s->intervals;
158 if (s->size == 0)
159 return NULL_INTERVAL;
161 searchpos = XINT (*begin);
164 if (NULL_INTERVAL_P (i))
165 return (force ? create_root_interval (object) : i);
167 return find_interval (i, searchpos);
170 /* Validate LIST as a property list. If LIST is not a list, then
171 make one consisting of (LIST nil). Otherwise, verify that LIST
172 is even numbered and thus suitable as a plist. */
174 static Lisp_Object
175 validate_plist (list)
176 Lisp_Object list;
178 if (NILP (list))
179 return Qnil;
181 if (CONSP (list))
183 register int i;
184 register Lisp_Object tail;
185 for (i = 0, tail = list; !NILP (tail); i++)
187 tail = Fcdr (tail);
188 QUIT;
190 if (i & 1)
191 error ("Odd length text property list");
192 return list;
195 return Fcons (list, Fcons (Qnil, Qnil));
198 /* Return nonzero if interval I has all the properties,
199 with the same values, of list PLIST. */
201 static int
202 interval_has_all_properties (plist, i)
203 Lisp_Object plist;
204 INTERVAL i;
206 register Lisp_Object tail1, tail2, sym1, sym2;
207 register int found;
209 /* Go through each element of PLIST. */
210 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
212 sym1 = Fcar (tail1);
213 found = 0;
215 /* Go through I's plist, looking for sym1 */
216 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
217 if (EQ (sym1, Fcar (tail2)))
219 /* Found the same property on both lists. If the
220 values are unequal, return zero. */
221 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
222 return 0;
224 /* Property has same value on both lists; go to next one. */
225 found = 1;
226 break;
229 if (! found)
230 return 0;
233 return 1;
236 /* Return nonzero if the plist of interval I has any of the
237 properties of PLIST, regardless of their values. */
239 static INLINE int
240 interval_has_some_properties (plist, i)
241 Lisp_Object plist;
242 INTERVAL i;
244 register Lisp_Object tail1, tail2, sym;
246 /* Go through each element of PLIST. */
247 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
249 sym = Fcar (tail1);
251 /* Go through i's plist, looking for tail1 */
252 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
253 if (EQ (sym, Fcar (tail2)))
254 return 1;
257 return 0;
260 /* Changing the plists of individual intervals. */
262 /* Return the value of PROP in property-list PLIST, or Qunbound if it
263 has none. */
264 static Lisp_Object
265 property_value (plist, prop)
266 Lisp_Object plist, prop;
268 Lisp_Object value;
270 while (PLIST_ELT_P (plist, value))
271 if (EQ (XCONS (plist)->car, prop))
272 return XCONS (value)->car;
273 else
274 plist = XCONS (value)->cdr;
276 return Qunbound;
279 /* Set the properties of INTERVAL to PROPERTIES,
280 and record undo info for the previous values.
281 OBJECT is the string or buffer that INTERVAL belongs to. */
283 static void
284 set_properties (properties, interval, object)
285 Lisp_Object properties, object;
286 INTERVAL interval;
288 Lisp_Object sym, value;
290 if (BUFFERP (object))
292 /* For each property in the old plist which is missing from PROPERTIES,
293 or has a different value in PROPERTIES, make an undo record. */
294 for (sym = interval->plist;
295 PLIST_ELT_P (sym, value);
296 sym = XCONS (value)->cdr)
297 if (! EQ (property_value (properties, XCONS (sym)->car),
298 XCONS (value)->car))
300 record_property_change (interval->position, LENGTH (interval),
301 XCONS (sym)->car, XCONS (value)->car,
302 object);
305 /* For each new property that has no value at all in the old plist,
306 make an undo record binding it to nil, so it will be removed. */
307 for (sym = properties;
308 PLIST_ELT_P (sym, value);
309 sym = XCONS (value)->cdr)
310 if (EQ (property_value (interval->plist, XCONS (sym)->car), Qunbound))
312 record_property_change (interval->position, LENGTH (interval),
313 XCONS (sym)->car, Qnil,
314 object);
318 /* Store new properties. */
319 interval->plist = Fcopy_sequence (properties);
322 /* Add the properties of PLIST to the interval I, or set
323 the value of I's property to the value of the property on PLIST
324 if they are different.
326 OBJECT should be the string or buffer the interval is in.
328 Return nonzero if this changes I (i.e., if any members of PLIST
329 are actually added to I's plist) */
331 static int
332 add_properties (plist, i, object)
333 Lisp_Object plist;
334 INTERVAL i;
335 Lisp_Object object;
337 Lisp_Object tail1, tail2, sym1, val1;
338 register int changed = 0;
339 register int found;
340 struct gcpro gcpro1, gcpro2, gcpro3;
342 tail1 = plist;
343 sym1 = Qnil;
344 val1 = Qnil;
345 /* No need to protect OBJECT, because we can GC only in the case
346 where it is a buffer, and live buffers are always protected.
347 I and its plist are also protected, via OBJECT. */
348 GCPRO3 (tail1, sym1, val1);
350 /* Go through each element of PLIST. */
351 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
353 sym1 = Fcar (tail1);
354 val1 = Fcar (Fcdr (tail1));
355 found = 0;
357 /* Go through I's plist, looking for sym1 */
358 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
359 if (EQ (sym1, Fcar (tail2)))
361 /* No need to gcpro, because tail2 protects this
362 and it must be a cons cell (we get an error otherwise). */
363 register Lisp_Object this_cdr;
365 this_cdr = Fcdr (tail2);
366 /* Found the property. Now check its value. */
367 found = 1;
369 /* The properties have the same value on both lists.
370 Continue to the next property. */
371 if (EQ (val1, Fcar (this_cdr)))
372 break;
374 /* Record this change in the buffer, for undo purposes. */
375 if (BUFFERP (object))
377 record_property_change (i->position, LENGTH (i),
378 sym1, Fcar (this_cdr), object);
381 /* I's property has a different value -- change it */
382 Fsetcar (this_cdr, val1);
383 changed++;
384 break;
387 if (! found)
389 /* Record this change in the buffer, for undo purposes. */
390 if (BUFFERP (object))
392 record_property_change (i->position, LENGTH (i),
393 sym1, Qnil, object);
395 i->plist = Fcons (sym1, Fcons (val1, i->plist));
396 changed++;
400 UNGCPRO;
402 return changed;
405 /* For any members of PLIST which are properties of I, remove them
406 from I's plist.
407 OBJECT is the string or buffer containing I. */
409 static int
410 remove_properties (plist, i, object)
411 Lisp_Object plist;
412 INTERVAL i;
413 Lisp_Object object;
415 register Lisp_Object tail1, tail2, sym, current_plist;
416 register int changed = 0;
418 current_plist = i->plist;
419 /* Go through each element of plist. */
420 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
422 sym = Fcar (tail1);
424 /* First, remove the symbol if its at the head of the list */
425 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
427 if (BUFFERP (object))
429 record_property_change (i->position, LENGTH (i),
430 sym, Fcar (Fcdr (current_plist)),
431 object);
434 current_plist = Fcdr (Fcdr (current_plist));
435 changed++;
438 /* Go through i's plist, looking for sym */
439 tail2 = current_plist;
440 while (! NILP (tail2))
442 register Lisp_Object this;
443 this = Fcdr (Fcdr (tail2));
444 if (EQ (sym, Fcar (this)))
446 if (BUFFERP (object))
448 record_property_change (i->position, LENGTH (i),
449 sym, Fcar (Fcdr (this)), object);
452 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
453 changed++;
455 tail2 = this;
459 if (changed)
460 i->plist = current_plist;
461 return changed;
464 #if 0
465 /* Remove all properties from interval I. Return non-zero
466 if this changes the interval. */
468 static INLINE int
469 erase_properties (i)
470 INTERVAL i;
472 if (NILP (i->plist))
473 return 0;
475 i->plist = Qnil;
476 return 1;
478 #endif
480 /* Returns the interval of POSITION in OBJECT.
481 POSITION is BEG-based. */
483 INTERVAL
484 interval_of (position, object)
485 int position;
486 Lisp_Object object;
488 register INTERVAL i;
489 int beg, end;
491 if (NILP (object))
492 XSETBUFFER (object, current_buffer);
493 else if (EQ (object, Qt))
494 return NULL_INTERVAL;
496 CHECK_STRING_OR_BUFFER (object, 0);
498 if (BUFFERP (object))
500 register struct buffer *b = XBUFFER (object);
502 beg = BUF_BEGV (b);
503 end = BUF_ZV (b);
504 i = BUF_INTERVALS (b);
506 else
508 register struct Lisp_String *s = XSTRING (object);
510 beg = 0;
511 end = s->size;
512 i = s->intervals;
515 if (!(beg <= position && position <= end))
516 args_out_of_range (make_number (position), make_number (position));
517 if (beg == end || NULL_INTERVAL_P (i))
518 return NULL_INTERVAL;
520 return find_interval (i, position);
523 DEFUN ("text-properties-at", Ftext_properties_at,
524 Stext_properties_at, 1, 2, 0,
525 "Return the list of properties of the character at POSITION in OBJECT.\n\
526 OBJECT is the string or buffer to look for the properties in;\n\
527 nil means the current buffer.\n\
528 If POSITION is at the end of OBJECT, the value is nil.")
529 (position, object)
530 Lisp_Object position, object;
532 register INTERVAL i;
534 if (NILP (object))
535 XSETBUFFER (object, current_buffer);
537 i = validate_interval_range (object, &position, &position, soft);
538 if (NULL_INTERVAL_P (i))
539 return Qnil;
540 /* If POSITION is at the end of the interval,
541 it means it's the end of OBJECT.
542 There are no properties at the very end,
543 since no character follows. */
544 if (XINT (position) == LENGTH (i) + i->position)
545 return Qnil;
547 return i->plist;
550 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
551 "Return the value of POSITION's property PROP, in OBJECT.\n\
552 OBJECT is optional and defaults to the current buffer.\n\
553 If POSITION is at the end of OBJECT, the value is nil.")
554 (position, prop, object)
555 Lisp_Object position, object;
556 Lisp_Object prop;
558 return textget (Ftext_properties_at (position, object), prop);
561 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
562 "Return the value of POSITION's property PROP, in OBJECT.\n\
563 OBJECT is optional and defaults to the current buffer.\n\
564 If POSITION is at the end of OBJECT, the value is nil.\n\
565 If OBJECT is a buffer, then overlay properties are considered as well as\n\
566 text properties.\n\
567 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
568 overlays are considered only if they are associated with OBJECT.")
569 (position, prop, object)
570 Lisp_Object position, object;
571 register Lisp_Object prop;
573 struct window *w = 0;
575 CHECK_NUMBER_COERCE_MARKER (position, 0);
577 if (NILP (object))
578 XSETBUFFER (object, current_buffer);
580 if (WINDOWP (object))
582 w = XWINDOW (object);
583 object = w->buffer;
585 if (BUFFERP (object))
587 int posn = XINT (position);
588 int noverlays;
589 Lisp_Object *overlay_vec, tem;
590 int next_overlay;
591 int len;
592 struct buffer *obuf = current_buffer;
594 set_buffer_temp (XBUFFER (object));
596 /* First try with room for 40 overlays. */
597 len = 40;
598 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
600 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
601 &next_overlay, NULL);
603 /* If there are more than 40,
604 make enough space for all, and try again. */
605 if (noverlays > len)
607 len = noverlays;
608 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
609 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
610 &next_overlay, NULL);
612 noverlays = sort_overlays (overlay_vec, noverlays, w);
614 set_buffer_temp (obuf);
616 /* Now check the overlays in order of decreasing priority. */
617 while (--noverlays >= 0)
619 tem = Foverlay_get (overlay_vec[noverlays], prop);
620 if (!NILP (tem))
621 return (tem);
624 /* Not a buffer, or no appropriate overlay, so fall through to the
625 simpler case. */
626 return (Fget_text_property (position, prop, object));
629 DEFUN ("next-char-property-change", Fnext_char_property_change,
630 Snext_char_property_change, 1, 2, 0,
631 "Return the position of next text property or overlay change.\n\
632 This scans characters forward from POSITION in OBJECT till it finds\n\
633 a change in some text property, or the beginning or end of an overlay,\n\
634 and returns the position of that.\n\
635 If none is found, the function returns (point-max).\n\
637 If the optional third argument LIMIT is non-nil, don't search\n\
638 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
639 (position, limit)
640 Lisp_Object position, limit;
642 Lisp_Object temp;
644 temp = Fnext_overlay_change (position);
645 if (! NILP (limit))
647 CHECK_NUMBER (limit, 2);
648 if (XINT (limit) < XINT (temp))
649 temp = limit;
651 return Fnext_property_change (position, Qnil, temp);
654 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
655 Sprevious_char_property_change, 1, 2, 0,
656 "Return the position of previous text property or overlay change.\n\
657 Scans characters backward from POSITION in OBJECT till it finds\n\
658 a change in some text property, or the beginning or end of an overlay,\n\
659 and returns the position of that.\n\
660 If none is found, the function returns (point-max).\n\
662 If the optional third argument LIMIT is non-nil, don't search\n\
663 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
664 (position, limit)
665 Lisp_Object position, limit;
667 Lisp_Object temp;
669 temp = Fprevious_overlay_change (position);
670 if (! NILP (limit))
672 CHECK_NUMBER (limit, 2);
673 if (XINT (limit) > XINT (temp))
674 temp = limit;
676 return Fprevious_property_change (position, Qnil, temp);
679 DEFUN ("next-property-change", Fnext_property_change,
680 Snext_property_change, 1, 3, 0,
681 "Return the position of next property change.\n\
682 Scans characters forward from POSITION in OBJECT till it finds\n\
683 a change in some text property, then returns the position of the change.\n\
684 The optional second argument OBJECT is the string or buffer to scan.\n\
685 Return nil if the property is constant all the way to the end of OBJECT.\n\
686 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
687 If the optional third argument LIMIT is non-nil, don't search\n\
688 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
689 (position, object, limit)
690 Lisp_Object position, object, limit;
692 register INTERVAL i, next;
694 if (NILP (object))
695 XSETBUFFER (object, current_buffer);
697 if (! NILP (limit) && ! EQ (limit, Qt))
698 CHECK_NUMBER_COERCE_MARKER (limit, 0);
700 i = validate_interval_range (object, &position, &position, soft);
702 /* If LIMIT is t, return start of next interval--don't
703 bother checking further intervals. */
704 if (EQ (limit, Qt))
706 if (NULL_INTERVAL_P (i))
707 next = i;
708 else
709 next = next_interval (i);
711 if (NULL_INTERVAL_P (next))
712 XSETFASTINT (position, (STRINGP (object)
713 ? XSTRING (object)->size
714 : BUF_ZV (XBUFFER (object))));
715 else
716 XSETFASTINT (position, next->position);
717 return position;
720 if (NULL_INTERVAL_P (i))
721 return limit;
723 next = next_interval (i);
725 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
726 && (NILP (limit) || next->position < XFASTINT (limit)))
727 next = next_interval (next);
729 if (NULL_INTERVAL_P (next))
730 return limit;
731 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
732 return limit;
734 XSETFASTINT (position, next->position);
735 return position;
738 /* Return 1 if there's a change in some property between BEG and END. */
741 property_change_between_p (beg, end)
742 int beg, end;
744 register INTERVAL i, next;
745 Lisp_Object object, pos;
747 XSETBUFFER (object, current_buffer);
748 XSETFASTINT (pos, beg);
750 i = validate_interval_range (object, &pos, &pos, soft);
751 if (NULL_INTERVAL_P (i))
752 return 0;
754 next = next_interval (i);
755 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
757 next = next_interval (next);
758 if (NULL_INTERVAL_P (next))
759 return 0;
760 if (next->position >= end)
761 return 0;
764 if (NULL_INTERVAL_P (next))
765 return 0;
767 return 1;
770 DEFUN ("next-single-property-change", Fnext_single_property_change,
771 Snext_single_property_change, 2, 4, 0,
772 "Return the position of next property change for a specific property.\n\
773 Scans characters forward from POSITION till it finds\n\
774 a change in the PROP property, then returns the position of the change.\n\
775 The optional third argument OBJECT is the string or buffer to scan.\n\
776 The property values are compared with `eq'.\n\
777 Return nil if the property is constant all the way to the end of OBJECT.\n\
778 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
779 If the optional fourth argument LIMIT is non-nil, don't search\n\
780 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
781 (position, prop, object, limit)
782 Lisp_Object position, prop, object, limit;
784 register INTERVAL i, next;
785 register Lisp_Object here_val;
787 if (NILP (object))
788 XSETBUFFER (object, current_buffer);
790 if (!NILP (limit))
791 CHECK_NUMBER_COERCE_MARKER (limit, 0);
793 i = validate_interval_range (object, &position, &position, soft);
794 if (NULL_INTERVAL_P (i))
795 return limit;
797 here_val = textget (i->plist, prop);
798 next = next_interval (i);
799 while (! NULL_INTERVAL_P (next)
800 && EQ (here_val, textget (next->plist, prop))
801 && (NILP (limit) || next->position < XFASTINT (limit)))
802 next = next_interval (next);
804 if (NULL_INTERVAL_P (next))
805 return limit;
806 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
807 return limit;
809 return make_number (next->position);
812 DEFUN ("previous-property-change", Fprevious_property_change,
813 Sprevious_property_change, 1, 3, 0,
814 "Return the position of previous property change.\n\
815 Scans characters backwards from POSITION in OBJECT till it finds\n\
816 a change in some text property, then returns the position of the change.\n\
817 The optional second argument OBJECT is the string or buffer to scan.\n\
818 Return nil if the property is constant all the way to the start of OBJECT.\n\
819 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
820 If the optional third argument LIMIT is non-nil, don't search\n\
821 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
822 (position, object, limit)
823 Lisp_Object position, object, limit;
825 register INTERVAL i, previous;
827 if (NILP (object))
828 XSETBUFFER (object, current_buffer);
830 if (!NILP (limit))
831 CHECK_NUMBER_COERCE_MARKER (limit, 0);
833 i = validate_interval_range (object, &position, &position, soft);
834 if (NULL_INTERVAL_P (i))
835 return limit;
837 /* Start with the interval containing the char before point. */
838 if (i->position == XFASTINT (position))
839 i = previous_interval (i);
841 previous = previous_interval (i);
842 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
843 && (NILP (limit)
844 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
845 previous = previous_interval (previous);
846 if (NULL_INTERVAL_P (previous))
847 return limit;
848 if (!NILP (limit)
849 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
850 return limit;
852 return make_number (previous->position + LENGTH (previous));
855 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
856 Sprevious_single_property_change, 2, 4, 0,
857 "Return the position of previous property change for a specific property.\n\
858 Scans characters backward from POSITION till it finds\n\
859 a change in the PROP property, then returns the position of the change.\n\
860 The optional third argument OBJECT is the string or buffer to scan.\n\
861 The property values are compared with `eq'.\n\
862 Return nil if the property is constant all the way to the start of OBJECT.\n\
863 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
864 If the optional fourth argument LIMIT is non-nil, don't search\n\
865 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
866 (position, prop, object, limit)
867 Lisp_Object position, prop, object, limit;
869 register INTERVAL i, previous;
870 register Lisp_Object here_val;
872 if (NILP (object))
873 XSETBUFFER (object, current_buffer);
875 if (!NILP (limit))
876 CHECK_NUMBER_COERCE_MARKER (limit, 0);
878 i = validate_interval_range (object, &position, &position, soft);
880 /* Start with the interval containing the char before point. */
881 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
882 i = previous_interval (i);
884 if (NULL_INTERVAL_P (i))
885 return limit;
887 here_val = textget (i->plist, prop);
888 previous = previous_interval (i);
889 while (! NULL_INTERVAL_P (previous)
890 && EQ (here_val, textget (previous->plist, prop))
891 && (NILP (limit)
892 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
893 previous = previous_interval (previous);
894 if (NULL_INTERVAL_P (previous))
895 return limit;
896 if (!NILP (limit)
897 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
898 return limit;
900 return make_number (previous->position + LENGTH (previous));
903 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
905 DEFUN ("add-text-properties", Fadd_text_properties,
906 Sadd_text_properties, 3, 4, 0,
907 "Add properties to the text from START to END.\n\
908 The third argument PROPERTIES is a property list\n\
909 specifying the property values to add.\n\
910 The optional fourth argument, OBJECT,\n\
911 is the string or buffer containing the text.\n\
912 Return t if any property value actually changed, nil otherwise.")
913 (start, end, properties, object)
914 Lisp_Object start, end, properties, object;
916 register INTERVAL i, unchanged;
917 register int s, len, modified = 0;
918 struct gcpro gcpro1;
920 properties = validate_plist (properties);
921 if (NILP (properties))
922 return Qnil;
924 if (NILP (object))
925 XSETBUFFER (object, current_buffer);
927 i = validate_interval_range (object, &start, &end, hard);
928 if (NULL_INTERVAL_P (i))
929 return Qnil;
931 s = XINT (start);
932 len = XINT (end) - s;
934 /* No need to protect OBJECT, because we GC only if it's a buffer,
935 and live buffers are always protected. */
936 GCPRO1 (properties);
938 /* If we're not starting on an interval boundary, we have to
939 split this interval. */
940 if (i->position != s)
942 /* If this interval already has the properties, we can
943 skip it. */
944 if (interval_has_all_properties (properties, i))
946 int got = (LENGTH (i) - (s - i->position));
947 if (got >= len)
948 RETURN_UNGCPRO (Qnil);
949 len -= got;
950 i = next_interval (i);
952 else
954 unchanged = i;
955 i = split_interval_right (unchanged, s - unchanged->position);
956 copy_properties (unchanged, i);
960 if (BUFFERP (object))
961 modify_region (XBUFFER (object), XINT (start), XINT (end));
963 /* We are at the beginning of interval I, with LEN chars to scan. */
964 for (;;)
966 if (i == 0)
967 abort ();
969 if (LENGTH (i) >= len)
971 /* We can UNGCPRO safely here, because there will be just
972 one more chance to gc, in the next call to add_properties,
973 and after that we will not need PROPERTIES or OBJECT again. */
974 UNGCPRO;
976 if (interval_has_all_properties (properties, i))
978 if (BUFFERP (object))
979 signal_after_change (XINT (start), XINT (end) - XINT (start),
980 XINT (end) - XINT (start));
982 return modified ? Qt : Qnil;
985 if (LENGTH (i) == len)
987 add_properties (properties, i, object);
988 if (BUFFERP (object))
989 signal_after_change (XINT (start), XINT (end) - XINT (start),
990 XINT (end) - XINT (start));
991 return Qt;
994 /* i doesn't have the properties, and goes past the change limit */
995 unchanged = i;
996 i = split_interval_left (unchanged, len);
997 copy_properties (unchanged, i);
998 add_properties (properties, i, object);
999 if (BUFFERP (object))
1000 signal_after_change (XINT (start), XINT (end) - XINT (start),
1001 XINT (end) - XINT (start));
1002 return Qt;
1005 len -= LENGTH (i);
1006 modified += add_properties (properties, i, object);
1007 i = next_interval (i);
1011 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1013 DEFUN ("put-text-property", Fput_text_property,
1014 Sput_text_property, 4, 5, 0,
1015 "Set one property of the text from START to END.\n\
1016 The third and fourth arguments PROPERTY and VALUE\n\
1017 specify the property to add.\n\
1018 The optional fifth argument, OBJECT,\n\
1019 is the string or buffer containing the text.")
1020 (start, end, property, value, object)
1021 Lisp_Object start, end, property, value, object;
1023 Fadd_text_properties (start, end,
1024 Fcons (property, Fcons (value, Qnil)),
1025 object);
1026 return Qnil;
1029 DEFUN ("set-text-properties", Fset_text_properties,
1030 Sset_text_properties, 3, 4, 0,
1031 "Completely replace properties of text from START to END.\n\
1032 The third argument PROPERTIES is the new property list.\n\
1033 The optional fourth argument, OBJECT,\n\
1034 is the string or buffer containing the text.")
1035 (start, end, properties, object)
1036 Lisp_Object start, end, properties, object;
1038 register INTERVAL i, unchanged;
1039 register INTERVAL prev_changed = NULL_INTERVAL;
1040 register int s, len;
1041 Lisp_Object ostart, oend;
1042 int have_modified = 0;
1044 ostart = start;
1045 oend = end;
1047 properties = validate_plist (properties);
1049 if (NILP (object))
1050 XSETBUFFER (object, current_buffer);
1052 /* If we want no properties for a whole string,
1053 get rid of its intervals. */
1054 if (NILP (properties) && STRINGP (object)
1055 && XFASTINT (start) == 0
1056 && XFASTINT (end) == XSTRING (object)->size)
1058 if (! XSTRING (object)->intervals)
1059 return Qt;
1061 XSTRING (object)->intervals = 0;
1062 return Qt;
1065 i = validate_interval_range (object, &start, &end, soft);
1067 if (NULL_INTERVAL_P (i))
1069 /* If buffer has no properties, and we want none, return now. */
1070 if (NILP (properties))
1071 return Qnil;
1073 /* Restore the original START and END values
1074 because validate_interval_range increments them for strings. */
1075 start = ostart;
1076 end = oend;
1078 i = validate_interval_range (object, &start, &end, hard);
1079 /* This can return if start == end. */
1080 if (NULL_INTERVAL_P (i))
1081 return Qnil;
1084 s = XINT (start);
1085 len = XINT (end) - s;
1087 if (BUFFERP (object))
1088 modify_region (XBUFFER (object), XINT (start), XINT (end));
1090 if (i->position != s)
1092 unchanged = i;
1093 i = split_interval_right (unchanged, s - unchanged->position);
1095 if (LENGTH (i) > len)
1097 copy_properties (unchanged, i);
1098 i = split_interval_left (i, len);
1099 set_properties (properties, i, object);
1100 if (BUFFERP (object))
1101 signal_after_change (XINT (start), XINT (end) - XINT (start),
1102 XINT (end) - XINT (start));
1104 return Qt;
1107 set_properties (properties, i, object);
1109 if (LENGTH (i) == len)
1111 if (BUFFERP (object))
1112 signal_after_change (XINT (start), XINT (end) - XINT (start),
1113 XINT (end) - XINT (start));
1115 return Qt;
1118 prev_changed = i;
1119 len -= LENGTH (i);
1120 i = next_interval (i);
1123 /* We are starting at the beginning of an interval, I */
1124 while (len > 0)
1126 if (i == 0)
1127 abort ();
1129 if (LENGTH (i) >= len)
1131 if (LENGTH (i) > len)
1132 i = split_interval_left (i, len);
1134 /* We have to call set_properties even if we are going to
1135 merge the intervals, so as to make the undo records
1136 and cause redisplay to happen. */
1137 set_properties (properties, i, object);
1138 if (!NULL_INTERVAL_P (prev_changed))
1139 merge_interval_left (i);
1140 if (BUFFERP (object))
1141 signal_after_change (XINT (start), XINT (end) - XINT (start),
1142 XINT (end) - XINT (start));
1143 return Qt;
1146 len -= LENGTH (i);
1148 /* We have to call set_properties even if we are going to
1149 merge the intervals, so as to make the undo records
1150 and cause redisplay to happen. */
1151 set_properties (properties, i, object);
1152 if (NULL_INTERVAL_P (prev_changed))
1153 prev_changed = i;
1154 else
1155 prev_changed = i = merge_interval_left (i);
1157 i = next_interval (i);
1160 if (BUFFERP (object))
1161 signal_after_change (XINT (start), XINT (end) - XINT (start),
1162 XINT (end) - XINT (start));
1163 return Qt;
1166 DEFUN ("remove-text-properties", Fremove_text_properties,
1167 Sremove_text_properties, 3, 4, 0,
1168 "Remove some properties from text from START to END.\n\
1169 The third argument PROPERTIES is a property list\n\
1170 whose property names specify the properties to remove.\n\
1171 \(The values stored in PROPERTIES are ignored.)\n\
1172 The optional fourth argument, OBJECT,\n\
1173 is the string or buffer containing the text.\n\
1174 Return t if any property was actually removed, nil otherwise.")
1175 (start, end, properties, object)
1176 Lisp_Object start, end, properties, object;
1178 register INTERVAL i, unchanged;
1179 register int s, len, modified = 0;
1181 if (NILP (object))
1182 XSETBUFFER (object, current_buffer);
1184 i = validate_interval_range (object, &start, &end, soft);
1185 if (NULL_INTERVAL_P (i))
1186 return Qnil;
1188 s = XINT (start);
1189 len = XINT (end) - s;
1191 if (i->position != s)
1193 /* No properties on this first interval -- return if
1194 it covers the entire region. */
1195 if (! interval_has_some_properties (properties, i))
1197 int got = (LENGTH (i) - (s - i->position));
1198 if (got >= len)
1199 return Qnil;
1200 len -= got;
1201 i = next_interval (i);
1203 /* Split away the beginning of this interval; what we don't
1204 want to modify. */
1205 else
1207 unchanged = i;
1208 i = split_interval_right (unchanged, s - unchanged->position);
1209 copy_properties (unchanged, i);
1213 if (BUFFERP (object))
1214 modify_region (XBUFFER (object), XINT (start), XINT (end));
1216 /* We are at the beginning of an interval, with len to scan */
1217 for (;;)
1219 if (i == 0)
1220 abort ();
1222 if (LENGTH (i) >= len)
1224 if (! interval_has_some_properties (properties, i))
1225 return modified ? Qt : Qnil;
1227 if (LENGTH (i) == len)
1229 remove_properties (properties, i, object);
1230 if (BUFFERP (object))
1231 signal_after_change (XINT (start), XINT (end) - XINT (start),
1232 XINT (end) - XINT (start));
1233 return Qt;
1236 /* i has the properties, and goes past the change limit */
1237 unchanged = i;
1238 i = split_interval_left (i, len);
1239 copy_properties (unchanged, i);
1240 remove_properties (properties, i, object);
1241 if (BUFFERP (object))
1242 signal_after_change (XINT (start), XINT (end) - XINT (start),
1243 XINT (end) - XINT (start));
1244 return Qt;
1247 len -= LENGTH (i);
1248 modified += remove_properties (properties, i, object);
1249 i = next_interval (i);
1253 DEFUN ("text-property-any", Ftext_property_any,
1254 Stext_property_any, 4, 5, 0,
1255 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1256 If so, return the position of the first character whose property PROPERTY\n\
1257 is `eq' to VALUE. Otherwise return nil.\n\
1258 The optional fifth argument, OBJECT, is the string or buffer\n\
1259 containing the text.")
1260 (start, end, property, value, object)
1261 Lisp_Object start, end, property, value, object;
1263 register INTERVAL i;
1264 register int e, pos;
1266 if (NILP (object))
1267 XSETBUFFER (object, current_buffer);
1268 i = validate_interval_range (object, &start, &end, soft);
1269 if (NULL_INTERVAL_P (i))
1270 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1271 e = XINT (end);
1273 while (! NULL_INTERVAL_P (i))
1275 if (i->position >= e)
1276 break;
1277 if (EQ (textget (i->plist, property), value))
1279 pos = i->position;
1280 if (pos < XINT (start))
1281 pos = XINT (start);
1282 return make_number (pos);
1284 i = next_interval (i);
1286 return Qnil;
1289 DEFUN ("text-property-not-all", Ftext_property_not_all,
1290 Stext_property_not_all, 4, 5, 0,
1291 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1292 If so, return the position of the first character whose property PROPERTY\n\
1293 is not `eq' to VALUE. Otherwise, return nil.\n\
1294 The optional fifth argument, OBJECT, is the string or buffer\n\
1295 containing the text.")
1296 (start, end, property, value, object)
1297 Lisp_Object start, end, property, value, object;
1299 register INTERVAL i;
1300 register int s, e;
1302 if (NILP (object))
1303 XSETBUFFER (object, current_buffer);
1304 i = validate_interval_range (object, &start, &end, soft);
1305 if (NULL_INTERVAL_P (i))
1306 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1307 s = XINT (start);
1308 e = XINT (end);
1310 while (! NULL_INTERVAL_P (i))
1312 if (i->position >= e)
1313 break;
1314 if (! EQ (textget (i->plist, property), value))
1316 if (i->position > s)
1317 s = i->position;
1318 return make_number (s);
1320 i = next_interval (i);
1322 return Qnil;
1325 /* I don't think this is the right interface to export; how often do you
1326 want to do something like this, other than when you're copying objects
1327 around?
1329 I think it would be better to have a pair of functions, one which
1330 returns the text properties of a region as a list of ranges and
1331 plists, and another which applies such a list to another object. */
1333 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1334 SRC and DEST may each refer to strings or buffers.
1335 Optional sixth argument PROP causes only that property to be copied.
1336 Properties are copied to DEST as if by `add-text-properties'.
1337 Return t if any property value actually changed, nil otherwise. */
1339 /* Note this can GC when DEST is a buffer. */
1341 Lisp_Object
1342 copy_text_properties (start, end, src, pos, dest, prop)
1343 Lisp_Object start, end, src, pos, dest, prop;
1345 INTERVAL i;
1346 Lisp_Object res;
1347 Lisp_Object stuff;
1348 Lisp_Object plist;
1349 int s, e, e2, p, len, modified = 0;
1350 struct gcpro gcpro1, gcpro2;
1352 i = validate_interval_range (src, &start, &end, soft);
1353 if (NULL_INTERVAL_P (i))
1354 return Qnil;
1356 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1358 Lisp_Object dest_start, dest_end;
1360 dest_start = pos;
1361 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1362 /* Apply this to a copy of pos; it will try to increment its arguments,
1363 which we don't want. */
1364 validate_interval_range (dest, &dest_start, &dest_end, soft);
1367 s = XINT (start);
1368 e = XINT (end);
1369 p = XINT (pos);
1371 stuff = Qnil;
1373 while (s < e)
1375 e2 = i->position + LENGTH (i);
1376 if (e2 > e)
1377 e2 = e;
1378 len = e2 - s;
1380 plist = i->plist;
1381 if (! NILP (prop))
1382 while (! NILP (plist))
1384 if (EQ (Fcar (plist), prop))
1386 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1387 break;
1389 plist = Fcdr (Fcdr (plist));
1391 if (! NILP (plist))
1393 /* Must defer modifications to the interval tree in case src
1394 and dest refer to the same string or buffer. */
1395 stuff = Fcons (Fcons (make_number (p),
1396 Fcons (make_number (p + len),
1397 Fcons (plist, Qnil))),
1398 stuff);
1401 i = next_interval (i);
1402 if (NULL_INTERVAL_P (i))
1403 break;
1405 p += len;
1406 s = i->position;
1409 GCPRO2 (stuff, dest);
1411 while (! NILP (stuff))
1413 res = Fcar (stuff);
1414 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1415 Fcar (Fcdr (Fcdr (res))), dest);
1416 if (! NILP (res))
1417 modified++;
1418 stuff = Fcdr (stuff);
1421 UNGCPRO;
1423 return modified ? Qt : Qnil;
1426 /* Call the modification hook functions in LIST, each with START and END. */
1428 static void
1429 call_mod_hooks (list, start, end)
1430 Lisp_Object list, start, end;
1432 struct gcpro gcpro1;
1433 GCPRO1 (list);
1434 while (!NILP (list))
1436 call2 (Fcar (list), start, end);
1437 list = Fcdr (list);
1439 UNGCPRO;
1442 /* Check for read-only intervals between character positions START ... END,
1443 in BUF, and signal an error if we find one.
1445 Then check for any modification hooks in the range.
1446 Create a list of all these hooks in lexicographic order,
1447 eliminating consecutive extra copies of the same hook. Then call
1448 those hooks in order, with START and END - 1 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 on character range START ... END.
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, start, end);
1630 if (! NILP (interval_insert_in_front_hooks)
1631 && ! EQ (interval_insert_in_front_hooks,
1632 interval_insert_behind_hooks))
1633 call_mod_hooks (interval_insert_in_front_hooks, start, end);
1636 void
1637 syms_of_textprop ()
1639 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
1640 "Property-list used as default values.\n\
1641 The value of a property in this list is seen as the value for every\n\
1642 character that does not have its own value for that property.");
1643 Vdefault_text_properties = Qnil;
1645 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1646 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1647 This also inhibits the use of the `intangible' text property.");
1648 Vinhibit_point_motion_hooks = Qnil;
1650 staticpro (&interval_insert_behind_hooks);
1651 staticpro (&interval_insert_in_front_hooks);
1652 interval_insert_behind_hooks = Qnil;
1653 interval_insert_in_front_hooks = Qnil;
1656 /* Common attributes one might give text */
1658 staticpro (&Qforeground);
1659 Qforeground = intern ("foreground");
1660 staticpro (&Qbackground);
1661 Qbackground = intern ("background");
1662 staticpro (&Qfont);
1663 Qfont = intern ("font");
1664 staticpro (&Qstipple);
1665 Qstipple = intern ("stipple");
1666 staticpro (&Qunderline);
1667 Qunderline = intern ("underline");
1668 staticpro (&Qread_only);
1669 Qread_only = intern ("read-only");
1670 staticpro (&Qinvisible);
1671 Qinvisible = intern ("invisible");
1672 staticpro (&Qintangible);
1673 Qintangible = intern ("intangible");
1674 staticpro (&Qcategory);
1675 Qcategory = intern ("category");
1676 staticpro (&Qlocal_map);
1677 Qlocal_map = intern ("local-map");
1678 staticpro (&Qfront_sticky);
1679 Qfront_sticky = intern ("front-sticky");
1680 staticpro (&Qrear_nonsticky);
1681 Qrear_nonsticky = intern ("rear-nonsticky");
1683 /* Properties that text might use to specify certain actions */
1685 staticpro (&Qmouse_left);
1686 Qmouse_left = intern ("mouse-left");
1687 staticpro (&Qmouse_entered);
1688 Qmouse_entered = intern ("mouse-entered");
1689 staticpro (&Qpoint_left);
1690 Qpoint_left = intern ("point-left");
1691 staticpro (&Qpoint_entered);
1692 Qpoint_entered = intern ("point-entered");
1694 defsubr (&Stext_properties_at);
1695 defsubr (&Sget_text_property);
1696 defsubr (&Sget_char_property);
1697 defsubr (&Snext_char_property_change);
1698 defsubr (&Sprevious_char_property_change);
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 */