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)
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. */
23 #include "intervals.h"
28 #define NULL (void *)0
31 /* Test for membership, allowing for t (actually any non-cons) to mean the
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. */
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
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
;
80 /* Signal a `text-read-only' error. This function makes it easier
81 to capture that error in GDB by putting a breakpoint on it. */
86 Fsignal (Qtext_read_only
, Qnil
);
91 /* Extract the interval at the position pointed to by BEGIN from
92 OBJECT, a string or buffer. Additionally, check that the positions
93 pointed to by BEGIN and END are within the bounds of OBJECT, and
94 reverse them if *BEGIN is greater than *END. The objects pointed
95 to by BEGIN and END may be integers or markers; if the latter, they
96 are coerced to integers.
98 When OBJECT is a string, we increment *BEGIN and *END
99 to make them origin-one.
101 Note that buffer points don't correspond to interval indices.
102 For example, point-max is 1 greater than the index of the last
103 character. This difference is handled in the caller, which uses
104 the validated points to determine a length, and operates on that.
105 Exceptions are Ftext_properties_at, Fnext_property_change, and
106 Fprevious_property_change which call this function with BEGIN == END.
107 Handle this case specially.
109 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
110 create an interval tree for OBJECT if one doesn't exist, provided
111 the object actually contains text. In the current design, if there
112 is no text, there can be no text properties. */
118 validate_interval_range (object
, begin
, end
, force
)
119 Lisp_Object object
, *begin
, *end
;
125 CHECK_STRING_OR_BUFFER (object
, 0);
126 CHECK_NUMBER_COERCE_MARKER (*begin
, 0);
127 CHECK_NUMBER_COERCE_MARKER (*end
, 0);
129 /* If we are asked for a point, but from a subr which operates
130 on a range, then return nothing. */
131 if (EQ (*begin
, *end
) && begin
!= end
)
132 return NULL_INTERVAL
;
134 if (XINT (*begin
) > XINT (*end
))
142 if (BUFFERP (object
))
144 register struct buffer
*b
= XBUFFER (object
);
146 if (!(BUF_BEGV (b
) <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
147 && XINT (*end
) <= BUF_ZV (b
)))
148 args_out_of_range (*begin
, *end
);
149 i
= BUF_INTERVALS (b
);
151 /* If there's no text, there are no properties. */
152 if (BUF_BEGV (b
) == BUF_ZV (b
))
153 return NULL_INTERVAL
;
155 searchpos
= XINT (*begin
);
159 register struct Lisp_String
*s
= XSTRING (object
);
161 if (! (0 <= XINT (*begin
) && XINT (*begin
) <= XINT (*end
)
162 && XINT (*end
) <= s
->size
))
163 args_out_of_range (*begin
, *end
);
164 XSETFASTINT (*begin
, XFASTINT (*begin
));
166 XSETFASTINT (*end
, XFASTINT (*end
));
170 return NULL_INTERVAL
;
172 searchpos
= XINT (*begin
);
175 if (NULL_INTERVAL_P (i
))
176 return (force
? create_root_interval (object
) : i
);
178 return find_interval (i
, searchpos
);
181 /* Validate LIST as a property list. If LIST is not a list, then
182 make one consisting of (LIST nil). Otherwise, verify that LIST
183 is even numbered and thus suitable as a plist. */
186 validate_plist (list
)
195 register Lisp_Object tail
;
196 for (i
= 0, tail
= list
; !NILP (tail
); i
++)
202 error ("Odd length text property list");
206 return Fcons (list
, Fcons (Qnil
, Qnil
));
209 /* Return nonzero if interval I has all the properties,
210 with the same values, of list PLIST. */
213 interval_has_all_properties (plist
, i
)
217 register Lisp_Object tail1
, tail2
, sym1
;
220 /* Go through each element of PLIST. */
221 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
226 /* Go through I's plist, looking for sym1 */
227 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
228 if (EQ (sym1
, Fcar (tail2
)))
230 /* Found the same property on both lists. If the
231 values are unequal, return zero. */
232 if (! EQ (Fcar (Fcdr (tail1
)), Fcar (Fcdr (tail2
))))
235 /* Property has same value on both lists; go to next one. */
247 /* Return nonzero if the plist of interval I has any of the
248 properties of PLIST, regardless of their values. */
251 interval_has_some_properties (plist
, i
)
255 register Lisp_Object tail1
, tail2
, sym
;
257 /* Go through each element of PLIST. */
258 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
262 /* Go through i's plist, looking for tail1 */
263 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
264 if (EQ (sym
, Fcar (tail2
)))
271 /* Changing the plists of individual intervals. */
273 /* Return the value of PROP in property-list PLIST, or Qunbound if it
276 property_value (plist
, prop
)
277 Lisp_Object plist
, prop
;
281 while (PLIST_ELT_P (plist
, value
))
282 if (EQ (XCAR (plist
), prop
))
285 plist
= XCDR (value
);
290 /* Set the properties of INTERVAL to PROPERTIES,
291 and record undo info for the previous values.
292 OBJECT is the string or buffer that INTERVAL belongs to. */
295 set_properties (properties
, interval
, object
)
296 Lisp_Object properties
, object
;
299 Lisp_Object sym
, value
;
301 if (BUFFERP (object
))
303 /* For each property in the old plist which is missing from PROPERTIES,
304 or has a different value in PROPERTIES, make an undo record. */
305 for (sym
= interval
->plist
;
306 PLIST_ELT_P (sym
, value
);
308 if (! EQ (property_value (properties
, XCAR (sym
)),
311 record_property_change (interval
->position
, LENGTH (interval
),
312 XCAR (sym
), XCAR (value
),
316 /* For each new property that has no value at all in the old plist,
317 make an undo record binding it to nil, so it will be removed. */
318 for (sym
= properties
;
319 PLIST_ELT_P (sym
, value
);
321 if (EQ (property_value (interval
->plist
, XCAR (sym
)), Qunbound
))
323 record_property_change (interval
->position
, LENGTH (interval
),
329 /* Store new properties. */
330 interval
->plist
= Fcopy_sequence (properties
);
333 /* Add the properties of PLIST to the interval I, or set
334 the value of I's property to the value of the property on PLIST
335 if they are different.
337 OBJECT should be the string or buffer the interval is in.
339 Return nonzero if this changes I (i.e., if any members of PLIST
340 are actually added to I's plist) */
343 add_properties (plist
, i
, object
)
348 Lisp_Object tail1
, tail2
, sym1
, val1
;
349 register int changed
= 0;
351 struct gcpro gcpro1
, gcpro2
, gcpro3
;
356 /* No need to protect OBJECT, because we can GC only in the case
357 where it is a buffer, and live buffers are always protected.
358 I and its plist are also protected, via OBJECT. */
359 GCPRO3 (tail1
, sym1
, val1
);
361 /* Go through each element of PLIST. */
362 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
365 val1
= Fcar (Fcdr (tail1
));
368 /* Go through I's plist, looking for sym1 */
369 for (tail2
= i
->plist
; ! NILP (tail2
); tail2
= Fcdr (Fcdr (tail2
)))
370 if (EQ (sym1
, Fcar (tail2
)))
372 /* No need to gcpro, because tail2 protects this
373 and it must be a cons cell (we get an error otherwise). */
374 register Lisp_Object this_cdr
;
376 this_cdr
= Fcdr (tail2
);
377 /* Found the property. Now check its value. */
380 /* The properties have the same value on both lists.
381 Continue to the next property. */
382 if (EQ (val1
, Fcar (this_cdr
)))
385 /* Record this change in the buffer, for undo purposes. */
386 if (BUFFERP (object
))
388 record_property_change (i
->position
, LENGTH (i
),
389 sym1
, Fcar (this_cdr
), object
);
392 /* I's property has a different value -- change it */
393 Fsetcar (this_cdr
, val1
);
400 /* Record this change in the buffer, for undo purposes. */
401 if (BUFFERP (object
))
403 record_property_change (i
->position
, LENGTH (i
),
406 i
->plist
= Fcons (sym1
, Fcons (val1
, i
->plist
));
416 /* For any members of PLIST which are properties of I, remove them
418 OBJECT is the string or buffer containing I. */
421 remove_properties (plist
, i
, object
)
426 register Lisp_Object tail1
, tail2
, sym
, current_plist
;
427 register int changed
= 0;
429 current_plist
= i
->plist
;
430 /* Go through each element of plist. */
431 for (tail1
= plist
; ! NILP (tail1
); tail1
= Fcdr (Fcdr (tail1
)))
435 /* First, remove the symbol if its at the head of the list */
436 while (! NILP (current_plist
) && EQ (sym
, Fcar (current_plist
)))
438 if (BUFFERP (object
))
440 record_property_change (i
->position
, LENGTH (i
),
441 sym
, Fcar (Fcdr (current_plist
)),
445 current_plist
= Fcdr (Fcdr (current_plist
));
449 /* Go through i's plist, looking for sym */
450 tail2
= current_plist
;
451 while (! NILP (tail2
))
453 register Lisp_Object
this;
454 this = Fcdr (Fcdr (tail2
));
455 if (EQ (sym
, Fcar (this)))
457 if (BUFFERP (object
))
459 record_property_change (i
->position
, LENGTH (i
),
460 sym
, Fcar (Fcdr (this)), object
);
463 Fsetcdr (Fcdr (tail2
), Fcdr (Fcdr (this)));
471 i
->plist
= current_plist
;
476 /* Remove all properties from interval I. Return non-zero
477 if this changes the interval. */
491 /* Returns the interval of POSITION in OBJECT.
492 POSITION is BEG-based. */
495 interval_of (position
, object
)
503 XSETBUFFER (object
, current_buffer
);
504 else if (EQ (object
, Qt
))
505 return NULL_INTERVAL
;
507 CHECK_STRING_OR_BUFFER (object
, 0);
509 if (BUFFERP (object
))
511 register struct buffer
*b
= XBUFFER (object
);
515 i
= BUF_INTERVALS (b
);
519 register struct Lisp_String
*s
= XSTRING (object
);
526 if (!(beg
<= position
&& position
<= end
))
527 args_out_of_range (make_number (position
), make_number (position
));
528 if (beg
== end
|| NULL_INTERVAL_P (i
))
529 return NULL_INTERVAL
;
531 return find_interval (i
, position
);
534 DEFUN ("text-properties-at", Ftext_properties_at
,
535 Stext_properties_at
, 1, 2, 0,
536 "Return the list of properties of the character at POSITION in OBJECT.\n\
537 OBJECT is the string or buffer to look for the properties in;\n\
538 nil means the current buffer.\n\
539 If POSITION is at the end of OBJECT, the value is nil.")
541 Lisp_Object position
, object
;
546 XSETBUFFER (object
, current_buffer
);
548 i
= validate_interval_range (object
, &position
, &position
, soft
);
549 if (NULL_INTERVAL_P (i
))
551 /* If POSITION is at the end of the interval,
552 it means it's the end of OBJECT.
553 There are no properties at the very end,
554 since no character follows. */
555 if (XINT (position
) == LENGTH (i
) + i
->position
)
561 DEFUN ("get-text-property", Fget_text_property
, Sget_text_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.")
565 (position
, prop
, object
)
566 Lisp_Object position
, object
;
569 return textget (Ftext_properties_at (position
, object
), prop
);
572 /* Return the value of POSITION's property PROP, in OBJECT.
573 OBJECT is optional and defaults to the current buffer.
574 If OVERLAY is non-0, then in the case that the returned property is from
575 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
576 returned in *OVERLAY.
577 If POSITION is at the end of OBJECT, the value is nil.
578 If OBJECT is a buffer, then overlay properties are considered as well as
580 If OBJECT is a window, then that window's buffer is used, but
581 window-specific overlays are considered only if they are associated
584 get_char_property_and_overlay (position
, prop
, object
, overlay
)
585 Lisp_Object position
, object
;
586 register Lisp_Object prop
;
587 Lisp_Object
*overlay
;
589 struct window
*w
= 0;
591 CHECK_NUMBER_COERCE_MARKER (position
, 0);
594 XSETBUFFER (object
, current_buffer
);
596 if (WINDOWP (object
))
598 w
= XWINDOW (object
);
601 if (BUFFERP (object
))
603 int posn
= XINT (position
);
605 Lisp_Object
*overlay_vec
, tem
;
608 struct buffer
*obuf
= current_buffer
;
610 set_buffer_temp (XBUFFER (object
));
612 /* First try with room for 40 overlays. */
614 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
616 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
617 &next_overlay
, NULL
, 0);
619 /* If there are more than 40,
620 make enough space for all, and try again. */
624 overlay_vec
= (Lisp_Object
*) alloca (len
* sizeof (Lisp_Object
));
625 noverlays
= overlays_at (posn
, 0, &overlay_vec
, &len
,
626 &next_overlay
, NULL
, 0);
628 noverlays
= sort_overlays (overlay_vec
, noverlays
, w
);
630 set_buffer_temp (obuf
);
632 /* Now check the overlays in order of decreasing priority. */
633 while (--noverlays
>= 0)
635 tem
= Foverlay_get (overlay_vec
[noverlays
], prop
);
639 /* Return the overlay we got the property from. */
640 *overlay
= overlay_vec
[noverlays
];
647 /* Indicate that the return value is not from an overlay. */
650 /* Not a buffer, or no appropriate overlay, so fall through to the
652 return Fget_text_property (position
, prop
, object
);
655 DEFUN ("get-char-property", Fget_char_property
, Sget_char_property
, 2, 3, 0,
656 "Return the value of POSITION's property PROP, in OBJECT.\n\
657 OBJECT is optional and defaults to the current buffer.\n\
658 If POSITION is at the end of OBJECT, the value is nil.\n\
659 If OBJECT is a buffer, then overlay properties are considered as well as\n\
661 If OBJECT is a window, then that window's buffer is used, but window-specific\n\
662 overlays are considered only if they are associated with OBJECT.")
663 (position
, prop
, object
)
664 Lisp_Object position
, object
;
665 register Lisp_Object prop
;
667 return get_char_property_and_overlay (position
, prop
, object
, 0);
670 DEFUN ("next-char-property-change", Fnext_char_property_change
,
671 Snext_char_property_change
, 1, 2, 0,
672 "Return the position of next text property or overlay change.\n\
673 This scans characters forward from POSITION in OBJECT till it finds\n\
674 a change in some text property, or the beginning or end of an overlay,\n\
675 and returns the position of that.\n\
676 If none is found, the function returns (point-max).\n\
678 If the optional third argument LIMIT is non-nil, don't search\n\
679 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
681 Lisp_Object position
, limit
;
685 temp
= Fnext_overlay_change (position
);
688 CHECK_NUMBER (limit
, 2);
689 if (XINT (limit
) < XINT (temp
))
692 return Fnext_property_change (position
, Qnil
, temp
);
695 DEFUN ("previous-char-property-change", Fprevious_char_property_change
,
696 Sprevious_char_property_change
, 1, 2, 0,
697 "Return the position of previous text property or overlay change.\n\
698 Scans characters backward from POSITION in OBJECT till it finds\n\
699 a change in some text property, or the beginning or end of an overlay,\n\
700 and returns the position of that.\n\
701 If none is found, the function returns (point-max).\n\
703 If the optional third argument LIMIT is non-nil, don't search\n\
704 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
706 Lisp_Object position
, limit
;
710 temp
= Fprevious_overlay_change (position
);
713 CHECK_NUMBER (limit
, 2);
714 if (XINT (limit
) > XINT (temp
))
717 return Fprevious_property_change (position
, Qnil
, temp
);
721 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change
,
722 Snext_single_char_property_change
, 2, 4, 0,
723 "Return the position of next text property or overlay change for a specific property.\n\
724 Scans characters forward from POSITION till it finds\n\
725 a change in the PROP property, then returns the position of the change.\n\
726 The optional third argument OBJECT is the string or buffer to scan.\n\
727 The property values are compared with `eq'.\n\
728 Return nil if the property is constant all the way to the end of OBJECT.\n\
729 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
730 If the optional fourth argument LIMIT is non-nil, don't search\n\
731 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
732 (position
, prop
, object
, limit
)
733 Lisp_Object prop
, position
, object
, limit
;
735 if (STRINGP (object
))
737 position
= Fnext_single_property_change (position
, prop
, object
, limit
);
741 position
= make_number (XSTRING (object
)->size
);
748 Lisp_Object initial_value
, value
;
749 int count
= specpdl_ptr
- specpdl
;
752 CHECK_BUFFER (object
, 0);
754 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
756 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
757 Fset_buffer (object
);
760 initial_value
= Fget_char_property (position
, prop
, object
);
763 XSETFASTINT (limit
, BUF_ZV (current_buffer
));
765 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
769 position
= Fnext_char_property_change (position
, limit
);
770 if (XFASTINT (position
) >= XFASTINT (limit
)) {
775 value
= Fget_char_property (position
, prop
, object
);
776 if (!EQ (value
, initial_value
))
780 unbind_to (count
, Qnil
);
786 DEFUN ("previous-single-char-property-change",
787 Fprevious_single_char_property_change
,
788 Sprevious_single_char_property_change
, 2, 4, 0,
789 "Return the position of previous text property or overlay change for a specific property.\n\
790 Scans characters backward from POSITION till it finds\n\
791 a change in the PROP property, then returns the position of the change.\n\
792 The optional third argument OBJECT is the string or buffer to scan.\n\
793 The property values are compared with `eq'.\n\
794 Return nil if the property is constant all the way to the start of OBJECT.\n\
795 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
796 If the optional fourth argument LIMIT is non-nil, don't search\n\
797 back past position LIMIT; return LIMIT if nothing is found before LIMIT.")
798 (position
, prop
, object
, limit
)
799 Lisp_Object prop
, position
, object
, limit
;
801 if (STRINGP (object
))
803 position
= Fprevious_single_property_change (position
, prop
, object
, limit
);
807 position
= make_number (XSTRING (object
)->size
);
814 int count
= specpdl_ptr
- specpdl
;
817 CHECK_BUFFER (object
, 0);
819 if (BUFFERP (object
) && current_buffer
!= XBUFFER (object
))
821 record_unwind_protect (Fset_buffer
, Fcurrent_buffer ());
822 Fset_buffer (object
);
826 XSETFASTINT (limit
, BUF_BEGV (current_buffer
));
828 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
830 if (XFASTINT (position
) <= XFASTINT (limit
))
834 Lisp_Object initial_value
=
835 Fget_char_property (make_number (XFASTINT (position
) - 1),
840 position
= Fprevious_char_property_change (position
, limit
);
842 if (XFASTINT (position
) <= XFASTINT (limit
))
850 Fget_char_property (make_number (XFASTINT (position
) - 1),
853 if (!EQ (value
, initial_value
))
859 unbind_to (count
, Qnil
);
865 DEFUN ("next-property-change", Fnext_property_change
,
866 Snext_property_change
, 1, 3, 0,
867 "Return the position of next property change.\n\
868 Scans characters forward from POSITION in OBJECT till it finds\n\
869 a change in some text property, then returns the position of the change.\n\
870 The optional second argument OBJECT is the string or buffer to scan.\n\
871 Return nil if the property is constant all the way to the end of OBJECT.\n\
872 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
873 If the optional third argument LIMIT is non-nil, don't search\n\
874 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
875 (position
, object
, limit
)
876 Lisp_Object position
, object
, limit
;
878 register INTERVAL i
, next
;
881 XSETBUFFER (object
, current_buffer
);
883 if (! NILP (limit
) && ! EQ (limit
, Qt
))
884 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
886 i
= validate_interval_range (object
, &position
, &position
, soft
);
888 /* If LIMIT is t, return start of next interval--don't
889 bother checking further intervals. */
892 if (NULL_INTERVAL_P (i
))
895 next
= next_interval (i
);
897 if (NULL_INTERVAL_P (next
))
898 XSETFASTINT (position
, (STRINGP (object
)
899 ? XSTRING (object
)->size
900 : BUF_ZV (XBUFFER (object
))));
902 XSETFASTINT (position
, next
->position
);
906 if (NULL_INTERVAL_P (i
))
909 next
= next_interval (i
);
911 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
)
912 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
913 next
= next_interval (next
);
915 if (NULL_INTERVAL_P (next
))
917 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
920 XSETFASTINT (position
, next
->position
);
924 /* Return 1 if there's a change in some property between BEG and END. */
927 property_change_between_p (beg
, end
)
930 register INTERVAL i
, next
;
931 Lisp_Object object
, pos
;
933 XSETBUFFER (object
, current_buffer
);
934 XSETFASTINT (pos
, beg
);
936 i
= validate_interval_range (object
, &pos
, &pos
, soft
);
937 if (NULL_INTERVAL_P (i
))
940 next
= next_interval (i
);
941 while (! NULL_INTERVAL_P (next
) && intervals_equal (i
, next
))
943 next
= next_interval (next
);
944 if (NULL_INTERVAL_P (next
))
946 if (next
->position
>= end
)
950 if (NULL_INTERVAL_P (next
))
956 DEFUN ("next-single-property-change", Fnext_single_property_change
,
957 Snext_single_property_change
, 2, 4, 0,
958 "Return the position of next property change for a specific property.\n\
959 Scans characters forward from POSITION till it finds\n\
960 a change in the PROP property, then returns the position of the change.\n\
961 The optional third argument OBJECT is the string or buffer to scan.\n\
962 The property values are compared with `eq'.\n\
963 Return nil if the property is constant all the way to the end of OBJECT.\n\
964 If the value is non-nil, it is a position greater than POSITION, never equal.\n\n\
965 If the optional fourth argument LIMIT is non-nil, don't search\n\
966 past position LIMIT; return LIMIT if nothing is found before LIMIT.")
967 (position
, prop
, object
, limit
)
968 Lisp_Object position
, prop
, object
, limit
;
970 register INTERVAL i
, next
;
971 register Lisp_Object here_val
;
974 XSETBUFFER (object
, current_buffer
);
977 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
979 i
= validate_interval_range (object
, &position
, &position
, soft
);
980 if (NULL_INTERVAL_P (i
))
983 here_val
= textget (i
->plist
, prop
);
984 next
= next_interval (i
);
985 while (! NULL_INTERVAL_P (next
)
986 && EQ (here_val
, textget (next
->plist
, prop
))
987 && (NILP (limit
) || next
->position
< XFASTINT (limit
)))
988 next
= next_interval (next
);
990 if (NULL_INTERVAL_P (next
))
992 if (! NILP (limit
) && !(next
->position
< XFASTINT (limit
)))
995 return make_number (next
->position
);
998 DEFUN ("previous-property-change", Fprevious_property_change
,
999 Sprevious_property_change
, 1, 3, 0,
1000 "Return the position of previous property change.\n\
1001 Scans characters backwards from POSITION in OBJECT till it finds\n\
1002 a change in some text property, then returns the position of the change.\n\
1003 The optional second argument OBJECT is the string or buffer to scan.\n\
1004 Return nil if the property is constant all the way to the start of OBJECT.\n\
1005 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1006 If the optional third argument LIMIT is non-nil, don't search\n\
1007 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1008 (position
, object
, limit
)
1009 Lisp_Object position
, object
, limit
;
1011 register INTERVAL i
, previous
;
1014 XSETBUFFER (object
, current_buffer
);
1017 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
1019 i
= validate_interval_range (object
, &position
, &position
, soft
);
1020 if (NULL_INTERVAL_P (i
))
1023 /* Start with the interval containing the char before point. */
1024 if (i
->position
== XFASTINT (position
))
1025 i
= previous_interval (i
);
1027 previous
= previous_interval (i
);
1028 while (! NULL_INTERVAL_P (previous
) && intervals_equal (previous
, i
)
1030 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1031 previous
= previous_interval (previous
);
1032 if (NULL_INTERVAL_P (previous
))
1035 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1038 return make_number (previous
->position
+ LENGTH (previous
));
1041 DEFUN ("previous-single-property-change", Fprevious_single_property_change
,
1042 Sprevious_single_property_change
, 2, 4, 0,
1043 "Return the position of previous property change for a specific property.\n\
1044 Scans characters backward from POSITION till it finds\n\
1045 a change in the PROP property, then returns the position of the change.\n\
1046 The optional third argument OBJECT is the string or buffer to scan.\n\
1047 The property values are compared with `eq'.\n\
1048 Return nil if the property is constant all the way to the start of OBJECT.\n\
1049 If the value is non-nil, it is a position less than POSITION, never equal.\n\n\
1050 If the optional fourth argument LIMIT is non-nil, don't search\n\
1051 back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
1052 (position
, prop
, object
, limit
)
1053 Lisp_Object position
, prop
, object
, limit
;
1055 register INTERVAL i
, previous
;
1056 register Lisp_Object here_val
;
1059 XSETBUFFER (object
, current_buffer
);
1062 CHECK_NUMBER_COERCE_MARKER (limit
, 0);
1064 i
= validate_interval_range (object
, &position
, &position
, soft
);
1066 /* Start with the interval containing the char before point. */
1067 if (! NULL_INTERVAL_P (i
) && i
->position
== XFASTINT (position
))
1068 i
= previous_interval (i
);
1070 if (NULL_INTERVAL_P (i
))
1073 here_val
= textget (i
->plist
, prop
);
1074 previous
= previous_interval (i
);
1075 while (! NULL_INTERVAL_P (previous
)
1076 && EQ (here_val
, textget (previous
->plist
, prop
))
1078 || (previous
->position
+ LENGTH (previous
) > XFASTINT (limit
))))
1079 previous
= previous_interval (previous
);
1080 if (NULL_INTERVAL_P (previous
))
1083 && !(previous
->position
+ LENGTH (previous
) > XFASTINT (limit
)))
1086 return make_number (previous
->position
+ LENGTH (previous
));
1089 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1091 DEFUN ("add-text-properties", Fadd_text_properties
,
1092 Sadd_text_properties
, 3, 4, 0,
1093 "Add properties to the text from START to END.\n\
1094 The third argument PROPERTIES is a property list\n\
1095 specifying the property values to add.\n\
1096 The optional fourth argument, OBJECT,\n\
1097 is the string or buffer containing the text.\n\
1098 Return t if any property value actually changed, nil otherwise.")
1099 (start
, end
, properties
, object
)
1100 Lisp_Object start
, end
, properties
, object
;
1102 register INTERVAL i
, unchanged
;
1103 register int s
, len
, modified
= 0;
1104 struct gcpro gcpro1
;
1106 properties
= validate_plist (properties
);
1107 if (NILP (properties
))
1111 XSETBUFFER (object
, current_buffer
);
1113 i
= validate_interval_range (object
, &start
, &end
, hard
);
1114 if (NULL_INTERVAL_P (i
))
1118 len
= XINT (end
) - s
;
1120 /* No need to protect OBJECT, because we GC only if it's a buffer,
1121 and live buffers are always protected. */
1122 GCPRO1 (properties
);
1124 /* If we're not starting on an interval boundary, we have to
1125 split this interval. */
1126 if (i
->position
!= s
)
1128 /* If this interval already has the properties, we can
1130 if (interval_has_all_properties (properties
, i
))
1132 int got
= (LENGTH (i
) - (s
- i
->position
));
1134 RETURN_UNGCPRO (Qnil
);
1136 i
= next_interval (i
);
1141 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1142 copy_properties (unchanged
, i
);
1146 if (BUFFERP (object
))
1147 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1149 /* We are at the beginning of interval I, with LEN chars to scan. */
1155 if (LENGTH (i
) >= len
)
1157 /* We can UNGCPRO safely here, because there will be just
1158 one more chance to gc, in the next call to add_properties,
1159 and after that we will not need PROPERTIES or OBJECT again. */
1162 if (interval_has_all_properties (properties
, i
))
1164 if (BUFFERP (object
))
1165 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1166 XINT (end
) - XINT (start
));
1168 return modified
? Qt
: Qnil
;
1171 if (LENGTH (i
) == len
)
1173 add_properties (properties
, i
, object
);
1174 if (BUFFERP (object
))
1175 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1176 XINT (end
) - XINT (start
));
1180 /* i doesn't have the properties, and goes past the change limit */
1182 i
= split_interval_left (unchanged
, len
);
1183 copy_properties (unchanged
, i
);
1184 add_properties (properties
, i
, object
);
1185 if (BUFFERP (object
))
1186 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1187 XINT (end
) - XINT (start
));
1192 modified
+= add_properties (properties
, i
, object
);
1193 i
= next_interval (i
);
1197 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1199 DEFUN ("put-text-property", Fput_text_property
,
1200 Sput_text_property
, 4, 5, 0,
1201 "Set one property of the text from START to END.\n\
1202 The third and fourth arguments PROPERTY and VALUE\n\
1203 specify the property to add.\n\
1204 The optional fifth argument, OBJECT,\n\
1205 is the string or buffer containing the text.")
1206 (start
, end
, property
, value
, object
)
1207 Lisp_Object start
, end
, property
, value
, object
;
1209 Fadd_text_properties (start
, end
,
1210 Fcons (property
, Fcons (value
, Qnil
)),
1215 DEFUN ("set-text-properties", Fset_text_properties
,
1216 Sset_text_properties
, 3, 4, 0,
1217 "Completely replace properties of text from START to END.\n\
1218 The third argument PROPERTIES is the new property list.\n\
1219 The optional fourth argument, OBJECT,\n\
1220 is the string or buffer containing the text.\n\
1221 If OBJECT is omitted or nil, it defaults to the current buffer.")
1222 (start
, end
, properties
, object
)
1223 Lisp_Object start
, end
, properties
, object
;
1225 return set_text_properties (start
, end
, properties
, object
, Qt
);
1229 /* Replace properties of text from START to END with new list of
1230 properties PROPERTIES. OBJECT is the buffer or string containing
1231 the text. OBJECT nil means use the current buffer.
1232 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1233 is non-nil if properties were replaced; it is nil if there weren't
1234 any properties to replace. */
1237 set_text_properties (start
, end
, properties
, object
, signal_after_change_p
)
1238 Lisp_Object start
, end
, properties
, object
, signal_after_change_p
;
1240 register INTERVAL i
, unchanged
;
1241 register INTERVAL prev_changed
= NULL_INTERVAL
;
1242 register int s
, len
;
1243 Lisp_Object ostart
, oend
;
1248 properties
= validate_plist (properties
);
1251 XSETBUFFER (object
, current_buffer
);
1253 /* If we want no properties for a whole string,
1254 get rid of its intervals. */
1255 if (NILP (properties
) && STRINGP (object
)
1256 && XFASTINT (start
) == 0
1257 && XFASTINT (end
) == XSTRING (object
)->size
)
1259 if (! XSTRING (object
)->intervals
)
1262 XSTRING (object
)->intervals
= 0;
1266 i
= validate_interval_range (object
, &start
, &end
, soft
);
1268 if (NULL_INTERVAL_P (i
))
1270 /* If buffer has no properties, and we want none, return now. */
1271 if (NILP (properties
))
1274 /* Restore the original START and END values
1275 because validate_interval_range increments them for strings. */
1279 i
= validate_interval_range (object
, &start
, &end
, hard
);
1280 /* This can return if start == end. */
1281 if (NULL_INTERVAL_P (i
))
1286 len
= XINT (end
) - s
;
1288 if (BUFFERP (object
))
1289 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1291 if (i
->position
!= s
)
1294 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1296 if (LENGTH (i
) > len
)
1298 copy_properties (unchanged
, i
);
1299 i
= split_interval_left (i
, len
);
1300 set_properties (properties
, i
, object
);
1301 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1302 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1303 XINT (end
) - XINT (start
));
1308 set_properties (properties
, i
, object
);
1310 if (LENGTH (i
) == len
)
1312 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1313 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1314 XINT (end
) - XINT (start
));
1321 i
= next_interval (i
);
1324 /* We are starting at the beginning of an interval, I */
1330 if (LENGTH (i
) >= len
)
1332 if (LENGTH (i
) > len
)
1333 i
= split_interval_left (i
, len
);
1335 /* We have to call set_properties even if we are going to
1336 merge the intervals, so as to make the undo records
1337 and cause redisplay to happen. */
1338 set_properties (properties
, i
, object
);
1339 if (!NULL_INTERVAL_P (prev_changed
))
1340 merge_interval_left (i
);
1341 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1342 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1343 XINT (end
) - XINT (start
));
1349 /* We have to call set_properties even if we are going to
1350 merge the intervals, so as to make the undo records
1351 and cause redisplay to happen. */
1352 set_properties (properties
, i
, object
);
1353 if (NULL_INTERVAL_P (prev_changed
))
1356 prev_changed
= i
= merge_interval_left (i
);
1358 i
= next_interval (i
);
1361 if (BUFFERP (object
) && !NILP (signal_after_change_p
))
1362 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1363 XINT (end
) - XINT (start
));
1367 DEFUN ("remove-text-properties", Fremove_text_properties
,
1368 Sremove_text_properties
, 3, 4, 0,
1369 "Remove some properties from text from START to END.\n\
1370 The third argument PROPERTIES is a property list\n\
1371 whose property names specify the properties to remove.\n\
1372 \(The values stored in PROPERTIES are ignored.)\n\
1373 The optional fourth argument, OBJECT,\n\
1374 is the string or buffer containing the text.\n\
1375 Return t if any property was actually removed, nil otherwise.")
1376 (start
, end
, properties
, object
)
1377 Lisp_Object start
, end
, properties
, object
;
1379 register INTERVAL i
, unchanged
;
1380 register int s
, len
, modified
= 0;
1383 XSETBUFFER (object
, current_buffer
);
1385 i
= validate_interval_range (object
, &start
, &end
, soft
);
1386 if (NULL_INTERVAL_P (i
))
1390 len
= XINT (end
) - s
;
1392 if (i
->position
!= s
)
1394 /* No properties on this first interval -- return if
1395 it covers the entire region. */
1396 if (! interval_has_some_properties (properties
, i
))
1398 int got
= (LENGTH (i
) - (s
- i
->position
));
1402 i
= next_interval (i
);
1404 /* Split away the beginning of this interval; what we don't
1409 i
= split_interval_right (unchanged
, s
- unchanged
->position
);
1410 copy_properties (unchanged
, i
);
1414 if (BUFFERP (object
))
1415 modify_region (XBUFFER (object
), XINT (start
), XINT (end
));
1417 /* We are at the beginning of an interval, with len to scan */
1423 if (LENGTH (i
) >= len
)
1425 if (! interval_has_some_properties (properties
, i
))
1426 return modified
? Qt
: Qnil
;
1428 if (LENGTH (i
) == len
)
1430 remove_properties (properties
, i
, object
);
1431 if (BUFFERP (object
))
1432 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1433 XINT (end
) - XINT (start
));
1437 /* i has the properties, and goes past the change limit */
1439 i
= split_interval_left (i
, len
);
1440 copy_properties (unchanged
, i
);
1441 remove_properties (properties
, i
, object
);
1442 if (BUFFERP (object
))
1443 signal_after_change (XINT (start
), XINT (end
) - XINT (start
),
1444 XINT (end
) - XINT (start
));
1449 modified
+= remove_properties (properties
, i
, object
);
1450 i
= next_interval (i
);
1454 DEFUN ("text-property-any", Ftext_property_any
,
1455 Stext_property_any
, 4, 5, 0,
1456 "Check text from START to END for property PROPERTY equalling VALUE.\n\
1457 If so, return the position of the first character whose property PROPERTY\n\
1458 is `eq' to VALUE. Otherwise return nil.\n\
1459 The optional fifth argument, OBJECT, is the string or buffer\n\
1460 containing the text.")
1461 (start
, end
, property
, value
, object
)
1462 Lisp_Object start
, end
, property
, value
, object
;
1464 register INTERVAL i
;
1465 register int e
, pos
;
1468 XSETBUFFER (object
, current_buffer
);
1469 i
= validate_interval_range (object
, &start
, &end
, soft
);
1470 if (NULL_INTERVAL_P (i
))
1471 return (!NILP (value
) || EQ (start
, end
) ? Qnil
: start
);
1474 while (! NULL_INTERVAL_P (i
))
1476 if (i
->position
>= e
)
1478 if (EQ (textget (i
->plist
, property
), value
))
1481 if (pos
< XINT (start
))
1483 return make_number (pos
);
1485 i
= next_interval (i
);
1490 DEFUN ("text-property-not-all", Ftext_property_not_all
,
1491 Stext_property_not_all
, 4, 5, 0,
1492 "Check text from START to END for property PROPERTY not equalling VALUE.\n\
1493 If so, return the position of the first character whose property PROPERTY\n\
1494 is not `eq' to VALUE. Otherwise, return nil.\n\
1495 The optional fifth argument, OBJECT, is the string or buffer\n\
1496 containing the text.")
1497 (start
, end
, property
, value
, object
)
1498 Lisp_Object start
, end
, property
, value
, object
;
1500 register INTERVAL i
;
1504 XSETBUFFER (object
, current_buffer
);
1505 i
= validate_interval_range (object
, &start
, &end
, soft
);
1506 if (NULL_INTERVAL_P (i
))
1507 return (NILP (value
) || EQ (start
, end
)) ? Qnil
: start
;
1511 while (! NULL_INTERVAL_P (i
))
1513 if (i
->position
>= e
)
1515 if (! EQ (textget (i
->plist
, property
), value
))
1517 if (i
->position
> s
)
1519 return make_number (s
);
1521 i
= next_interval (i
);
1526 /* I don't think this is the right interface to export; how often do you
1527 want to do something like this, other than when you're copying objects
1530 I think it would be better to have a pair of functions, one which
1531 returns the text properties of a region as a list of ranges and
1532 plists, and another which applies such a list to another object. */
1534 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1535 SRC and DEST may each refer to strings or buffers.
1536 Optional sixth argument PROP causes only that property to be copied.
1537 Properties are copied to DEST as if by `add-text-properties'.
1538 Return t if any property value actually changed, nil otherwise. */
1540 /* Note this can GC when DEST is a buffer. */
1543 copy_text_properties (start
, end
, src
, pos
, dest
, prop
)
1544 Lisp_Object start
, end
, src
, pos
, dest
, prop
;
1550 int s
, e
, e2
, p
, len
, modified
= 0;
1551 struct gcpro gcpro1
, gcpro2
;
1553 i
= validate_interval_range (src
, &start
, &end
, soft
);
1554 if (NULL_INTERVAL_P (i
))
1557 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
1559 Lisp_Object dest_start
, dest_end
;
1562 XSETFASTINT (dest_end
, XINT (dest_start
) + (XINT (end
) - XINT (start
)));
1563 /* Apply this to a copy of pos; it will try to increment its arguments,
1564 which we don't want. */
1565 validate_interval_range (dest
, &dest_start
, &dest_end
, soft
);
1576 e2
= i
->position
+ LENGTH (i
);
1583 while (! NILP (plist
))
1585 if (EQ (Fcar (plist
), prop
))
1587 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1590 plist
= Fcdr (Fcdr (plist
));
1594 /* Must defer modifications to the interval tree in case src
1595 and dest refer to the same string or buffer. */
1596 stuff
= Fcons (Fcons (make_number (p
),
1597 Fcons (make_number (p
+ len
),
1598 Fcons (plist
, Qnil
))),
1602 i
= next_interval (i
);
1603 if (NULL_INTERVAL_P (i
))
1610 GCPRO2 (stuff
, dest
);
1612 while (! NILP (stuff
))
1615 res
= Fadd_text_properties (Fcar (res
), Fcar (Fcdr (res
)),
1616 Fcar (Fcdr (Fcdr (res
))), dest
);
1619 stuff
= Fcdr (stuff
);
1624 return modified
? Qt
: Qnil
;
1628 /* Return a list representing the text properties of OBJECT between
1629 START and END. if PROP is non-nil, report only on that property.
1630 Each result list element has the form (S E PLIST), where S and E
1631 are positions in OBJECT and PLIST is a property list containing the
1632 text properties of OBJECT between S and E. Value is nil if OBJECT
1633 doesn't contain text properties between START and END. */
1636 text_property_list (object
, start
, end
, prop
)
1637 Lisp_Object object
, start
, end
, prop
;
1644 i
= validate_interval_range (object
, &start
, &end
, soft
);
1645 if (!NULL_INTERVAL_P (i
))
1647 int s
= XINT (start
);
1652 int interval_end
, len
;
1655 interval_end
= i
->position
+ LENGTH (i
);
1656 if (interval_end
> e
)
1658 len
= interval_end
- s
;
1663 for (; !NILP (plist
); plist
= Fcdr (Fcdr (plist
)))
1664 if (EQ (Fcar (plist
), prop
))
1666 plist
= Fcons (prop
, Fcons (Fcar (Fcdr (plist
)), Qnil
));
1671 result
= Fcons (Fcons (make_number (s
),
1672 Fcons (make_number (s
+ len
),
1673 Fcons (plist
, Qnil
))),
1676 i
= next_interval (i
);
1677 if (NULL_INTERVAL_P (i
))
1687 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1688 (START END PLIST), where START and END are positions and PLIST is a
1689 property list containing the text properties to add. Adjust START
1690 and END positions by DELTA before adding properties. Value is
1691 non-zero if OBJECT was modified. */
1694 add_text_properties_from_list (object
, list
, delta
)
1695 Lisp_Object object
, list
, delta
;
1697 struct gcpro gcpro1
, gcpro2
;
1700 GCPRO2 (list
, object
);
1702 for (; CONSP (list
); list
= XCDR (list
))
1704 Lisp_Object item
, start
, end
, plist
, tem
;
1707 start
= make_number (XINT (XCAR (item
)) + XINT (delta
));
1708 end
= make_number (XINT (XCAR (XCDR (item
))) + XINT (delta
));
1709 plist
= XCAR (XCDR (XCDR (item
)));
1711 tem
= Fadd_text_properties (start
, end
, plist
, object
);
1722 /* Modify end-points of ranges in LIST destructively. LIST is a list
1723 as returned from text_property_list. Change end-points equal to
1724 OLD_END to NEW_END. */
1727 extend_property_ranges (list
, old_end
, new_end
)
1728 Lisp_Object list
, old_end
, new_end
;
1730 for (; CONSP (list
); list
= XCDR (list
))
1732 Lisp_Object item
, end
;
1735 end
= XCAR (XCDR (item
));
1737 if (EQ (end
, old_end
))
1738 XCAR (XCDR (item
)) = new_end
;
1744 /* Call the modification hook functions in LIST, each with START and END. */
1747 call_mod_hooks (list
, start
, end
)
1748 Lisp_Object list
, start
, end
;
1750 struct gcpro gcpro1
;
1752 while (!NILP (list
))
1754 call2 (Fcar (list
), start
, end
);
1760 /* Check for read-only intervals between character positions START ... END,
1761 in BUF, and signal an error if we find one.
1763 Then check for any modification hooks in the range.
1764 Create a list of all these hooks in lexicographic order,
1765 eliminating consecutive extra copies of the same hook. Then call
1766 those hooks in order, with START and END - 1 as arguments. */
1769 verify_interval_modification (buf
, start
, end
)
1773 register INTERVAL intervals
= BUF_INTERVALS (buf
);
1774 register INTERVAL i
;
1776 register Lisp_Object prev_mod_hooks
;
1777 Lisp_Object mod_hooks
;
1778 struct gcpro gcpro1
;
1781 prev_mod_hooks
= Qnil
;
1784 interval_insert_behind_hooks
= Qnil
;
1785 interval_insert_in_front_hooks
= Qnil
;
1787 if (NULL_INTERVAL_P (intervals
))
1797 /* For an insert operation, check the two chars around the position. */
1800 INTERVAL prev
= NULL
;
1801 Lisp_Object before
, after
;
1803 /* Set I to the interval containing the char after START,
1804 and PREV to the interval containing the char before START.
1805 Either one may be null. They may be equal. */
1806 i
= find_interval (intervals
, start
);
1808 if (start
== BUF_BEGV (buf
))
1810 else if (i
->position
== start
)
1811 prev
= previous_interval (i
);
1812 else if (i
->position
< start
)
1814 if (start
== BUF_ZV (buf
))
1817 /* If Vinhibit_read_only is set and is not a list, we can
1818 skip the read_only checks. */
1819 if (NILP (Vinhibit_read_only
) || CONSP (Vinhibit_read_only
))
1821 /* If I and PREV differ we need to check for the read-only
1822 property together with its stickiness. If either I or
1823 PREV are 0, this check is all we need.
1824 We have to take special care, since read-only may be
1825 indirectly defined via the category property. */
1828 if (! NULL_INTERVAL_P (i
))
1830 after
= textget (i
->plist
, Qread_only
);
1832 /* If interval I is read-only and read-only is
1833 front-sticky, inhibit insertion.
1834 Check for read-only as well as category. */
1836 && NILP (Fmemq (after
, Vinhibit_read_only
)))
1840 tem
= textget (i
->plist
, Qfront_sticky
);
1841 if (TMEM (Qread_only
, tem
)
1842 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1843 && TMEM (Qcategory
, tem
)))
1848 if (! NULL_INTERVAL_P (prev
))
1850 before
= textget (prev
->plist
, Qread_only
);
1852 /* If interval PREV is read-only and read-only isn't
1853 rear-nonsticky, inhibit insertion.
1854 Check for read-only as well as category. */
1856 && NILP (Fmemq (before
, Vinhibit_read_only
)))
1860 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1861 if (! TMEM (Qread_only
, tem
)
1862 && (! NILP (Fplist_get (prev
->plist
,Qread_only
))
1863 || ! TMEM (Qcategory
, tem
)))
1868 else if (! NULL_INTERVAL_P (i
))
1870 after
= textget (i
->plist
, Qread_only
);
1872 /* If interval I is read-only and read-only is
1873 front-sticky, inhibit insertion.
1874 Check for read-only as well as category. */
1875 if (! NILP (after
) && NILP (Fmemq (after
, Vinhibit_read_only
)))
1879 tem
= textget (i
->plist
, Qfront_sticky
);
1880 if (TMEM (Qread_only
, tem
)
1881 || (NILP (Fplist_get (i
->plist
, Qread_only
))
1882 && TMEM (Qcategory
, tem
)))
1885 tem
= textget (prev
->plist
, Qrear_nonsticky
);
1886 if (! TMEM (Qread_only
, tem
)
1887 && (! NILP (Fplist_get (prev
->plist
, Qread_only
))
1888 || ! TMEM (Qcategory
, tem
)))
1894 /* Run both insert hooks (just once if they're the same). */
1895 if (!NULL_INTERVAL_P (prev
))
1896 interval_insert_behind_hooks
1897 = textget (prev
->plist
, Qinsert_behind_hooks
);
1898 if (!NULL_INTERVAL_P (i
))
1899 interval_insert_in_front_hooks
1900 = textget (i
->plist
, Qinsert_in_front_hooks
);
1904 /* Loop over intervals on or next to START...END,
1905 collecting their hooks. */
1907 i
= find_interval (intervals
, start
);
1910 if (! INTERVAL_WRITABLE_P (i
))
1913 mod_hooks
= textget (i
->plist
, Qmodification_hooks
);
1914 if (! NILP (mod_hooks
) && ! EQ (mod_hooks
, prev_mod_hooks
))
1916 hooks
= Fcons (mod_hooks
, hooks
);
1917 prev_mod_hooks
= mod_hooks
;
1920 i
= next_interval (i
);
1922 /* Keep going thru the interval containing the char before END. */
1923 while (! NULL_INTERVAL_P (i
) && i
->position
< end
);
1926 hooks
= Fnreverse (hooks
);
1927 while (! EQ (hooks
, Qnil
))
1929 call_mod_hooks (Fcar (hooks
), make_number (start
),
1931 hooks
= Fcdr (hooks
);
1937 /* Run the interval hooks for an insertion on character range START ... END.
1938 verify_interval_modification chose which hooks to run;
1939 this function is called after the insertion happens
1940 so it can indicate the range of inserted text. */
1943 report_interval_modification (start
, end
)
1944 Lisp_Object start
, end
;
1946 if (! NILP (interval_insert_behind_hooks
))
1947 call_mod_hooks (interval_insert_behind_hooks
, start
, end
);
1948 if (! NILP (interval_insert_in_front_hooks
)
1949 && ! EQ (interval_insert_in_front_hooks
,
1950 interval_insert_behind_hooks
))
1951 call_mod_hooks (interval_insert_in_front_hooks
, start
, end
);
1957 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties
,
1958 "Property-list used as default values.\n\
1959 The value of a property in this list is seen as the value for every\n\
1960 character that does not have its own value for that property.");
1961 Vdefault_text_properties
= Qnil
;
1963 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks
,
1964 "If non-nil, don't run `point-left' and `point-entered' text properties.\n\
1965 This also inhibits the use of the `intangible' text property.");
1966 Vinhibit_point_motion_hooks
= Qnil
;
1968 DEFVAR_LISP ("text-property-default-nonsticky",
1969 &Vtext_property_default_nonsticky
,
1970 "Alist of properties vs the corresponding non-stickinesses.\n\
1971 Each element has the form (PROPERTY . NONSTICKINESS).\n\
1973 If a character in a buffer has PROPERTY, new text inserted adjacent to\n\
1974 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,\n\
1975 inherits it if NONSTICKINESS is nil. The front-sticky and\n\
1976 rear-nonsticky properties of the character overrides NONSTICKINESS.");
1977 Vtext_property_default_nonsticky
= Qnil
;
1979 staticpro (&interval_insert_behind_hooks
);
1980 staticpro (&interval_insert_in_front_hooks
);
1981 interval_insert_behind_hooks
= Qnil
;
1982 interval_insert_in_front_hooks
= Qnil
;
1985 /* Common attributes one might give text */
1987 staticpro (&Qforeground
);
1988 Qforeground
= intern ("foreground");
1989 staticpro (&Qbackground
);
1990 Qbackground
= intern ("background");
1992 Qfont
= intern ("font");
1993 staticpro (&Qstipple
);
1994 Qstipple
= intern ("stipple");
1995 staticpro (&Qunderline
);
1996 Qunderline
= intern ("underline");
1997 staticpro (&Qread_only
);
1998 Qread_only
= intern ("read-only");
1999 staticpro (&Qinvisible
);
2000 Qinvisible
= intern ("invisible");
2001 staticpro (&Qintangible
);
2002 Qintangible
= intern ("intangible");
2003 staticpro (&Qcategory
);
2004 Qcategory
= intern ("category");
2005 staticpro (&Qlocal_map
);
2006 Qlocal_map
= intern ("local-map");
2007 staticpro (&Qfront_sticky
);
2008 Qfront_sticky
= intern ("front-sticky");
2009 staticpro (&Qrear_nonsticky
);
2010 Qrear_nonsticky
= intern ("rear-nonsticky");
2011 staticpro (&Qmouse_face
);
2012 Qmouse_face
= intern ("mouse-face");
2014 /* Properties that text might use to specify certain actions */
2016 staticpro (&Qmouse_left
);
2017 Qmouse_left
= intern ("mouse-left");
2018 staticpro (&Qmouse_entered
);
2019 Qmouse_entered
= intern ("mouse-entered");
2020 staticpro (&Qpoint_left
);
2021 Qpoint_left
= intern ("point-left");
2022 staticpro (&Qpoint_entered
);
2023 Qpoint_entered
= intern ("point-entered");
2025 defsubr (&Stext_properties_at
);
2026 defsubr (&Sget_text_property
);
2027 defsubr (&Sget_char_property
);
2028 defsubr (&Snext_char_property_change
);
2029 defsubr (&Sprevious_char_property_change
);
2030 defsubr (&Snext_single_char_property_change
);
2031 defsubr (&Sprevious_single_char_property_change
);
2032 defsubr (&Snext_property_change
);
2033 defsubr (&Snext_single_property_change
);
2034 defsubr (&Sprevious_property_change
);
2035 defsubr (&Sprevious_single_property_change
);
2036 defsubr (&Sadd_text_properties
);
2037 defsubr (&Sput_text_property
);
2038 defsubr (&Sset_text_properties
);
2039 defsubr (&Sremove_text_properties
);
2040 defsubr (&Stext_property_any
);
2041 defsubr (&Stext_property_not_all
);
2042 /* defsubr (&Serase_text_properties); */
2043 /* defsubr (&Scopy_text_properties); */