(cvs-menu): Don't move point. Use popup-menu.
[emacs.git] / src / textprop.c
blobbf4e5efc2b33770f2414512708c7e28ea1163f98
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 #include <config.h>
22 #include "lisp.h"
23 #include "intervals.h"
24 #include "buffer.h"
25 #include "window.h"
27 #ifndef NULL
28 #define NULL (void *)0
29 #endif
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
32 universal set. */
34 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
37 /* NOTES: previous- and next- property change will have to skip
38 zero-length intervals if they are implemented. This could be done
39 inside next_interval and previous_interval.
41 set_properties needs to deal with the interval property cache.
43 It is assumed that for any interval plist, a property appears
44 only once on the list. Although some code i.e., remove_properties,
45 handles the more general case, the uniqueness of properties is
46 necessary for the system to remain consistent. This requirement
47 is enforced by the subrs installing properties onto the intervals. */
50 /* Types of hooks. */
51 Lisp_Object Qmouse_left;
52 Lisp_Object Qmouse_entered;
53 Lisp_Object Qpoint_left;
54 Lisp_Object Qpoint_entered;
55 Lisp_Object Qcategory;
56 Lisp_Object Qlocal_map;
58 /* Visual properties text (including strings) may have. */
59 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
60 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
62 /* Sticky properties */
63 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
66 the o1's cdr. Otherwise, return zero. This is handy for
67 traversing plists. */
68 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70 Lisp_Object Vinhibit_point_motion_hooks;
71 Lisp_Object Vdefault_text_properties;
72 Lisp_Object Vtext_property_default_nonsticky;
74 /* verify_interval_modification saves insertion hooks here
75 to be run later by report_interval_modification. */
76 Lisp_Object interval_insert_behind_hooks;
77 Lisp_Object interval_insert_in_front_hooks;
79 /* Extract the interval at the position pointed to by BEGIN from
80 OBJECT, a string or buffer. Additionally, check that the positions
81 pointed to by BEGIN and END are within the bounds of OBJECT, and
82 reverse them if *BEGIN is greater than *END. The objects pointed
83 to by BEGIN and END may be integers or markers; if the latter, they
84 are coerced to integers.
86 When OBJECT is a string, we increment *BEGIN and *END
87 to make them origin-one.
89 Note that buffer points don't correspond to interval indices.
90 For example, point-max is 1 greater than the index of the last
91 character. This difference is handled in the caller, which uses
92 the validated points to determine a length, and operates on that.
93 Exceptions are Ftext_properties_at, Fnext_property_change, and
94 Fprevious_property_change which call this function with BEGIN == END.
95 Handle this case specially.
97 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
98 create an interval tree for OBJECT if one doesn't exist, provided
99 the object actually contains text. In the current design, if there
100 is no text, there can be no text properties. */
102 #define soft 0
103 #define hard 1
105 INTERVAL
106 validate_interval_range (object, begin, end, force)
107 Lisp_Object object, *begin, *end;
108 int force;
110 register INTERVAL i;
111 int searchpos;
113 CHECK_STRING_OR_BUFFER (object, 0);
114 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
115 CHECK_NUMBER_COERCE_MARKER (*end, 0);
117 /* If we are asked for a point, but from a subr which operates
118 on a range, then return nothing. */
119 if (EQ (*begin, *end) && begin != end)
120 return NULL_INTERVAL;
122 if (XINT (*begin) > XINT (*end))
124 Lisp_Object n;
125 n = *begin;
126 *begin = *end;
127 *end = n;
130 if (BUFFERP (object))
132 register struct buffer *b = XBUFFER (object);
134 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
135 && XINT (*end) <= BUF_ZV (b)))
136 args_out_of_range (*begin, *end);
137 i = BUF_INTERVALS (b);
139 /* If there's no text, there are no properties. */
140 if (BUF_BEGV (b) == BUF_ZV (b))
141 return NULL_INTERVAL;
143 searchpos = XINT (*begin);
145 else
147 register struct Lisp_String *s = XSTRING (object);
149 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
150 && XINT (*end) <= s->size))
151 args_out_of_range (*begin, *end);
152 XSETFASTINT (*begin, XFASTINT (*begin));
153 if (begin != end)
154 XSETFASTINT (*end, XFASTINT (*end));
155 i = s->intervals;
157 if (s->size == 0)
158 return NULL_INTERVAL;
160 searchpos = XINT (*begin);
163 if (NULL_INTERVAL_P (i))
164 return (force ? create_root_interval (object) : i);
166 return find_interval (i, searchpos);
169 /* Validate LIST as a property list. If LIST is not a list, then
170 make one consisting of (LIST nil). Otherwise, verify that LIST
171 is even numbered and thus suitable as a plist. */
173 static Lisp_Object
174 validate_plist (list)
175 Lisp_Object list;
177 if (NILP (list))
178 return Qnil;
180 if (CONSP (list))
182 register int i;
183 register Lisp_Object tail;
184 for (i = 0, tail = list; !NILP (tail); i++)
186 tail = Fcdr (tail);
187 QUIT;
189 if (i & 1)
190 error ("Odd length text property list");
191 return list;
194 return Fcons (list, Fcons (Qnil, Qnil));
197 /* Return nonzero if interval I has all the properties,
198 with the same values, of list PLIST. */
200 static int
201 interval_has_all_properties (plist, i)
202 Lisp_Object plist;
203 INTERVAL i;
205 register Lisp_Object tail1, tail2, sym1;
206 register int found;
208 /* Go through each element of PLIST. */
209 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
211 sym1 = Fcar (tail1);
212 found = 0;
214 /* Go through I's plist, looking for sym1 */
215 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
216 if (EQ (sym1, Fcar (tail2)))
218 /* Found the same property on both lists. If the
219 values are unequal, return zero. */
220 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
221 return 0;
223 /* Property has same value on both lists; go to next one. */
224 found = 1;
225 break;
228 if (! found)
229 return 0;
232 return 1;
235 /* Return nonzero if the plist of interval I has any of the
236 properties of PLIST, regardless of their values. */
238 static INLINE int
239 interval_has_some_properties (plist, i)
240 Lisp_Object plist;
241 INTERVAL i;
243 register Lisp_Object tail1, tail2, sym;
245 /* Go through each element of PLIST. */
246 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
248 sym = Fcar (tail1);
250 /* Go through i's plist, looking for tail1 */
251 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
252 if (EQ (sym, Fcar (tail2)))
253 return 1;
256 return 0;
259 /* Changing the plists of individual intervals. */
261 /* Return the value of PROP in property-list PLIST, or Qunbound if it
262 has none. */
263 static Lisp_Object
264 property_value (plist, prop)
265 Lisp_Object plist, prop;
267 Lisp_Object value;
269 while (PLIST_ELT_P (plist, value))
270 if (EQ (XCAR (plist), prop))
271 return XCAR (value);
272 else
273 plist = XCDR (value);
275 return Qunbound;
278 /* Set the properties of INTERVAL to PROPERTIES,
279 and record undo info for the previous values.
280 OBJECT is the string or buffer that INTERVAL belongs to. */
282 static void
283 set_properties (properties, interval, object)
284 Lisp_Object properties, object;
285 INTERVAL interval;
287 Lisp_Object sym, value;
289 if (BUFFERP (object))
291 /* For each property in the old plist which is missing from PROPERTIES,
292 or has a different value in PROPERTIES, make an undo record. */
293 for (sym = interval->plist;
294 PLIST_ELT_P (sym, value);
295 sym = XCDR (value))
296 if (! EQ (property_value (properties, XCAR (sym)),
297 XCAR (value)))
299 record_property_change (interval->position, LENGTH (interval),
300 XCAR (sym), XCAR (value),
301 object);
304 /* For each new property that has no value at all in the old plist,
305 make an undo record binding it to nil, so it will be removed. */
306 for (sym = properties;
307 PLIST_ELT_P (sym, value);
308 sym = XCDR (value))
309 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
311 record_property_change (interval->position, LENGTH (interval),
312 XCAR (sym), Qnil,
313 object);
317 /* Store new properties. */
318 interval->plist = Fcopy_sequence (properties);
321 /* Add the properties of PLIST to the interval I, or set
322 the value of I's property to the value of the property on PLIST
323 if they are different.
325 OBJECT should be the string or buffer the interval is in.
327 Return nonzero if this changes I (i.e., if any members of PLIST
328 are actually added to I's plist) */
330 static int
331 add_properties (plist, i, object)
332 Lisp_Object plist;
333 INTERVAL i;
334 Lisp_Object object;
336 Lisp_Object tail1, tail2, sym1, val1;
337 register int changed = 0;
338 register int found;
339 struct gcpro gcpro1, gcpro2, gcpro3;
341 tail1 = plist;
342 sym1 = Qnil;
343 val1 = Qnil;
344 /* No need to protect OBJECT, because we can GC only in the case
345 where it is a buffer, and live buffers are always protected.
346 I and its plist are also protected, via OBJECT. */
347 GCPRO3 (tail1, sym1, val1);
349 /* Go through each element of PLIST. */
350 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
352 sym1 = Fcar (tail1);
353 val1 = Fcar (Fcdr (tail1));
354 found = 0;
356 /* Go through I's plist, looking for sym1 */
357 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
358 if (EQ (sym1, Fcar (tail2)))
360 /* No need to gcpro, because tail2 protects this
361 and it must be a cons cell (we get an error otherwise). */
362 register Lisp_Object this_cdr;
364 this_cdr = Fcdr (tail2);
365 /* Found the property. Now check its value. */
366 found = 1;
368 /* The properties have the same value on both lists.
369 Continue to the next property. */
370 if (EQ (val1, Fcar (this_cdr)))
371 break;
373 /* Record this change in the buffer, for undo purposes. */
374 if (BUFFERP (object))
376 record_property_change (i->position, LENGTH (i),
377 sym1, Fcar (this_cdr), object);
380 /* I's property has a different value -- change it */
381 Fsetcar (this_cdr, val1);
382 changed++;
383 break;
386 if (! found)
388 /* Record this change in the buffer, for undo purposes. */
389 if (BUFFERP (object))
391 record_property_change (i->position, LENGTH (i),
392 sym1, Qnil, object);
394 i->plist = Fcons (sym1, Fcons (val1, i->plist));
395 changed++;
399 UNGCPRO;
401 return changed;
404 /* For any members of PLIST which are properties of I, remove them
405 from I's plist.
406 OBJECT is the string or buffer containing I. */
408 static int
409 remove_properties (plist, i, object)
410 Lisp_Object plist;
411 INTERVAL i;
412 Lisp_Object object;
414 register Lisp_Object tail1, tail2, sym, current_plist;
415 register int changed = 0;
417 current_plist = i->plist;
418 /* Go through each element of plist. */
419 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
421 sym = Fcar (tail1);
423 /* First, remove the symbol if its at the head of the list */
424 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
426 if (BUFFERP (object))
428 record_property_change (i->position, LENGTH (i),
429 sym, Fcar (Fcdr (current_plist)),
430 object);
433 current_plist = Fcdr (Fcdr (current_plist));
434 changed++;
437 /* Go through i's plist, looking for sym */
438 tail2 = current_plist;
439 while (! NILP (tail2))
441 register Lisp_Object this;
442 this = Fcdr (Fcdr (tail2));
443 if (EQ (sym, Fcar (this)))
445 if (BUFFERP (object))
447 record_property_change (i->position, LENGTH (i),
448 sym, Fcar (Fcdr (this)), object);
451 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
452 changed++;
454 tail2 = this;
458 if (changed)
459 i->plist = current_plist;
460 return changed;
463 #if 0
464 /* Remove all properties from interval I. Return non-zero
465 if this changes the interval. */
467 static INLINE int
468 erase_properties (i)
469 INTERVAL i;
471 if (NILP (i->plist))
472 return 0;
474 i->plist = Qnil;
475 return 1;
477 #endif
479 /* Returns the interval of POSITION in OBJECT.
480 POSITION is BEG-based. */
482 INTERVAL
483 interval_of (position, object)
484 int position;
485 Lisp_Object object;
487 register INTERVAL i;
488 int beg, end;
490 if (NILP (object))
491 XSETBUFFER (object, current_buffer);
492 else if (EQ (object, Qt))
493 return NULL_INTERVAL;
495 CHECK_STRING_OR_BUFFER (object, 0);
497 if (BUFFERP (object))
499 register struct buffer *b = XBUFFER (object);
501 beg = BUF_BEGV (b);
502 end = BUF_ZV (b);
503 i = BUF_INTERVALS (b);
505 else
507 register struct Lisp_String *s = XSTRING (object);
509 beg = 0;
510 end = s->size;
511 i = s->intervals;
514 if (!(beg <= position && position <= end))
515 args_out_of_range (make_number (position), make_number (position));
516 if (beg == end || NULL_INTERVAL_P (i))
517 return NULL_INTERVAL;
519 return find_interval (i, position);
522 DEFUN ("text-properties-at", Ftext_properties_at,
523 Stext_properties_at, 1, 2, 0,
524 "Return the list of properties of the character at POSITION in OBJECT.\n\
525 OBJECT is the string or buffer to look for the properties in;\n\
526 nil means the current buffer.\n\
527 If POSITION is at the end of OBJECT, the value is nil.")
528 (position, object)
529 Lisp_Object position, object;
531 register INTERVAL i;
533 if (NILP (object))
534 XSETBUFFER (object, current_buffer);
536 i = validate_interval_range (object, &position, &position, soft);
537 if (NULL_INTERVAL_P (i))
538 return Qnil;
539 /* If POSITION is at the end of the interval,
540 it means it's the end of OBJECT.
541 There are no properties at the very end,
542 since no character follows. */
543 if (XINT (position) == LENGTH (i) + i->position)
544 return Qnil;
546 return i->plist;
549 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
550 "Return the value of POSITION's property PROP, in OBJECT.\n\
551 OBJECT is optional and defaults to the current buffer.\n\
552 If POSITION is at the end of OBJECT, the value is nil.")
553 (position, prop, object)
554 Lisp_Object position, object;
555 Lisp_Object prop;
557 return textget (Ftext_properties_at (position, object), prop);
560 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
561 "Return the value of POSITION's property PROP, in OBJECT.\n\
562 OBJECT is optional and defaults to the current buffer.\n\
563 If POSITION is at the end of OBJECT, the value is nil.\n\
564 If OBJECT is a buffer, then overlay properties are considered as well as\n\
565 text properties.\n\
566 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
567 overlays are considered only if they are associated with OBJECT.")
568 (position, prop, object)
569 Lisp_Object position, object;
570 register Lisp_Object prop;
572 struct window *w = 0;
574 CHECK_NUMBER_COERCE_MARKER (position, 0);
576 if (NILP (object))
577 XSETBUFFER (object, current_buffer);
579 if (WINDOWP (object))
581 w = XWINDOW (object);
582 object = w->buffer;
584 if (BUFFERP (object))
586 int posn = XINT (position);
587 int noverlays;
588 Lisp_Object *overlay_vec, tem;
589 int next_overlay;
590 int len;
591 struct buffer *obuf = current_buffer;
593 set_buffer_temp (XBUFFER (object));
595 /* First try with room for 40 overlays. */
596 len = 40;
597 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
599 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
600 &next_overlay, NULL, 0);
602 /* If there are more than 40,
603 make enough space for all, and try again. */
604 if (noverlays > len)
606 len = noverlays;
607 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
608 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
609 &next_overlay, NULL, 0);
611 noverlays = sort_overlays (overlay_vec, noverlays, w);
613 set_buffer_temp (obuf);
615 /* Now check the overlays in order of decreasing priority. */
616 while (--noverlays >= 0)
618 tem = Foverlay_get (overlay_vec[noverlays], prop);
619 if (!NILP (tem))
620 return (tem);
623 /* Not a buffer, or no appropriate overlay, so fall through to the
624 simpler case. */
625 return (Fget_text_property (position, prop, object));
628 DEFUN ("next-char-property-change", Fnext_char_property_change,
629 Snext_char_property_change, 1, 2, 0,
630 "Return the position of next text property or overlay change.\n\
631 This scans characters forward from POSITION in OBJECT till it finds\n\
632 a change in some text property, or the beginning or end of an overlay,\n\
633 and returns the position of that.\n\
634 If none is found, the function returns (point-max).\n\
636 If the optional third argument LIMIT is non-nil, don't search\n\
637 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
638 (position, limit)
639 Lisp_Object position, limit;
641 Lisp_Object temp;
643 temp = Fnext_overlay_change (position);
644 if (! NILP (limit))
646 CHECK_NUMBER (limit, 2);
647 if (XINT (limit) < XINT (temp))
648 temp = limit;
650 return Fnext_property_change (position, Qnil, temp);
653 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
654 Sprevious_char_property_change, 1, 2, 0,
655 "Return the position of previous text property or overlay change.\n\
656 Scans characters backward from POSITION in OBJECT till it finds\n\
657 a change in some text property, or the beginning or end of an overlay,\n\
658 and returns the position of that.\n\
659 If none is found, the function returns (point-max).\n\
661 If the optional third argument LIMIT is non-nil, don't search\n\
662 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
663 (position, limit)
664 Lisp_Object position, limit;
666 Lisp_Object temp;
668 temp = Fprevious_overlay_change (position);
669 if (! NILP (limit))
671 CHECK_NUMBER (limit, 2);
672 if (XINT (limit) > XINT (temp))
673 temp = limit;
675 return Fprevious_property_change (position, Qnil, temp);
679 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
680 Snext_single_char_property_change, 2, 4, 0,
681 "Return the position of next text property or overlay change for a specific property.\n\
682 Scans characters forward from POSITION till it finds\n\
683 a change in the PROP property, then returns the position of the change.\n\
684 The optional third argument OBJECT is the string or buffer to scan.\n\
685 The property values are compared with `eq'.\n\
686 Return nil if the property is constant all the way to the end of OBJECT.\n\
687 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
688 If the optional fourth argument LIMIT is non-nil, don't search\n\
689 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
690 (position, prop, object, limit)
691 Lisp_Object prop, position, object, limit;
693 if (STRINGP (object))
695 position = Fnext_single_property_change (position, prop, object, limit);
696 if (NILP (position))
698 if (NILP (limit))
699 position = make_number (XSTRING (object)->size);
700 else
701 position = limit;
704 else
706 Lisp_Object initial_value, value;
707 int count = specpdl_ptr - specpdl;
709 if (! NILP (object))
710 CHECK_BUFFER (object, 0);
712 if (BUFFERP (object) && current_buffer != XBUFFER (object))
714 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
715 Fset_buffer (object);
718 initial_value = Fget_char_property (position, prop, object);
720 if (NILP (limit))
721 XSETFASTINT (limit, BUF_ZV (current_buffer));
722 else
723 CHECK_NUMBER_COERCE_MARKER (limit, 0);
725 for (;;)
727 position = Fnext_char_property_change (position, limit);
728 if (XFASTINT (position) >= XFASTINT (limit)) {
729 position = limit;
730 break;
733 value = Fget_char_property (position, prop, object);
734 if (!EQ (value, initial_value))
735 break;
738 unbind_to (count, Qnil);
741 return position;
744 DEFUN ("previous-single-char-property-change",
745 Fprevious_single_char_property_change,
746 Sprevious_single_char_property_change, 2, 4, 0,
747 "Return the position of previous text property or overlay change for a specific property.\n\
748 Scans characters backward from POSITION till it finds\n\
749 a change in the PROP property, then returns the position of the change.\n\
750 The optional third argument OBJECT is the string or buffer to scan.\n\
751 The property values are compared with `eq'.\n\
752 Return nil if the property is constant all the way to the start of OBJECT.\n\
753 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
754 If the optional fourth argument LIMIT is non-nil, don't search\n\
755 back past position LIMIT; return LIMIT if nothing is found before LIMIT.")
756 (position, prop, object, limit)
757 Lisp_Object prop, position, object, limit;
759 if (STRINGP (object))
761 position = Fprevious_single_property_change (position, prop, object, limit);
762 if (NILP (position))
764 if (NILP (limit))
765 position = make_number (XSTRING (object)->size);
766 else
767 position = limit;
770 else
772 int count = specpdl_ptr - specpdl;
774 if (! NILP (object))
775 CHECK_BUFFER (object, 0);
777 if (BUFFERP (object) && current_buffer != XBUFFER (object))
779 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
780 Fset_buffer (object);
783 if (NILP (limit))
784 XSETFASTINT (limit, BUF_BEGV (current_buffer));
785 else
786 CHECK_NUMBER_COERCE_MARKER (limit, 0);
788 if (XFASTINT (position) <= XFASTINT (limit))
789 position = limit;
790 else
792 Lisp_Object initial_value =
793 Fget_char_property (make_number (XFASTINT (position) - 1),
794 prop, object);
796 for (;;)
798 position = Fprevious_char_property_change (position, limit);
800 if (XFASTINT (position) <= XFASTINT (limit))
802 position = limit;
803 break;
805 else
807 Lisp_Object value =
808 Fget_char_property (make_number (XFASTINT (position) - 1),
809 prop, object);
811 if (!EQ (value, initial_value))
812 break;
817 unbind_to (count, Qnil);
820 return position;
823 DEFUN ("next-property-change", Fnext_property_change,
824 Snext_property_change, 1, 3, 0,
825 "Return the position of next property change.\n\
826 Scans characters forward from POSITION in OBJECT till it finds\n\
827 a change in some text property, then returns the position of the change.\n\
828 The optional second argument OBJECT is the string or buffer to scan.\n\
829 Return nil if the property is constant all the way to the end of OBJECT.\n\
830 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
831 If the optional third argument LIMIT is non-nil, don't search\n\
832 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
833 (position, object, limit)
834 Lisp_Object position, object, limit;
836 register INTERVAL i, next;
838 if (NILP (object))
839 XSETBUFFER (object, current_buffer);
841 if (! NILP (limit) && ! EQ (limit, Qt))
842 CHECK_NUMBER_COERCE_MARKER (limit, 0);
844 i = validate_interval_range (object, &position, &position, soft);
846 /* If LIMIT is t, return start of next interval--don't
847 bother checking further intervals. */
848 if (EQ (limit, Qt))
850 if (NULL_INTERVAL_P (i))
851 next = i;
852 else
853 next = next_interval (i);
855 if (NULL_INTERVAL_P (next))
856 XSETFASTINT (position, (STRINGP (object)
857 ? XSTRING (object)->size
858 : BUF_ZV (XBUFFER (object))));
859 else
860 XSETFASTINT (position, next->position);
861 return position;
864 if (NULL_INTERVAL_P (i))
865 return limit;
867 next = next_interval (i);
869 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
870 && (NILP (limit) || next->position < XFASTINT (limit)))
871 next = next_interval (next);
873 if (NULL_INTERVAL_P (next))
874 return limit;
875 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
876 return limit;
878 XSETFASTINT (position, next->position);
879 return position;
882 /* Return 1 if there's a change in some property between BEG and END. */
885 property_change_between_p (beg, end)
886 int beg, end;
888 register INTERVAL i, next;
889 Lisp_Object object, pos;
891 XSETBUFFER (object, current_buffer);
892 XSETFASTINT (pos, beg);
894 i = validate_interval_range (object, &pos, &pos, soft);
895 if (NULL_INTERVAL_P (i))
896 return 0;
898 next = next_interval (i);
899 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
901 next = next_interval (next);
902 if (NULL_INTERVAL_P (next))
903 return 0;
904 if (next->position >= end)
905 return 0;
908 if (NULL_INTERVAL_P (next))
909 return 0;
911 return 1;
914 DEFUN ("next-single-property-change", Fnext_single_property_change,
915 Snext_single_property_change, 2, 4, 0,
916 "Return the position of next property change for a specific property.\n\
917 Scans characters forward from POSITION till it finds\n\
918 a change in the PROP property, then returns the position of the change.\n\
919 The optional third argument OBJECT is the string or buffer to scan.\n\
920 The property values are compared with `eq'.\n\
921 Return nil if the property is constant all the way to the end of OBJECT.\n\
922 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
923 If the optional fourth argument LIMIT is non-nil, don't search\n\
924 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
925 (position, prop, object, limit)
926 Lisp_Object position, prop, object, limit;
928 register INTERVAL i, next;
929 register Lisp_Object here_val;
931 if (NILP (object))
932 XSETBUFFER (object, current_buffer);
934 if (!NILP (limit))
935 CHECK_NUMBER_COERCE_MARKER (limit, 0);
937 i = validate_interval_range (object, &position, &position, soft);
938 if (NULL_INTERVAL_P (i))
939 return limit;
941 here_val = textget (i->plist, prop);
942 next = next_interval (i);
943 while (! NULL_INTERVAL_P (next)
944 && EQ (here_val, textget (next->plist, prop))
945 && (NILP (limit) || next->position < XFASTINT (limit)))
946 next = next_interval (next);
948 if (NULL_INTERVAL_P (next))
949 return limit;
950 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
951 return limit;
953 return make_number (next->position);
956 DEFUN ("previous-property-change", Fprevious_property_change,
957 Sprevious_property_change, 1, 3, 0,
958 "Return the position of previous property change.\n\
959 Scans characters backwards from POSITION in OBJECT till it finds\n\
960 a change in some text property, then returns the position of the change.\n\
961 The optional second argument OBJECT is the string or buffer to scan.\n\
962 Return nil if the property is constant all the way to the start of OBJECT.\n\
963 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
964 If the optional third argument LIMIT is non-nil, don't search\n\
965 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
966 (position, object, limit)
967 Lisp_Object position, object, limit;
969 register INTERVAL i, previous;
971 if (NILP (object))
972 XSETBUFFER (object, current_buffer);
974 if (!NILP (limit))
975 CHECK_NUMBER_COERCE_MARKER (limit, 0);
977 i = validate_interval_range (object, &position, &position, soft);
978 if (NULL_INTERVAL_P (i))
979 return limit;
981 /* Start with the interval containing the char before point. */
982 if (i->position == XFASTINT (position))
983 i = previous_interval (i);
985 previous = previous_interval (i);
986 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
987 && (NILP (limit)
988 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
989 previous = previous_interval (previous);
990 if (NULL_INTERVAL_P (previous))
991 return limit;
992 if (!NILP (limit)
993 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
994 return limit;
996 return make_number (previous->position + LENGTH (previous));
999 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1000 Sprevious_single_property_change, 2, 4, 0,
1001 "Return the position of previous property change for a specific property.\n\
1002 Scans characters backward from POSITION till it finds\n\
1003 a change in the PROP property, then returns the position of the change.\n\
1004 The optional third argument OBJECT is the string or buffer to scan.\n\
1005 The property values are compared with `eq'.\n\
1006 Return nil if the property is constant all the way to the start of OBJECT.\n\
1007 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1008 If the optional fourth argument LIMIT is non-nil, don't search\n\
1009 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1010 (position, prop, object, limit)
1011 Lisp_Object position, prop, object, limit;
1013 register INTERVAL i, previous;
1014 register Lisp_Object here_val;
1016 if (NILP (object))
1017 XSETBUFFER (object, current_buffer);
1019 if (!NILP (limit))
1020 CHECK_NUMBER_COERCE_MARKER (limit, 0);
1022 i = validate_interval_range (object, &position, &position, soft);
1024 /* Start with the interval containing the char before point. */
1025 if (! NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1026 i = previous_interval (i);
1028 if (NULL_INTERVAL_P (i))
1029 return limit;
1031 here_val = textget (i->plist, prop);
1032 previous = previous_interval (i);
1033 while (! NULL_INTERVAL_P (previous)
1034 && EQ (here_val, textget (previous->plist, prop))
1035 && (NILP (limit)
1036 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1037 previous = previous_interval (previous);
1038 if (NULL_INTERVAL_P (previous))
1039 return limit;
1040 if (!NILP (limit)
1041 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
1042 return limit;
1044 return make_number (previous->position + LENGTH (previous));
1047 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1049 DEFUN ("add-text-properties", Fadd_text_properties,
1050 Sadd_text_properties, 3, 4, 0,
1051 "Add properties to the text from START to END.\n\
1052 The third argument PROPERTIES is a property list\n\
1053 specifying the property values to add.\n\
1054 The optional fourth argument, OBJECT,\n\
1055 is the string or buffer containing the text.\n\
1056 Return t if any property value actually changed, nil otherwise.")
1057 (start, end, properties, object)
1058 Lisp_Object start, end, properties, object;
1060 register INTERVAL i, unchanged;
1061 register int s, len, modified = 0;
1062 struct gcpro gcpro1;
1064 properties = validate_plist (properties);
1065 if (NILP (properties))
1066 return Qnil;
1068 if (NILP (object))
1069 XSETBUFFER (object, current_buffer);
1071 i = validate_interval_range (object, &start, &end, hard);
1072 if (NULL_INTERVAL_P (i))
1073 return Qnil;
1075 s = XINT (start);
1076 len = XINT (end) - s;
1078 /* No need to protect OBJECT, because we GC only if it's a buffer,
1079 and live buffers are always protected. */
1080 GCPRO1 (properties);
1082 /* If we're not starting on an interval boundary, we have to
1083 split this interval. */
1084 if (i->position != s)
1086 /* If this interval already has the properties, we can
1087 skip it. */
1088 if (interval_has_all_properties (properties, i))
1090 int got = (LENGTH (i) - (s - i->position));
1091 if (got >= len)
1092 RETURN_UNGCPRO (Qnil);
1093 len -= got;
1094 i = next_interval (i);
1096 else
1098 unchanged = i;
1099 i = split_interval_right (unchanged, s - unchanged->position);
1100 copy_properties (unchanged, i);
1104 if (BUFFERP (object))
1105 modify_region (XBUFFER (object), XINT (start), XINT (end));
1107 /* We are at the beginning of interval I, with LEN chars to scan. */
1108 for (;;)
1110 if (i == 0)
1111 abort ();
1113 if (LENGTH (i) >= len)
1115 /* We can UNGCPRO safely here, because there will be just
1116 one more chance to gc, in the next call to add_properties,
1117 and after that we will not need PROPERTIES or OBJECT again. */
1118 UNGCPRO;
1120 if (interval_has_all_properties (properties, i))
1122 if (BUFFERP (object))
1123 signal_after_change (XINT (start), XINT (end) - XINT (start),
1124 XINT (end) - XINT (start));
1126 return modified ? Qt : Qnil;
1129 if (LENGTH (i) == len)
1131 add_properties (properties, i, object);
1132 if (BUFFERP (object))
1133 signal_after_change (XINT (start), XINT (end) - XINT (start),
1134 XINT (end) - XINT (start));
1135 return Qt;
1138 /* i doesn't have the properties, and goes past the change limit */
1139 unchanged = i;
1140 i = split_interval_left (unchanged, len);
1141 copy_properties (unchanged, i);
1142 add_properties (properties, i, object);
1143 if (BUFFERP (object))
1144 signal_after_change (XINT (start), XINT (end) - XINT (start),
1145 XINT (end) - XINT (start));
1146 return Qt;
1149 len -= LENGTH (i);
1150 modified += add_properties (properties, i, object);
1151 i = next_interval (i);
1155 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1157 DEFUN ("put-text-property", Fput_text_property,
1158 Sput_text_property, 4, 5, 0,
1159 "Set one property of the text from START to END.\n\
1160 The third and fourth arguments PROPERTY and VALUE\n\
1161 specify the property to add.\n\
1162 The optional fifth argument, OBJECT,\n\
1163 is the string or buffer containing the text.")
1164 (start, end, property, value, object)
1165 Lisp_Object start, end, property, value, object;
1167 Fadd_text_properties (start, end,
1168 Fcons (property, Fcons (value, Qnil)),
1169 object);
1170 return Qnil;
1173 DEFUN ("set-text-properties", Fset_text_properties,
1174 Sset_text_properties, 3, 4, 0,
1175 "Completely replace properties of text from START to END.\n\
1176 The third argument PROPERTIES is the new property list.\n\
1177 The optional fourth argument, OBJECT,\n\
1178 is the string or buffer containing the text.")
1179 (start, end, properties, object)
1180 Lisp_Object start, end, properties, object;
1182 return set_text_properties (start, end, properties, object, Qt);
1186 /* Replace properties of text from START to END with new list of
1187 properties PROPERTIES. OBJECT is the buffer or string containing
1188 the text. OBJECT nil means use the current buffer.
1189 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1190 is non-nil if properties were replaced; it is nil if there weren't
1191 any properties to replace. */
1193 Lisp_Object
1194 set_text_properties (start, end, properties, object, signal_after_change_p)
1195 Lisp_Object start, end, properties, object, signal_after_change_p;
1197 register INTERVAL i, unchanged;
1198 register INTERVAL prev_changed = NULL_INTERVAL;
1199 register int s, len;
1200 Lisp_Object ostart, oend;
1202 ostart = start;
1203 oend = end;
1205 properties = validate_plist (properties);
1207 if (NILP (object))
1208 XSETBUFFER (object, current_buffer);
1210 /* If we want no properties for a whole string,
1211 get rid of its intervals. */
1212 if (NILP (properties) && STRINGP (object)
1213 && XFASTINT (start) == 0
1214 && XFASTINT (end) == XSTRING (object)->size)
1216 if (! XSTRING (object)->intervals)
1217 return Qt;
1219 XSTRING (object)->intervals = 0;
1220 return Qt;
1223 i = validate_interval_range (object, &start, &end, soft);
1225 if (NULL_INTERVAL_P (i))
1227 /* If buffer has no properties, and we want none, return now. */
1228 if (NILP (properties))
1229 return Qnil;
1231 /* Restore the original START and END values
1232 because validate_interval_range increments them for strings. */
1233 start = ostart;
1234 end = oend;
1236 i = validate_interval_range (object, &start, &end, hard);
1237 /* This can return if start == end. */
1238 if (NULL_INTERVAL_P (i))
1239 return Qnil;
1242 s = XINT (start);
1243 len = XINT (end) - s;
1245 if (BUFFERP (object))
1246 modify_region (XBUFFER (object), XINT (start), XINT (end));
1248 if (i->position != s)
1250 unchanged = i;
1251 i = split_interval_right (unchanged, s - unchanged->position);
1253 if (LENGTH (i) > len)
1255 copy_properties (unchanged, i);
1256 i = split_interval_left (i, len);
1257 set_properties (properties, i, object);
1258 if (BUFFERP (object) && !NILP (signal_after_change_p))
1259 signal_after_change (XINT (start), XINT (end) - XINT (start),
1260 XINT (end) - XINT (start));
1262 return Qt;
1265 set_properties (properties, i, object);
1267 if (LENGTH (i) == len)
1269 if (BUFFERP (object) && !NILP (signal_after_change_p))
1270 signal_after_change (XINT (start), XINT (end) - XINT (start),
1271 XINT (end) - XINT (start));
1273 return Qt;
1276 prev_changed = i;
1277 len -= LENGTH (i);
1278 i = next_interval (i);
1281 /* We are starting at the beginning of an interval, I */
1282 while (len > 0)
1284 if (i == 0)
1285 abort ();
1287 if (LENGTH (i) >= len)
1289 if (LENGTH (i) > len)
1290 i = split_interval_left (i, len);
1292 /* We have to call set_properties even if we are going to
1293 merge the intervals, so as to make the undo records
1294 and cause redisplay to happen. */
1295 set_properties (properties, i, object);
1296 if (!NULL_INTERVAL_P (prev_changed))
1297 merge_interval_left (i);
1298 if (BUFFERP (object) && !NILP (signal_after_change_p))
1299 signal_after_change (XINT (start), XINT (end) - XINT (start),
1300 XINT (end) - XINT (start));
1301 return Qt;
1304 len -= LENGTH (i);
1306 /* We have to call set_properties even if we are going to
1307 merge the intervals, so as to make the undo records
1308 and cause redisplay to happen. */
1309 set_properties (properties, i, object);
1310 if (NULL_INTERVAL_P (prev_changed))
1311 prev_changed = i;
1312 else
1313 prev_changed = i = merge_interval_left (i);
1315 i = next_interval (i);
1318 if (BUFFERP (object) && !NILP (signal_after_change_p))
1319 signal_after_change (XINT (start), XINT (end) - XINT (start),
1320 XINT (end) - XINT (start));
1321 return Qt;
1324 DEFUN ("remove-text-properties", Fremove_text_properties,
1325 Sremove_text_properties, 3, 4, 0,
1326 "Remove some properties from text from START to END.\n\
1327 The third argument PROPERTIES is a property list\n\
1328 whose property names specify the properties to remove.\n\
1329 \(The values stored in PROPERTIES are ignored.)\n\
1330 The optional fourth argument, OBJECT,\n\
1331 is the string or buffer containing the text.\n\
1332 Return t if any property was actually removed, nil otherwise.")
1333 (start, end, properties, object)
1334 Lisp_Object start, end, properties, object;
1336 register INTERVAL i, unchanged;
1337 register int s, len, modified = 0;
1339 if (NILP (object))
1340 XSETBUFFER (object, current_buffer);
1342 i = validate_interval_range (object, &start, &end, soft);
1343 if (NULL_INTERVAL_P (i))
1344 return Qnil;
1346 s = XINT (start);
1347 len = XINT (end) - s;
1349 if (i->position != s)
1351 /* No properties on this first interval -- return if
1352 it covers the entire region. */
1353 if (! interval_has_some_properties (properties, i))
1355 int got = (LENGTH (i) - (s - i->position));
1356 if (got >= len)
1357 return Qnil;
1358 len -= got;
1359 i = next_interval (i);
1361 /* Split away the beginning of this interval; what we don't
1362 want to modify. */
1363 else
1365 unchanged = i;
1366 i = split_interval_right (unchanged, s - unchanged->position);
1367 copy_properties (unchanged, i);
1371 if (BUFFERP (object))
1372 modify_region (XBUFFER (object), XINT (start), XINT (end));
1374 /* We are at the beginning of an interval, with len to scan */
1375 for (;;)
1377 if (i == 0)
1378 abort ();
1380 if (LENGTH (i) >= len)
1382 if (! interval_has_some_properties (properties, i))
1383 return modified ? Qt : Qnil;
1385 if (LENGTH (i) == len)
1387 remove_properties (properties, i, object);
1388 if (BUFFERP (object))
1389 signal_after_change (XINT (start), XINT (end) - XINT (start),
1390 XINT (end) - XINT (start));
1391 return Qt;
1394 /* i has the properties, and goes past the change limit */
1395 unchanged = i;
1396 i = split_interval_left (i, len);
1397 copy_properties (unchanged, i);
1398 remove_properties (properties, i, object);
1399 if (BUFFERP (object))
1400 signal_after_change (XINT (start), XINT (end) - XINT (start),
1401 XINT (end) - XINT (start));
1402 return Qt;
1405 len -= LENGTH (i);
1406 modified += remove_properties (properties, i, object);
1407 i = next_interval (i);
1411 DEFUN ("text-property-any", Ftext_property_any,
1412 Stext_property_any, 4, 5, 0,
1413 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1414 If so, return the position of the first character whose property PROPERTY\n\
1415 is `eq' to VALUE. Otherwise return nil.\n\
1416 The optional fifth argument, OBJECT, is the string or buffer\n\
1417 containing the text.")
1418 (start, end, property, value, object)
1419 Lisp_Object start, end, property, value, object;
1421 register INTERVAL i;
1422 register int e, pos;
1424 if (NILP (object))
1425 XSETBUFFER (object, current_buffer);
1426 i = validate_interval_range (object, &start, &end, soft);
1427 if (NULL_INTERVAL_P (i))
1428 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1429 e = XINT (end);
1431 while (! NULL_INTERVAL_P (i))
1433 if (i->position >= e)
1434 break;
1435 if (EQ (textget (i->plist, property), value))
1437 pos = i->position;
1438 if (pos < XINT (start))
1439 pos = XINT (start);
1440 return make_number (pos);
1442 i = next_interval (i);
1444 return Qnil;
1447 DEFUN ("text-property-not-all", Ftext_property_not_all,
1448 Stext_property_not_all, 4, 5, 0,
1449 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1450 If so, return the position of the first character whose property PROPERTY\n\
1451 is not `eq' to VALUE. Otherwise, return nil.\n\
1452 The optional fifth argument, OBJECT, is the string or buffer\n\
1453 containing the text.")
1454 (start, end, property, value, object)
1455 Lisp_Object start, end, property, value, object;
1457 register INTERVAL i;
1458 register int s, e;
1460 if (NILP (object))
1461 XSETBUFFER (object, current_buffer);
1462 i = validate_interval_range (object, &start, &end, soft);
1463 if (NULL_INTERVAL_P (i))
1464 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1465 s = XINT (start);
1466 e = XINT (end);
1468 while (! NULL_INTERVAL_P (i))
1470 if (i->position >= e)
1471 break;
1472 if (! EQ (textget (i->plist, property), value))
1474 if (i->position > s)
1475 s = i->position;
1476 return make_number (s);
1478 i = next_interval (i);
1480 return Qnil;
1483 /* I don't think this is the right interface to export; how often do you
1484 want to do something like this, other than when you're copying objects
1485 around?
1487 I think it would be better to have a pair of functions, one which
1488 returns the text properties of a region as a list of ranges and
1489 plists, and another which applies such a list to another object. */
1491 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1492 SRC and DEST may each refer to strings or buffers.
1493 Optional sixth argument PROP causes only that property to be copied.
1494 Properties are copied to DEST as if by `add-text-properties'.
1495 Return t if any property value actually changed, nil otherwise. */
1497 /* Note this can GC when DEST is a buffer. */
1499 Lisp_Object
1500 copy_text_properties (start, end, src, pos, dest, prop)
1501 Lisp_Object start, end, src, pos, dest, prop;
1503 INTERVAL i;
1504 Lisp_Object res;
1505 Lisp_Object stuff;
1506 Lisp_Object plist;
1507 int s, e, e2, p, len, modified = 0;
1508 struct gcpro gcpro1, gcpro2;
1510 i = validate_interval_range (src, &start, &end, soft);
1511 if (NULL_INTERVAL_P (i))
1512 return Qnil;
1514 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1516 Lisp_Object dest_start, dest_end;
1518 dest_start = pos;
1519 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1520 /* Apply this to a copy of pos; it will try to increment its arguments,
1521 which we don't want. */
1522 validate_interval_range (dest, &dest_start, &dest_end, soft);
1525 s = XINT (start);
1526 e = XINT (end);
1527 p = XINT (pos);
1529 stuff = Qnil;
1531 while (s < e)
1533 e2 = i->position + LENGTH (i);
1534 if (e2 > e)
1535 e2 = e;
1536 len = e2 - s;
1538 plist = i->plist;
1539 if (! NILP (prop))
1540 while (! NILP (plist))
1542 if (EQ (Fcar (plist), prop))
1544 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1545 break;
1547 plist = Fcdr (Fcdr (plist));
1549 if (! NILP (plist))
1551 /* Must defer modifications to the interval tree in case src
1552 and dest refer to the same string or buffer. */
1553 stuff = Fcons (Fcons (make_number (p),
1554 Fcons (make_number (p + len),
1555 Fcons (plist, Qnil))),
1556 stuff);
1559 i = next_interval (i);
1560 if (NULL_INTERVAL_P (i))
1561 break;
1563 p += len;
1564 s = i->position;
1567 GCPRO2 (stuff, dest);
1569 while (! NILP (stuff))
1571 res = Fcar (stuff);
1572 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1573 Fcar (Fcdr (Fcdr (res))), dest);
1574 if (! NILP (res))
1575 modified++;
1576 stuff = Fcdr (stuff);
1579 UNGCPRO;
1581 return modified ? Qt : Qnil;
1585 /* Return a list representing the text properties of OBJECT between
1586 START and END. if PROP is non-nil, report only on that property.
1587 Each result list element has the form (S E PLIST), where S and E
1588 are positions in OBJECT and PLIST is a property list containing the
1589 text properties of OBJECT between S and E. Value is nil if OBJECT
1590 doesn't contain text properties between START and END. */
1592 Lisp_Object
1593 text_property_list (object, start, end, prop)
1594 Lisp_Object object, start, end, prop;
1596 struct interval *i;
1597 Lisp_Object result;
1599 result = Qnil;
1601 i = validate_interval_range (object, &start, &end, soft);
1602 if (!NULL_INTERVAL_P (i))
1604 int s = XINT (start);
1605 int e = XINT (end);
1607 while (s < e)
1609 int interval_end, len;
1610 Lisp_Object plist;
1612 interval_end = i->position + LENGTH (i);
1613 if (interval_end > e)
1614 interval_end = e;
1615 len = interval_end - s;
1617 plist = i->plist;
1619 if (!NILP (prop))
1620 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1621 if (EQ (Fcar (plist), prop))
1623 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1624 break;
1627 if (!NILP (plist))
1628 result = Fcons (Fcons (make_number (s),
1629 Fcons (make_number (s + len),
1630 Fcons (plist, Qnil))),
1631 result);
1633 i = next_interval (i);
1634 if (NULL_INTERVAL_P (i))
1635 break;
1636 s = i->position;
1640 return result;
1644 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1645 (START END PLIST), where START and END are positions and PLIST is a
1646 property list containing the text properties to add. Adjust START
1647 and END positions by DELTA before adding properties. Value is
1648 non-zero if OBJECT was modified. */
1651 add_text_properties_from_list (object, list, delta)
1652 Lisp_Object object, list, delta;
1654 struct gcpro gcpro1, gcpro2;
1655 int modified_p = 0;
1657 GCPRO2 (list, object);
1659 for (; CONSP (list); list = XCDR (list))
1661 Lisp_Object item, start, end, plist, tem;
1663 item = XCAR (list);
1664 start = make_number (XINT (XCAR (item)) + XINT (delta));
1665 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1666 plist = XCAR (XCDR (XCDR (item)));
1668 tem = Fadd_text_properties (start, end, plist, object);
1669 if (!NILP (tem))
1670 modified_p = 1;
1673 UNGCPRO;
1674 return modified_p;
1679 /* Modify end-points of ranges in LIST destructively. LIST is a list
1680 as returned from text_property_list. Change end-points equal to
1681 OLD_END to NEW_END. */
1683 void
1684 extend_property_ranges (list, old_end, new_end)
1685 Lisp_Object list, old_end, new_end;
1687 for (; CONSP (list); list = XCDR (list))
1689 Lisp_Object item, end;
1691 item = XCAR (list);
1692 end = XCAR (XCDR (item));
1694 if (EQ (end, old_end))
1695 XCAR (XCDR (item)) = new_end;
1701 /* Call the modification hook functions in LIST, each with START and END. */
1703 static void
1704 call_mod_hooks (list, start, end)
1705 Lisp_Object list, start, end;
1707 struct gcpro gcpro1;
1708 GCPRO1 (list);
1709 while (!NILP (list))
1711 call2 (Fcar (list), start, end);
1712 list = Fcdr (list);
1714 UNGCPRO;
1717 /* Check for read-only intervals between character positions START ... END,
1718 in BUF, and signal an error if we find one.
1720 Then check for any modification hooks in the range.
1721 Create a list of all these hooks in lexicographic order,
1722 eliminating consecutive extra copies of the same hook. Then call
1723 those hooks in order, with START and END - 1 as arguments. */
1725 void
1726 verify_interval_modification (buf, start, end)
1727 struct buffer *buf;
1728 int start, end;
1730 register INTERVAL intervals = BUF_INTERVALS (buf);
1731 register INTERVAL i;
1732 Lisp_Object hooks;
1733 register Lisp_Object prev_mod_hooks;
1734 Lisp_Object mod_hooks;
1735 struct gcpro gcpro1;
1737 hooks = Qnil;
1738 prev_mod_hooks = Qnil;
1739 mod_hooks = Qnil;
1741 interval_insert_behind_hooks = Qnil;
1742 interval_insert_in_front_hooks = Qnil;
1744 if (NULL_INTERVAL_P (intervals))
1745 return;
1747 if (start > end)
1749 int temp = start;
1750 start = end;
1751 end = temp;
1754 /* For an insert operation, check the two chars around the position. */
1755 if (start == end)
1757 INTERVAL prev;
1758 Lisp_Object before, after;
1760 /* Set I to the interval containing the char after START,
1761 and PREV to the interval containing the char before START.
1762 Either one may be null. They may be equal. */
1763 i = find_interval (intervals, start);
1765 if (start == BUF_BEGV (buf))
1766 prev = 0;
1767 else if (i->position == start)
1768 prev = previous_interval (i);
1769 else if (i->position < start)
1770 prev = i;
1771 if (start == BUF_ZV (buf))
1772 i = 0;
1774 /* If Vinhibit_read_only is set and is not a list, we can
1775 skip the read_only checks. */
1776 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
1778 /* If I and PREV differ we need to check for the read-only
1779 property together with its stickiness. If either I or
1780 PREV are 0, this check is all we need.
1781 We have to take special care, since read-only may be
1782 indirectly defined via the category property. */
1783 if (i != prev)
1785 if (! NULL_INTERVAL_P (i))
1787 after = textget (i->plist, Qread_only);
1789 /* If interval I is read-only and read-only is
1790 front-sticky, inhibit insertion.
1791 Check for read-only as well as category. */
1792 if (! NILP (after)
1793 && NILP (Fmemq (after, Vinhibit_read_only)))
1795 Lisp_Object tem;
1797 tem = textget (i->plist, Qfront_sticky);
1798 if (TMEM (Qread_only, tem)
1799 || (NILP (Fplist_get (i->plist, Qread_only))
1800 && TMEM (Qcategory, tem)))
1801 Fsignal (Qtext_read_only, Qnil);
1805 if (! NULL_INTERVAL_P (prev))
1807 before = textget (prev->plist, Qread_only);
1809 /* If interval PREV is read-only and read-only isn't
1810 rear-nonsticky, inhibit insertion.
1811 Check for read-only as well as category. */
1812 if (! NILP (before)
1813 && NILP (Fmemq (before, Vinhibit_read_only)))
1815 Lisp_Object tem;
1817 tem = textget (prev->plist, Qrear_nonsticky);
1818 if (! TMEM (Qread_only, tem)
1819 && (! NILP (Fplist_get (prev->plist,Qread_only))
1820 || ! TMEM (Qcategory, tem)))
1821 Fsignal (Qtext_read_only, Qnil);
1825 else if (! NULL_INTERVAL_P (i))
1827 after = textget (i->plist, Qread_only);
1829 /* If interval I is read-only and read-only is
1830 front-sticky, inhibit insertion.
1831 Check for read-only as well as category. */
1832 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
1834 Lisp_Object tem;
1836 tem = textget (i->plist, Qfront_sticky);
1837 if (TMEM (Qread_only, tem)
1838 || (NILP (Fplist_get (i->plist, Qread_only))
1839 && TMEM (Qcategory, tem)))
1840 Fsignal (Qtext_read_only, Qnil);
1842 tem = textget (prev->plist, Qrear_nonsticky);
1843 if (! TMEM (Qread_only, tem)
1844 && (! NILP (Fplist_get (prev->plist, Qread_only))
1845 || ! TMEM (Qcategory, tem)))
1846 Fsignal (Qtext_read_only, Qnil);
1851 /* Run both insert hooks (just once if they're the same). */
1852 if (!NULL_INTERVAL_P (prev))
1853 interval_insert_behind_hooks
1854 = textget (prev->plist, Qinsert_behind_hooks);
1855 if (!NULL_INTERVAL_P (i))
1856 interval_insert_in_front_hooks
1857 = textget (i->plist, Qinsert_in_front_hooks);
1859 else
1861 /* Loop over intervals on or next to START...END,
1862 collecting their hooks. */
1864 i = find_interval (intervals, start);
1867 if (! INTERVAL_WRITABLE_P (i))
1868 Fsignal (Qtext_read_only, Qnil);
1870 mod_hooks = textget (i->plist, Qmodification_hooks);
1871 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
1873 hooks = Fcons (mod_hooks, hooks);
1874 prev_mod_hooks = mod_hooks;
1877 i = next_interval (i);
1879 /* Keep going thru the interval containing the char before END. */
1880 while (! NULL_INTERVAL_P (i) && i->position < end);
1882 GCPRO1 (hooks);
1883 hooks = Fnreverse (hooks);
1884 while (! EQ (hooks, Qnil))
1886 call_mod_hooks (Fcar (hooks), make_number (start),
1887 make_number (end));
1888 hooks = Fcdr (hooks);
1890 UNGCPRO;
1894 /* Run the interval hooks for an insertion on character range START ... END.
1895 verify_interval_modification chose which hooks to run;
1896 this function is called after the insertion happens
1897 so it can indicate the range of inserted text. */
1899 void
1900 report_interval_modification (start, end)
1901 Lisp_Object start, end;
1903 if (! NILP (interval_insert_behind_hooks))
1904 call_mod_hooks (interval_insert_behind_hooks, start, end);
1905 if (! NILP (interval_insert_in_front_hooks)
1906 && ! EQ (interval_insert_in_front_hooks,
1907 interval_insert_behind_hooks))
1908 call_mod_hooks (interval_insert_in_front_hooks, start, end);
1911 void
1912 syms_of_textprop ()
1914 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
1915 "Property-list used as default values.\n\
1916 The value of a property in this list is seen as the value for every\n\
1917 character that does not have its own value for that property.");
1918 Vdefault_text_properties = Qnil;
1920 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
1921 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1922 This also inhibits the use of the `intangible' text property.");
1923 Vinhibit_point_motion_hooks = Qnil;
1925 DEFVAR_LISP ("text-property-default-nonsticky",
1926 &Vtext_property_default_nonsticky,
1927 "Alist of properties vs the corresponding non-stickinesses.\n\
1928 Each element has the form (PROPERTY . NONSTICKINESS).\n\
1930 If a character in a buffer has PROPERTY, new text inserted adjacent to\n\
1931 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,\n\
1932 inherits it if NONSTICKINESS is nil. The front-sticky and\n\
1933 rear-nonsticky properties of the character overrides NONSTICKINESS.");
1934 Vtext_property_default_nonsticky = Qnil;
1936 staticpro (&interval_insert_behind_hooks);
1937 staticpro (&interval_insert_in_front_hooks);
1938 interval_insert_behind_hooks = Qnil;
1939 interval_insert_in_front_hooks = Qnil;
1942 /* Common attributes one might give text */
1944 staticpro (&Qforeground);
1945 Qforeground = intern ("foreground");
1946 staticpro (&Qbackground);
1947 Qbackground = intern ("background");
1948 staticpro (&Qfont);
1949 Qfont = intern ("font");
1950 staticpro (&Qstipple);
1951 Qstipple = intern ("stipple");
1952 staticpro (&Qunderline);
1953 Qunderline = intern ("underline");
1954 staticpro (&Qread_only);
1955 Qread_only = intern ("read-only");
1956 staticpro (&Qinvisible);
1957 Qinvisible = intern ("invisible");
1958 staticpro (&Qintangible);
1959 Qintangible = intern ("intangible");
1960 staticpro (&Qcategory);
1961 Qcategory = intern ("category");
1962 staticpro (&Qlocal_map);
1963 Qlocal_map = intern ("local-map");
1964 staticpro (&Qfront_sticky);
1965 Qfront_sticky = intern ("front-sticky");
1966 staticpro (&Qrear_nonsticky);
1967 Qrear_nonsticky = intern ("rear-nonsticky");
1968 staticpro (&Qmouse_face);
1969 Qmouse_face = intern ("mouse-face");
1971 /* Properties that text might use to specify certain actions */
1973 staticpro (&Qmouse_left);
1974 Qmouse_left = intern ("mouse-left");
1975 staticpro (&Qmouse_entered);
1976 Qmouse_entered = intern ("mouse-entered");
1977 staticpro (&Qpoint_left);
1978 Qpoint_left = intern ("point-left");
1979 staticpro (&Qpoint_entered);
1980 Qpoint_entered = intern ("point-entered");
1982 defsubr (&Stext_properties_at);
1983 defsubr (&Sget_text_property);
1984 defsubr (&Sget_char_property);
1985 defsubr (&Snext_char_property_change);
1986 defsubr (&Sprevious_char_property_change);
1987 defsubr (&Snext_single_char_property_change);
1988 defsubr (&Sprevious_single_char_property_change);
1989 defsubr (&Snext_property_change);
1990 defsubr (&Snext_single_property_change);
1991 defsubr (&Sprevious_property_change);
1992 defsubr (&Sprevious_single_property_change);
1993 defsubr (&Sadd_text_properties);
1994 defsubr (&Sput_text_property);
1995 defsubr (&Sset_text_properties);
1996 defsubr (&Sremove_text_properties);
1997 defsubr (&Stext_property_any);
1998 defsubr (&Stext_property_not_all);
1999 /* defsubr (&Serase_text_properties); */
2000 /* defsubr (&Scopy_text_properties); */